Skip to content
Server Plugin

會話

所需依賴項: io.ktor:ktor-server-sessions

程式碼範例: session-cookie-client, session-cookie-server, session-header-server

原生伺服器
Ktor 支援 Kotlin/Native,並允許您在無需額外執行時或虛擬機器下運行伺服器。
支援: ✅

The Sessions 外掛提供了一種在不同 HTTP 請求之間持久化資料的機制。典型應用場景包括儲存已登入使用者的 ID、購物籃的內容或在客戶端保留使用者偏好設定。在 Ktor 中,您可以透過使用 Cookie 或自訂標頭來實作會話,選擇將會話資料儲存在伺服器上還是傳遞給客戶端,以及簽署和加密會話資料等等。

在本主題中,我們將探討如何安裝 Sessions 外掛、配置它以及在路由處理程式內部存取會話資料。

新增依賴項

為了啟用會話支援,您需要在建置指令碼中包含 ktor-server-sessions artifact:

Kotlin
Groovy
XML

安裝 Sessions

要將 Sessions 外掛安裝到應用程式中, 請將其傳遞給指定

模組
模組允許您透過分組路由來組織應用程式。
中的 install 函數。 下面的程式碼片段展示了如何安裝 Sessions ...

  • ... 在 embeddedServer 函數呼叫內部。
  • ... 在明確定義的 module 內部,它是一個 Application 類別的擴充函數。
kotlin
kotlin

Sessions 外掛也可以安裝到特定路由。 如果您需要針對不同的應用程式資源使用不同的 Sessions 配置,這將會很有用。

會話配置概覽

要配置 Sessions 外掛,您需要執行以下步驟:

  1. 建立資料類別:在配置會話之前,您需要為儲存會話資料建立一個資料類別

  2. 選擇如何在伺服器和客戶端之間傳遞資料:使用 Cookie 或自訂標頭。Cookie 更適合純 HTML 應用程式,而自訂標頭則用於 API。

  3. 選擇在哪裡儲存會話有效負載:在客戶端或伺服器端。您可以透過 Cookie/標頭值將序列化的會話資料傳遞給客戶端,或者將有效負載儲存在伺服器上,僅傳遞會話識別碼。

    如果您想將會話有效負載儲存在伺服器上,您可以*選擇如何儲存它*:在伺服器記憶體中或在一個資料夾中。您也可以實作自訂儲存以保留會話資料。

  4. 保護會話資料:為了保護傳遞給客戶端的敏感會話資料,您需要簽署並加密會話的有效負載。

配置 Sessions 後,您可以在路由處理程式內部取得並設定會話資料

建立資料類別

在配置會話之前,您需要為儲存會話資料建立一個資料類別。 例如,下面的 UserSession 類別將用於儲存會話 ID 和頁面瀏覽次數:

kotlin
@Serializable
data class UserSession(val id: String, val count: Int)

如果您打算使用多個會話,您需要建立多個資料類別。

要使用 Cookie 傳遞會話資料,請在 install(Sessions) 區塊內呼叫帶有指定名稱和資料類別的 cookie 函數:

kotlin
install(Sessions) {
    cookie<UserSession>("user_session")
}

在上面的範例中,會話資料將使用添加到 Set-Cookie 標頭中的 user_session 屬性傳遞給客戶端。您可以透過在 cookie 區塊內部傳遞其他 Cookie 屬性來配置它們。例如,下面的程式碼片段展示了如何指定 Cookie 的路徑和過期時間:

kotlin
install(Sessions) {
    cookie<UserSession>("user_session") {
        cookie.path = "/"
        cookie.maxAgeInSeconds = 10
    }
}

如果所需的屬性沒有明確公開,請使用 extensions 屬性。例如,您可以透過以下方式傳遞 SameSite 屬性:

kotlin
install(Sessions) {
    cookie<UserSession>("user_session") {
        cookie.extensions["SameSite"] = "lax"
    }
}

要了解更多可用的配置設定,請參閱 CookieConfiguration

在將您的應用程式部署到生產環境之前,請確保 secure 屬性設定為 true。這將啟用僅透過安全連線傳輸 Cookie,並保護會話資料免受 HTTPS 降級攻擊。

要使用自訂標頭傳遞會話資料,請在 install(Sessions) 區塊內呼叫帶有指定名稱和資料類別的 header 函數:

kotlin
install(Sessions) {
    header<CartSession>("cart_session")
}

在上面的範例中,會話資料將使用 cart_session 自訂標頭傳遞給客戶端。 在客戶端,您需要將此標頭附加到每個請求以取得會話資料。

如果您使用 CORS 外掛來處理跨來源請求,請將您的自訂標頭按如下方式添加到 CORS 配置中:

kotlin
install(CORS) {
    allowHeader("cart_session")
    exposeHeader("cart_session")
}

儲存會話有效負載:客戶端 vs 伺服器

在 Ktor 中,您可以透過兩種方式管理會話資料:

  • 在客戶端和伺服器之間傳遞會話資料

    如果您只將會話名稱傳遞給 Cookie 或標頭函數,則會話資料將在客戶端和伺服器之間傳遞。在這種情況下,您需要簽署並加密會話的有效負載,以保護傳遞給客戶端的敏感會話資料。

  • 在伺服器上儲存會話資料,並僅在客戶端和伺服器之間傳遞會話 ID

    在這種情況下,您可以選擇在哪裡儲存有效負載在伺服器上。例如,您可以將會話資料儲存在記憶體中、指定資料夾中,或者您可以實作您自己的自訂儲存。

在伺服器上儲存會話有效負載

Ktor 允許您在伺服器上儲存會話資料,並僅在伺服器和客戶端之間傳遞會話 ID。在這種情況下,您可以選擇在哪裡保留伺服器上的有效負載。

記憶體儲存

SessionStorageMemory 啟用在記憶體中儲存會話內容。此儲存會在伺服器運行時保留資料,一旦伺服器停止就會丟棄資訊。例如,您可以如下在伺服器記憶體中儲存 Cookie:

kotlin
cookie<CartSession>("cart_session", SessionStorageMemory()) {
}

您可以在此處找到完整範例:session-cookie-server

請注意,SessionStorageMemory 僅用於開發。

目錄儲存

directorySessionStorage 可用於將會話資料儲存在指定目錄下的檔案中。例如,要在 build/.sessions 目錄下儲存會話資料,請以這種方式建立 directorySessionStorage

kotlin
header<CartSession>("cart_session", directorySessionStorage(File("build/.sessions"))) {
}

您可以在此處找到完整範例:session-header-server

自訂儲存

Ktor 提供了 SessionStorage 介面,允許您實作自訂儲存。

kotlin
interface SessionStorage {
    suspend fun invalidate(id: String)
    suspend fun write(id: String, value: String)
    suspend fun read(id: String): String
}

所有三個函數都是 suspending 函數。您可以使用 SessionStorageMemory 作為參考。

保護會話資料

簽署會話資料

簽署會話資料可以防止修改會話內容,但允許使用者查看此內容。 要簽署會話,請將簽名金鑰傳遞給 SessionTransportTransformerMessageAuthentication 建構函數,並將此實例傳遞給 transform 函數:

kotlin
install(Sessions) {
    val secretSignKey = hex("6819b57a326945c1968f45236589")
    cookie<CartSession>("cart_session", SessionStorageMemory()) {
        cookie.path = "/"
        transform(SessionTransportTransformerMessageAuthentication(secretSignKey))
    }
}

SessionTransportTransformerMessageAuthentication 使用 HmacSHA256 作為預設身份驗證演算法,該演算法可以更改。

簽署並加密會話資料

簽署並加密會話資料可以防止讀取和修改會話內容。 要簽署並加密會話,請將簽名/加密金鑰傳遞給 SessionTransportTransformerEncrypt 建構函數,並將此實例傳遞給 transform 函數:

kotlin
install(Sessions) {
    val secretEncryptKey = hex("00112233445566778899aabbccddeeff")
    val secretSignKey = hex("6819b57a326945c1968f45236589")
    cookie<UserSession>("user_session") {
        cookie.path = "/"
        cookie.maxAgeInSeconds = 10
        transform(SessionTransportTransformerEncrypt(secretEncryptKey, secretSignKey))
    }
}

請注意,Ktor 3.0.0 版本中加密方法已更新。如果您從早期版本遷移,請在 SessionTransportTransformerEncrypt 的建構函數中使用 backwardCompatibleRead 屬性,以確保與現有會話的相容性。

預設情況下,SessionTransportTransformerEncrypt 使用 AESHmacSHA256 演算法,這些演算法可以更改。

請注意,簽名/加密金鑰不應在程式碼中指定。您可以在配置檔案中使用自訂群組來儲存簽名/加密金鑰,並使用環境變數初始化它們。

取得並設定會話內容

要為特定路由設定會話內容,請使用 call.sessions 屬性。set 方法允許您建立新的會話實例:

kotlin
get("/login") {
    call.sessions.set(UserSession(id = "123abc", count = 0))
    call.respondRedirect("/user")
}

要取得會話內容,您可以呼叫 get,將其中一個已註冊的會話類型作為類型參數:

kotlin
get("/user") {
    val userSession = call.sessions.get<UserSession>()
    if (userSession != null) {
}

要修改會話,例如遞增計數器,您需要呼叫資料類別的 copy 方法:

kotlin
get("/user") {
    val userSession = call.sessions.get<UserSession>()
    if (userSession != null) {
        call.sessions.set(userSession.copy(count = userSession.count + 1))
        call.respondText("Session ID is ${userSession.id}. Reload count is ${userSession.count}.")
    } else {
        call.respondText("Session doesn't exist or is expired.")
    }
}

當您需要因任何原因清除會話時(例如,當使用者登出時),請呼叫 clear 函數:

kotlin
get("/logout") {
    call.sessions.clear<UserSession>()
    call.respondRedirect("/user")
}

您可以在此處找到完整範例:session-cookie-client

延遲會話擷取

預設情況下,Ktor 會嘗試為每個包含會話的請求從儲存中讀取會話,無論路由是否實際需要它。這種行為可能會導致不必要的開銷 — 特別是在使用自訂會話儲存的應用程式中。

您可以透過啟用 io.ktor.server.sessions.deferred 系統屬性來延遲會話載入:

kotlin
System.setProperty("io.ktor.server.sessions.deferred", "true")

範例

下面的可執行範例演示了如何使用 Sessions 外掛: