A session is a mechanism to persist data between different HTTP requests. Establishing a conversational context into the otherwise stateless nature of HTTP. They allow servers to keep a piece of information associated with the client during a sequence of HTTP requests and responses.
Different use-cases include: authentication and authorization, user tracking, keeping information at client like a shopping cart, and more.
Sessions are typically implemented by employing Cookies
, but could also be done using headers for example
to be consumed by other backends or an AJAX requests.
They are either client-side when the entire serialized object goes back and forth between the client and the server, or server-side when only the session ID is transferred and the associated data is stored entirely in the server.
Table of contents:
io.ktor.sessions.Sessions
类中定义,无需任何额外构件。
Sessions are usually represented as immutable data classes (and session is changed by calling the .copy
method):
data class SampleSession(val name: String, val value: Int)
A simple Sessions installation looks like this:
install(Sessions) {
cookie<SampleSession>("COOKIE_NAME")
}
And a more advanced installation could be like:
install(Sessions) {
cookie<SampleSession>(
"SESSION_FEATURE_SESSION_ID",
directorySessionStorage(File(".sessions"), cached = true)
) {
cookie.path = "/" // Specify cookie's path '/' so it can be used in the whole site
}
}
To configure sessions you have to to specify a cookie/header name, optional server-side storage, and a class associated to the session.
If you want to further customize sessions. Please read the extending section.
Since there are several combinations for configuring sessions, there is a section about deciding how to configure sessions.
In order to access or set the session content, you have to use the call.sessions
property:
To get the session content, you can call the call.sessions.get
method receiving as type parameter one
of the registered session types:
routing {
get("/") {
// If the session was not set, or is invalid, the returned value is null.
val mySession: MySession? = call.sessions.get<MySession>()
}
}
To create or modify current session you just call the set
method of the sessions
property with the value of the
data class:
call.sessions.set(MySession(name = "John", value = 12))
To modify a session (for example incrementing a counter), you have to call the .copy
method of the data class
:
val session = call.sessions.get<MySession>() ?: MySession(name = "Initial", value = 0)
call.sessions.set(session.copy(value = session.value + 1))
When a user logs out, or a session should be cleared for any other reason, you can call the clear
function:
call.sessions.clear<MySession>()
After calling this, retrieving that session will return null, until set again.
When handling requests, you can get, set, or clear your sessions:
val session = call.sessions.get<SampleSession>() // Gets a session of this type or null if not available
call.sessions.set(SampleSession(name = "John", value = 12)) // Sets a session of this type
call.sessions.clear<SampleSession>() // Clears the session of this type
Since there could be several conversational states for a single application, you can install multiple session mappings. For example:
application.install(Sessions) {
cookie<Session1>("Session1") // install a cookie stateless session
header<Session2>("Session2", sessionStorage) { // install a header server-side session
transform(SessionTransportTransformerDigest()) // sign the ID that travels to client
}
}
install(Sessions) {
cookie<SessionCart>("SESSION_CART_LIST") {
cookie.path = "/shop" // Just accessible in '/shop/*' subroutes
}
cookie<SessionLogin>(
"SESSION_LOGIN",
directorySessionStorage(File(".sessions"), cached = true)
) {
cookie.path = "/" // Specify cookie's path '/' so it can be used in the whole site
transform(SessionTransportTransformerDigest()) // sign the ID that travels to client
}
}
For multiple session mappings, both type and name should be unique.
You can configure the sessions in several different ways:
Since sessions can be implemented by various techniques, there is an extensive configuration facility to set them up:
cookie
will install cookie-based transportheader
will install header-based transportEach of these functions will get the name of the cookie or header.
If a function is passed an argument of type SessionStorage
it will use the storage to save the session, otherwise
it will serialize the data into the cookie/header value.
Each of these functions can receive an optional configuration lambda.
For cookies, the receiver is CookieSessionBuilder
which allows you to:
serializer
transformer
, like signing or encryptingFor headers, the receiver is HeaderSessionBuilder
which allows serializer
and transformer
customization.
For cookies & headers that are server-side with a SessionStorage
, additional configuration is identity
function
that should generate a new ID when the new session is created.
SessionStorageMemory
for development if you want to drop sessions after stopping the serverdirectorySessionStorage
for production environments or to keep sessions after restarting the serverSince no SessionStorage is provided as a cookie
second argument its contents will be stored in the cookie.
install(Sessions) {
val secretHashKey = hex("6819b57a326945c1968f45236589")
cookie<SampleSession>("SESSION_FEATURE_SESSION") {
cookie.path = "/"
transform(SessionTransportTransformerMessageAuthentication(secretHashKey, "HmacSHA256"))
}
}
SessionStorageMemory
don’t have parameters at this point.
install(Sessions) {
cookie<SampleSession>("SESSION_FEATURE_SESSION_ID", SessionStorageMemory()) {
cookie.path = "/"
}
}
Alongside SessionStorage there is a SessionStorageMemory
class that you can use for development.
It is a simple implementation that will keep sessions in-memory, thus all the sessions are dropped
once you shutdown the server and will constantly grow in memory since this implementation does not discard
the old sessions at all.
This implementation is not intended for production environments.
You have to include an additional artifact for the directorySessionStorage
function.
compile("io.ktor:ktor-server-sessions:$ktor_version") // Required for directorySessionStorage
install(Sessions) {
cookie<SampleSession>(
"SESSION_FEATURE_SESSION_ID",
directorySessionStorage(File(".sessions"), cached = true)
) {
cookie.path = "/" // Specify cookie's path '/' so it can be used in the whole site
}
}
As part of the io.ktor:ktor-server-sessions
artifact, there is a directorySessionStorage
function
which utalizes a session storage that will use a folder for storing sessions on disk.
This function has a first argument of type File
that is the folder that will store sessions (it will be created
if it doesn’t exist already).
There is also an optional cache argument, which when set, will keep a 60-second in-memory cache to prevent calling the OS and reading the session from disk each time.
Sessions are designed to be extensible, and there are some cases where you might want to further compose or change the default sessions behaviour.
For example by using custom encryption or authenticating algorithms for the transport value, or to store your session information server-side to a specific database.
You can define custom transformers, custom serializers and custom storages.