Cookies

Show me a website without any cookies, and I'll show you a pamphlet. At some point, you're going to need to write a cookie. Indeed, we've already seen that Ring writes out a cookie for us right out of the box for session tracking.

We can interact with cookies using the noir.cookies namespace. At its most basic, noir.cookies provides two functions: get and put!.

Getting a cookie's value

We can get a cookie's value by calling noir.cookies/get and providing either a string or a keyword for the name of the cookie to retrieve. We can see this in action by creating a trivial route that simply renders out the value of the ring-session cookie:

(require '[compojure.core :refer :all]
         '[noir.cookies :as cookies])
(defroutes some-route
  (GET "/ring-session" [] (cookies/get "ring-session"))   ;#1

The call to cookies/get retrieves the value of the cookie in question – if one exists – otherwise nil. In our case, the preceding code would render something similar to the following:

Getting a cookie's value

Setting a cookie's value

Knowing how to read a cookie's value is all fine and dandy, but it's about as useless as wearing a raincoat indoors if we don't know how to write a value to a cookie in the first place. We accomplish this by calling noir.cookies/put!, and, similar to get, pass it either as a string or a keyword for the name of the cookie. You can write the cookie from any code that's executed during the processing of the route, though I typically write out my cookies as close to the route def as possible. For example:

(require '[noir.cookies :as cookies])
(defn write-cookie-page
  "Just writes a cookie and a friendly message to the browser."
  []
  (cookies/put! :my-cookie "om nom nom nom!")
  "Hey there, buddy ol' chap!")
(defroutes some-routes
  (GET "/write-cookie" [] (write-cookie-page)))

The preceding code simply writes out a cookie, called my-cookie, with the value of "om nom nom nom!". All the other settings are left with their default values.

Similar to access rules, if we only provide a string as the value of a cookie, then a reasonable set of defaults will be used for the cookie (such as max-age, expires, path, domain, and so on). If we want to be specific about the cookie attributes however, we can set the cookie's value as a map.

Setting the cookie as a map

Setting the cookie as a map, as opposed to a string value, provides us with more granularity of how the cookie behaves. The cookie-as-a-map representation of the preceding example would be the following:

(cookies/put! :my-cookie {:value "om nom nom nom!"})

Other values we can specify in the map are:

  • :path (string): This is the URI on which the cookie is valid. Defaults to /
  • :domain (string): This is the domain on which the cookie is valid. Defaults to the current domain
  • :port (int): This is the port on which the cookie is valid.
  • :max-age (int): This is the number of seconds the cookie is valid for, after which, it expires
  • :expires (string): Instead of the number seconds, a date-time string explicitly stating when the cookie will expire
  • :secure (Boolean): A true/false value which, when true, states that the cookie requires HTTPS access
  • :http-only (Boolean): A true/false value which, when true, prohibits JavaScript from accessing the cookie

I will fully admit that I've never used the :domain or :port options, and I rarely use :expires because I almost never have a need to specify a specific date (the number of seconds usually suffices, and it's easier to set than messing around with date math). But I digress…

If we wanted to set up the precedingly mentioned cookie such that it was only available on our login page, and only over HTTPS, and not available to JavaScript, we could adjust it to the following:

(cookies/put! :my-cookie
                     {:value "om nom nom nom!"
                      :path "/login"
                      :secure true
                      :http-only true})

Securing a cookie

In addition to the standard get/put! functions for reading and writing a cookie, we can also use two other functions that will securely read and write a cookie: get-signed and put-signed!:

  • put-signed!: This writes a second cookie alongside the original cookie, containing a signature for the cookie. The signature cookie's name is the same as the original cookie, but with the suffix __s.
  • get-signed: This validates the signature of the cookie that was created using put-signed!

Note that put-signed! doesn't encrypt the cookie; rather, it writes a second cookie containing a signature for the first. As such, you can write a cookie using put-signed!, but can still read it using get. The validation of the cookie's signature is only performed when reading the cookie using get-signed, which, if invalid, nil will be returned instead of the cookie's value.

Tip

Beware! If you use put-signed! to write a cookie, and then later change the cookie's value using just put!, the signature cookie will not be updated; thus any subsequent get-signed will fail to validate the cookie.

Deleting a cookie

This is probably the most commonly asked question on the Internet when it comes to cookies, so I figured I would include it here. In short, there is no way to delete a cookie. The best way to delete a cookie is to overwrite the cookie with an empty value, and then set its :max-age to -1 (one second in the past).

We could delete our :my-cookie cookie then by overwriting it with the following code:

(cookies/put! :my-cookie {:value "" :max-age -1})

Note, however, that this only deletes the :my-cookie cookie and not its accompanying signature cookie, :my-cookie__s, which will also need to be overwritten if you wish to delete it.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.37.12