Sessions
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 metadata. 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 memoryring.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.