Skip to content

Sessions

Jan Rychter edited this page Jan 17, 2016 · 19 revisions

Sessions in Ring work a little differently than you might expect, because Ring attempts to be functional when possible.

Session data is passed via the request map on the :session key. The following example prints out the current username from the session.

(use 'ring.middleware.session
     'ring.util.response)

(defn handler [{session :session}]
  (response (str "Hello " (:username session))))

(def app
  (wrap-session handler))

To change the session data, you can add a :session key to the response that contains the updated session data. The next example counts the number of times the current session has accessed the page.

(defn handler [{session :session}]
  (let [count   (:count session 0)
        session (assoc session :count (inc count))]
    (-> (response (str "You accessed this page " count " times."))
        (assoc :session session))))

To delete the session entirely, set the :session key on the response to nil:

(defn handler [request]
  (-> (response "Session deleted.")
      (assoc :session nil)))

If you simply want to recreate the session, due to a privilege escalation for example, add the :recreate key to the session data. This will cause the session identifier, that is sent to the browser, to change.

(defn handler [request]
  (-> (response "Session identifier recreated")
      (assoc :session (vary-meta (:session request) assoc :recreate true)))

Often you'll want to control how long the session cookie exists on the user's browser. You can alter the session cookie's attributes using the :cookie-attrs option:

(def app
  (wrap-session handler {:cookie-attrs {:max-age 3600}}))

In this case, the cookie's maximum lifespan is set to 3600 seconds, or 1 hour.

You can also use this to ensure that session cookies for sites secured with HTTPS are not leaked through HTTP:

(def app
  (wrap-session handler {:cookie-attrs {:secure true}}))

Session Stores

Session data is saved in session stores. There are two stores included in Ring:

  • ring.middleware.session.memory/memory-store - stores sessions in memory
  • ring.middleware.session.cookie/cookie-store - stores sessions encrypted in a cookie

By default, Ring stores session data in memory, but this can be overridden with the :store option:

(use 'ring.middleware.session.cookie)

(def app
  (wrap-session handler {:store (cookie-store {:key "a 16-byte secret"})})

You can write your own session store by implementing the ring.middleware.session.store/SessionStore protocol:

(use 'ring.middleware.session.store)

(deftype CustomStore []
  SessionStore
  (read-session [_ key]
    (read-data key))
  (write-session [_ key data]
    (let [key (or key (generate-new-random-key))]
      (save-data key data)
      key))
  (delete-session [_ key]
    (delete-data key)
    nil))

Note that when writing the session, the key will be nil if this is a new session. The session store should expect this, and generate a new random key. It is very important that this key cannot be guessed, otherwise malicious users could access other people's session data.

Something went wrong with that request. Please try again.