Sessions
必要な依存関係: io.ktor:ktor-server-sessions
コード例: session-cookie-client, session-cookie-server, session-header-server
Sessions プラグインは、異なる HTTP リクエスト間でデータを永続化するためのメカニズムを提供します。典型的なユースケースには、ログイン済みユーザーの ID の保存、ショッピングカートの内容の保持、またはクライアント側でのユーザー設定の保持などがあります。Ktor では、クッキーまたはカスタムヘッダーを使用してセッションを実装したり、セッションデータをサーバーに保存するかクライアントに渡すかを選択したり、セッションデータの署名や暗号化を行ったりすることができます。
このトピックでは、Sessions プラグインのインストール方法、設定方法、およびルートハンドラー内でのセッションデータへのアクセス方法について説明します。
依存関係の追加
セッションのサポートを有効にするには、ビルドスクリプトに ktor-server-sessions アーティファクトを含める必要があります。
Sessions のインストール
Sessions プラグインをアプリケーションにインストールするには、 指定された
install 関数に渡します。 以下のコードスニペットは、Sessions をインストールする方法を示しています。 - ...
embeddedServer関数の呼び出し内。 - ...
Applicationクラスの拡張関数である、明示的に定義されたmodule内。
Sessions プラグインは、特定のルートにインストールすることもできます。 これは、アプリケーションの異なるリソースに対して異なる Sessions 設定が必要な場合に便利です。
セッション設定の概要
Sessions プラグインを設定するには、以下の手順を実行する必要があります。
サーバーとクライアント間でのデータ転送方法の選択: クッキーまたはカスタムヘッダーを使用します。クッキーは通常の HTML アプリケーションに適しており、カスタムヘッダーは API 向けです。
セッションペイロードの保存場所の選択: クライアント側またはサーバー側。シリアライズされたセッションデータをクッキー/ヘッダー値としてクライアントに渡すか、ペイロードをサーバーに保存してセッション識別子のみを渡すかを選択できます。
サーバー側にセッションペイロードを保存する場合は、保存方法の選択が可能です。サーバーメモリ内またはフォルダー内を選択できます。また、セッションデータを保持するためのカスタムストレージを実装することもできます。
セッションデータの保護: クライアントに渡される機密性の高いセッションデータを保護するために、セッションのペイロードに署名し、暗号化する必要があります。
Sessions を設定した後、ルートハンドラー内でセッションデータの取得と設定ができるようになります。
データクラスの作成
セッションを設定する前に、セッションデータを保存するためのデータクラスを作成する必要があります。 例えば、以下の UserSession クラスは、セッション ID とページビュー数を保存するために使用されます:
@Serializable
data class UserSession(val id: String, val count: Int)複数のセッションを使用する場合は、複数のデータクラスを作成する必要があります。
セッションデータの転送: Cookie vs Header
Cookie (クッキー)
クッキーを使用してセッションデータを渡すには、install(Sessions) ブロック内で指定された名前とデータクラスを使用して cookie 関数を呼び出します:
install(Sessions) {
cookie<UserSession>("user_session")
}上記の例では、セッションデータは Set-Cookie ヘッダーに追加された user_session 属性を使用してクライアントに渡されます。cookie ブロック内で他のクッキー属性を設定することもできます。例えば、以下のコードスニペットはクッキーのパスと有効期限を指定する方法を示しています:
install(Sessions) {
cookie<UserSession>("user_session") {
cookie.path = "/"
cookie.maxAgeInSeconds = 10
}
}必要な属性が明示的に公開されていない場合は、extensions プロパティを使用します。例えば、SameSite 属性は次のように渡すことができます:
install(Sessions) {
cookie<UserSession>("user_session") {
cookie.extensions["SameSite"] = "lax"
}
}利用可能な設定の詳細については、CookieConfiguration を参照してください。
アプリケーションを本番環境にデプロイする前に、
secureプロパティがtrueに設定されていることを確認してください。これにより、セキュアな接続を介してのみクッキーを転送できるようになり、セッションデータが HTTPS ダウングレード攻撃から保護されます。
Header (ヘッダー)
カスタムヘッダーを使用してセッションデータを渡すには、install(Sessions) ブロック内で指定された名前とデータクラスを使用して header 関数を呼び出します:
install(Sessions) {
header<CartSession>("cart_session")
}上記の例では、セッションデータは cart_session カスタムヘッダーを使用してクライアントに渡されます。 クライアント側では、セッションデータを取得するために各リクエストにこのヘッダーを付加する必要があります。
オリジン間リクエストを処理するために CORS プラグインを使用する場合は、次のようにカスタムヘッダーを
CORS設定に追加してください:kotlininstall(CORS) { allowHeader("cart_session") exposeHeader("cart_session") }
変更されたときのみセッションデータを送信
デフォルトでは、Ktor は変更がない場合でも、すべてのレスポンスでセッションデータを送信します。
セッションデータが変更されたときのみ送信するには、セッション設定で sendOnlyIfModified フラグを有効にします:
install(Sessions) {
cookie<MySession>("SESSION") {
sendOnlyIfModified = true
}
}このオプションは、クッキーベースとヘッダーベースの両方のセッションで利用可能です。
セッションペイロードの保存: Client vs Server
Ktor では、セッションデータを 2 つの方法で管理できます:
クライアントとサーバー間でセッションデータを渡す。
cookie または header 関数にセッション名のみを渡すと、セッションデータがクライアントとサーバー間で渡されます。この場合、クライアントに渡される機密性の高いセッションデータを保護するために、セッションのペイロードに署名し暗号化する必要があります。
サーバーにセッションデータを保存し、クライアントとサーバー間ではセッション ID のみを渡す。
この場合、サーバー上のペイロードの保存場所を選択できます。例えば、セッションデータをメモリ内、指定されたフォルダー内に保存したり、独自のカスタムストレージを実装したりできます。
サーバー側でのセッションペイロードの保存
Ktor では、セッションデータをサーバー側に保存し、サーバーとクライアントの間でセッション ID のみを渡すことができます。この場合、サーバー上のどこにペイロードを保持するかを選択できます。
インメモリ・ストレージ
SessionStorageMemory は、セッションの内容をメモリに保存できるようにします。このストレージはサーバーが稼働している間データを保持し、サーバーが停止すると情報は破棄されます。例えば、次のようにサーバーメモリにクッキーを保存できます:
cookie<CartSession>("cart_session", SessionStorageMemory()) {
}完全な例はこちらにあります: session-cookie-server。
SessionStorageMemoryは開発目的のみを意図していることに注意してください。
ディレクトリ・ストレージ
directorySessionStorage を使用すると、指定されたディレクトリの下にあるファイルにセッションデータを保存できます。例えば、build/.sessions ディレクトリの下のファイルにセッションデータを保存するには、次のように directorySessionStorage を作成します:
header<CartSession>("cart_session", directorySessionStorage(File("build/.sessions"))) {
}完全な例はこちらにあります: session-header-server。
カスタムストレージ
Ktor は、カスタムストレージを実装できる SessionStorage インターフェースを提供しています。
interface SessionStorage {
suspend fun invalidate(id: String)
suspend fun write(id: String, value: String)
suspend fun read(id: String): String
}3 つの関数はすべて 中断関数 (suspending functions) です。SessionStorageMemory をリファレンスとして使用できます。
セッションデータの保護
セッションデータへの署名
セッションデータに署名すると、セッションの内容の改ざんを防ぐことができますが、ユーザーはその内容を見ることができます。 セッションに署名するには、署名キーを SessionTransportTransformerMessageAuthentication コンストラクタに渡し、このインスタンスを transform 関数に渡します:
install(Sessions) {
val secretSignKey = hex("6819b57a326945c1968f45236589")
cookie<CartSession>("cart_session", SessionStorageMemory()) {
cookie.path = "/"
transform(SessionTransportTransformerMessageAuthentication(secretSignKey))
}
}SessionTransportTransformerMessageAuthentication はデフォルトの認証アルゴリズムとして HmacSHA256 を使用しますが、これは変更可能です。
セッションデータの署名と暗号化
セッションデータの署名と暗号化を行うことで、セッションの内容の読み取りと改ざんの両方を防ぐことができます。 セッションの署名と暗号化を行うには、署名/暗号化キーを SessionTransportTransformerEncrypt コンストラクタに渡し、このインスタンスを transform 関数に渡します:
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 は AES および HmacSHA256 アルゴリズムを使用しますが、これらは変更可能です。
署名/暗号化キーはコード内に直接記述すべきではないことに注意してください。設定ファイルのカスタムグループを使用して署名/暗号化キーを保存し、環境変数を使用してそれらを初期化することができます。
セッション内容の取得と設定
特定のルートのセッション内容を設定するには、call.sessions プロパティを使用します。set メソッドを使用すると、新しいセッションインスタンスを作成できます:
get("/login") {
call.sessions.set(UserSession(id = "123abc", count = 0))
call.respondRedirect("/user")
}セッション内容を取得するには、登録されたセッションタイプのいずれかを型パラメータとして受け取る get を呼び出します:
get("/user") {
val userSession = call.sessions.get<UserSession>()
if (userSession != null) {
}セッションを変更する場合(例えばカウンターを増やす場合など)は、データクラスの copy メソッドを呼び出す必要があります:
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 関数を呼び出します:
get("/logout") {
call.sessions.clear<UserSession>()
call.respondRedirect("/user")
}完全な例はこちらにあります: session-cookie-client。
遅延セッション取得
デフォルトでは、Ktor はセッションを含むすべてのリクエストに対して、ルートが実際にそれを必要とするかどうかに関係なく、ストレージからのセッションの読み取りを試みます。この動作は、特にカスタムセッションストレージを使用しているアプリケーションにおいて、不要なオーバーヘッドを引き起こす可能性があります。
io.ktor.server.sessions.deferred システムプロパティを有効にすることで、セッションの読み込みを遅延させることができます。
System.setProperty("io.ktor.server.sessions.deferred", "true")例
以下の実行可能な例は、Sessions プラグインの使用方法を示しています:
- session-cookie-client は、署名および暗号化されたセッションペイロードを クッキー を使用して クライアント に渡す方法を示しています。
- session-cookie-server は、セッションペイロードを サーバーメモリ に保持し、署名済み のセッション ID を クッキー を使用してクライアントに渡す方法を示しています。
- session-header-server は、サーバー上の ディレクトリ・ストレージ にセッションペイロードを保持し、署名済み のセッション ID を カスタムヘッダー を使用してクライアントに渡す方法を示しています。
