Handle Conversations with Sessions

预计阅读时间:8分钟

会话是一种在不同HTTP请求之间保留数据的机制. 将会话上下文建立到HTTP的其他无状态性质中. 它们允许服务器在一系列HTTP请求和响应期间保留与客户端相关的一条信息.

不同的用例包括:身份验证和授权,用户跟踪,将信息保存在客户(如购物车)等更多内容.

会话通常通过使用Cookies来实现,但也可以使用标头来完成,例如,标头将被其他后端或AJAX请求使用.

当整个序列化的对象在客户端和服务器之间来回移动时,它们或者是客户端,或者当仅传输会话ID并且关联的数据完全存储在服务器中时,它们是服务器端.

目录:

此功能在io.ktor.sessions.Sessions类中io.ktor.sessions.Sessions ,不需要其他工件.

Installation

会话通常表示为不可变的数据类(并且通过调用.copy方法来更改会话):

data class SampleSession(val name: String, val value: Int)

一个简单的Sessions安装看起来像这样:

install(Sessions) {
    cookie<SampleSession>("COOKIE_NAME")
}

而更高级的安装可能是:

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
    }
}

要配置会话,您必须指定Cookie /标头名称,可选的服务器端存储以及与该会话关联的类.

如果要进一步自定义会话. 请阅读扩展部分.

由于配置会话有多种组合,因此有一节介绍如何确定会话 .

Usage

In order to access or set the session content, you have to use the call.sessions property:

要获取会话内容,可以调用call.sessions.get方法,该方法以已注册的会话类型之一作为类型参数接收:

routing {
    get("/") {
        // If the session was not set, or is invalid, the returned value is null.
        val mySession: MySession? = call.sessions.get<MySession>()
    }
}

要创建或修改当前会话,您只需使用数据类的值调用sessions属性的set方法即可:

call.sessions.set(MySession(name = "John", value = 12))

要修改会话(例如,增加计数器),必须调用data class.copy方法:

val session = call.sessions.get<MySession>() ?: MySession(name = "Initial", value = 0)  
call.sessions.set(session.copy(value = session.value + 1))

当用户注销或由于其他任何原因而应清除会话时,可以调用clear函数:

call.sessions.clear<MySession>()

调用此方法后,检索该会话将返回null,直到再次设置.

处理请求时,您可以获取,设置或清除会话:

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 

Multiple sessions

由于单个应用程序可能有几种会话状态,因此您可以安装多个会话映射. 例如:

  • 将用户首选项或购物车信息存储为客户端Cookie.
  • 将用户登录名存储在服务器上的文件中.
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
    }
}

对于多个会话映射, 无论是类型和名称应该是唯一的.

Configuration

您可以通过几种不同的方式配置会话:

  • 有效负载存储在哪里:客户端或服务器端.
  • 如何传输有效负载或会话ID:使用Cookie或标头.
  • 如何进行序列化:使用内部格式,JSON,自定义引擎…
  • 有效负载存储在服务器中的什么位置:内存中的文件夹,redis…
  • 有效负载转换:加密,已认证...

由于可以通过各种技术来实现会话,因此可以使用广泛的配置工具进行设置:

  • cookie将安装基于cookie的传输
  • header将安装基于标头的传输

这些函数中的每一个都将获取cookie或标题的名称.

如果传递了一个函数SessionStorage类型的参数,它将使用存储来保存会话,否则它将数据序列化为cookie / header值.

这些功能中的每一个都可以接收可选的配置lambda.

对于cookie,接收者是CookieSessionBuilder ,它使您可以:

  • 指定自定义serializer
  • 添加一个值transformer ,例如签名或加密
  • 指定cookie配置,例如持续时间,编码,域,路径等

对于标头,接收器是HeaderSessionBuilder ,它允许serializertransformer自定义.

对于带有SessionStorage服务器端的Cookie和标头,附加配置是identity功能,该功能应在创建新会话时生成新ID.

Deciding how to configure sessions

  • Cookies用于纯HTML应用程序.
  • 如果HTTP客户端更简单,则将Header用于API或XHR请求.

Client vs Server

  • 如果要防止会话重播或进一步提高安全性,请使用服务器Cookie.
    • 如果要在停止服务器后删除会话,请使用SessionStorageMemory进行开发
    • 在生产环境中使用directorySessionStorage或在重新启动服务器后保留会话
  • 如果您想要一种更简单的方法而不需要后端存储,请使用Client Cookies
    • 如果您想在客户端即时进行修改以用于测试目的并且不关心修改,则可以将其普通使用
    • 将其与转换认证一起使用,并可以选择对其加密以防止修改
    • 如果您的会话有效负载容易受到重放攻击,则不要使用它. 这里的安全示例 .

Baked snippets

由于没有提供SessionStorage作为cookie第二个参数,因此其内容将存储在cookie中.

install(Sessions) {
    val secretHashKey = hex("6819b57a326945c1968f45236589")
    
    cookie<SampleSession>("SESSION_FEATURE_SESSION") {
        cookie.path = "/"
        transform(SessionTransportTransformerMessageAuthentication(secretHashKey, "HmacSHA256"))
    }
}

Storing a session id in a cookie, and storing session contents in-memory

SessionStorageMemory目前没有参数.

install(Sessions) {
    cookie<SampleSession>("SESSION_FEATURE_SESSION_ID", SessionStorageMemory()) {
        cookie.path = "/"
    }
}

除了SessionStorage之外,还有一个SessionStorageMemory类可用于开发. 这是一个简单的实现,它将会话保留在内存中,因此,一旦关闭服务器,所有会话都将被丢弃,并且由于该实现根本不会丢弃旧会话,因此内存将不断增长.

此实现不适用于生产环境.

Storing a session id in a cookie, and storing session contents in a file

您必须为directorySessionStorage函数包括其他工件.

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
    }
}

作为io.ktor:ktor-server-sessions工件的一部分,有一个directorySessionStorage函数可以对会话存储进行最终化,它将使用一个文件夹在磁盘上存储会话.

该函数的第一个参数类型为File ,这是用于存储会话的文件夹(如果尚不存在,则会创建该文件夹).

还有一个可选的cache参数,设置该参数时将保留60秒的内存中高速缓存,以防止每次调用OS并从磁盘读取会话.

Extending

会话被设计为可扩展的,在某些情况下,您可能希望进一步编写或更改默认会话行为.

例如,通过使用自定义加密或身份验证算法获取传输值,或将会话信息存储在服务器端到特定数据库.

您可以定义自定义转换器自定义序列化程序自定义存储 .

by  ICOPY.SITE