API ドキュメント
Javadoc 全体をオンラインで閲覧できます。主要な API については、次のセクションで説明します。
Session
を使用する
Session
は、名前と値のペアの簡略化された Map
です。
一般的な使用箇所は、次のようになります。
class RepositoryDemo<S extends Session> {
private SessionRepository<S> repository; (1)
void demo() {
S toSave = this.repository.createSession(); (2)
(3)
User rwinch = new User("rwinch");
toSave.setAttribute(ATTR_USER, rwinch);
this.repository.save(toSave); (4)
S session = this.repository.findById(toSave.getId()); (5)
(6)
User user = session.getAttribute(ATTR_USER);
assertThat(user).isEqualTo(rwinch);
}
// ... setter methods ...
}
1 | Session を継承するジェネリクス型 S を使用して SessionRepository インスタンスを作成します。ジェネリクス型はクラスで定義されています。 |
2 | SessionRepository を使用して新しい Session を作成し、それを型 S の変数に割り当てます。 |
3 | Session と対話します。この例では、User を Session に保存する方法を示します。 |
4 | ここで、Session を保存します。これが、ジェネリクス型 S が必要な理由です。SessionRepository では、同じ SessionRepository を使用して作成または取得された Session インスタンスのみを保存できます。これにより、SessionRepository は実装固有の最適化を行うことができます(つまり、変更された属性のみを書き込む)。 |
5 | SessionRepository から Session を取得します。 |
6 | 属性を明示的にキャストする必要なしに、Session から永続化された User を取得します。 |
Session
API は、Session
インスタンスの有効期限に関連する属性も提供します。
一般的な使用箇所は、次のようになります。
class ExpiringRepositoryDemo<S extends Session> {
private SessionRepository<S> repository; (1)
void demo() {
S toSave = this.repository.createSession(); (2)
// ...
toSave.setMaxInactiveInterval(Duration.ofSeconds(30)); (3)
this.repository.save(toSave); (4)
S session = this.repository.findById(toSave.getId()); (5)
// ...
}
// ... setter methods ...
}
1 | Session を継承するジェネリクス型 S を使用して SessionRepository インスタンスを作成します。ジェネリクス型はクラスで定義されています。 |
2 | SessionRepository を使用して新しい Session を作成し、それを型 S の変数に割り当てます。 |
3 | Session と対話します。この例では、Session が期限切れになる前に非アクティブにできる時間を更新する方法を示します。 |
4 | ここで、Session を保存します。これが、ジェネリクス型 S が必要な理由です。SessionRepository では、同じ SessionRepository を使用して作成または取得された Session インスタンスのみを保存できます。これにより、SessionRepository は実装固有の最適化を行うことができます(つまり、変更された属性のみを書き込む)。最終アクセス時刻は、Session が保存されるときに自動的に更新されます。 |
5 | SessionRepository から Session を取得します。Session の有効期限が切れている場合、結果は null になります。 |
SessionRepository
を使用する
SessionRepository
は、Session
インスタンスの作成、取得、永続化を担当します。
可能であれば、SessionRepository
または Session
と直接対話しないでください。代わりに、開発者は HttpSession
と WebSocket の統合を通じて間接的に SessionRepository
と Session
と対話することを好むべきです。
FindByIndexNameSessionRepository
を使用する
Session
を使用するための Spring Session の最も基本的な API は SessionRepository
です。この API は意図的に非常に単純であるため、基本的な機能を備えた追加の実装を簡単に提供できます。
一部の SessionRepository
実装では、FindByIndexNameSessionRepository
の実装を選択する場合もあります。例: Spring の Redis、JDBC、Hazelcast サポートライブラリはすべて FindByIndexNameSessionRepository
を実装しています。
FindByIndexNameSessionRepository
は、指定されたインデックス名とインデックス値を持つすべてのセッションを検索するメソッドを提供します。提供されているすべての FindByIndexNameSessionRepository
実装でサポートされている一般的な使用例として、便利な方法を使用して、特定のユーザーのすべてのセッションを検索できます。これは、FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME
という名前のセッション属性にユーザー名が入力されていることを確認することによって行われます。Spring Session は使用されている認証メカニズムを認識していないため、属性が設定されていることを確認するのはユーザーの責任です。これを使用する方法の例は、次のリストで見ることができます:
String username = "username";
this.session.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username);
FindByIndexNameSessionRepository の一部の実装は、他のセッション属性に自動的にインデックスを付けるためのフックを提供します。例: 多くの実装では、現在の Spring Security ユーザー名が FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME のインデックス名でインデックス付けされていることを自動的に確認します。 |
セッションのインデックスが作成されると、次のようなコードを使用して見つけることができます。
String username = "username";
Map<String, Session> sessionIdToSession = this.sessionRepository.findByPrincipalName(username);
ReactiveSessionRepository
を使用する
ReactiveSessionRepository
は、Session
インスタンスの作成、取得、永続化をノンブロッキングおよびリアクティブな方法で担当します。
可能であれば、ReactiveSessionRepository
または Session
と直接対話しないでください。代わりに、WebSession 統合を介して間接的に ReactiveSessionRepository
および Session
と対話することをお勧めします。
@EnableSpringHttpSession
を使用する
@EnableSpringHttpSession
アノテーションを @Configuration
クラスに追加して、SessionRepositoryFilter
を springSessionRepositoryFilter
という名前の Bean として公開できます。アノテーションを使用するには、単一の SessionRepository
Bean を提供する必要があります。次の例は、その方法を示しています。
@EnableSpringHttpSession
@Configuration
public class SpringHttpSessionConfig {
@Bean
public MapSessionRepository sessionRepository() {
return new MapSessionRepository(new ConcurrentHashMap<>());
}
}
セッションの有効期限のインフラストラクチャは構成されていないことに注意してください。これは、セッションの有効期限などが実装に大きく依存するためです。つまり、期限切れのセッションをクリーンアップする必要がある場合は、期限切れのセッションをクリーンアップする責任があります。
@EnableSpringWebSession
を使用する
@EnableSpringWebSession
アノテーションを @Configuration
クラスに追加して、WebSessionManager
を webSessionManager
という名前の Bean として公開できます。アノテーションを使用するには、単一の ReactiveSessionRepository
Bean を指定する必要があります。次の例は、その方法を示しています。
@Configuration(proxyBeanMethods = false)
@EnableSpringWebSession
public class SpringWebSessionConfig {
@Bean
public ReactiveSessionRepository reactiveSessionRepository() {
return new ReactiveMapSessionRepository(new ConcurrentHashMap<>());
}
}
セッションの有効期限のインフラストラクチャは構成されていないことに注意してください。これは、セッションの有効期限などが実装に大きく依存するためです。つまり、期限切れのセッションをクリーンアップする必要がある場合は、期限切れのセッションをクリーンアップする責任があります。
RedisSessionRepository
を使用する
RedisSessionRepository
は、Spring Data の RedisOperations
を使用して実装される SessionRepository
です。Web 環境では、これは通常 SessionRepositoryFilter
と組み合わせて使用されます。この実装は、セッションイベントの公開をサポートしていないことに注意してください。
RedisSessionRepository
のインスタンス化
次のリストに、新しいインスタンスを作成する方法の典型的な例を示します。
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// ... configure redisTemplate ...
SessionRepository<? extends Session> repository = new RedisSessionRepository(redisTemplate);
RedisConnectionFactory
の作成方法の詳細については、「Spring Data Redis リファレンス」を参照してください。
@EnableRedisHttpSession
を使用する
Web 環境では、新しい RedisSessionRepository
を作成する最も簡単な方法は、@EnableRedisHttpSession
を使用することです。完全な使用例はサンプルとガイド (ここから開始) にあります。次の属性を使用して、構成をカスタマイズできます。
enableIndexingAndEvents * enableIndexingAndEvents : RedisSessionRepository
の代わりに RedisIndexedSessionRepository
を使用するかどうか。デフォルトは false
です。* maxInactiveIntervalInSeconds : セッションの有効期限が切れるまでの時間 (秒単位)。* redisNamespace : セッションのアプリケーション固有の名前空間を構成できます。Redis キーとチャネル ID は、<redisNamespace>:
というプレフィックスで始まります。* flushMode : データが Redis に書き込まれるタイミングを指定できます。デフォルトは、SessionRepository
で save
が呼び出されたときのみです。値が FlushMode.IMMEDIATE
の場合、できるだけ早く Redis に書き込みます。
Redis でのセッションの表示
redis-cli をインストールした (英語) 後、redis-cli (英語) を使用して Redis (英語) の値を調べることができます。例: ターミナルウィンドウに次のコマンドを入力できます。
$ redis-cli
redis 127.0.0.1:6379> keys *
1) "spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021" (1)
1 | このキーのサフィックスは、Spring Session のセッション ID です。 |
hkeys
コマンドを使用して、各セッションの属性を表示することもできます。次の例は、その方法を示しています。
redis 127.0.0.1:6379> hkeys spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021
1) "lastAccessedTime"
2) "creationTime"
3) "maxInactiveInterval"
4) "sessionAttr:username"
redis 127.0.0.1:6379> hget spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021 sessionAttr:username
"\xac\xed\x00\x05t\x00\x03rob"
RedisIndexedSessionRepository
を使用する
RedisIndexedSessionRepository
は、Spring Data の RedisOperations
を使用して実装される SessionRepository
です。Web 環境では、これは通常 SessionRepositoryFilter
と組み合わせて使用されます。実装は、SessionDestroyedEvent
および SessionCreatedEvent
から SessionMessageListener
をサポートします。
RedisIndexedSessionRepository
のインスタンス化
次のリストに、新しいインスタンスを作成する方法の典型的な例を示します。
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// ... configure redisTemplate ...
SessionRepository<? extends Session> repository = new RedisIndexedSessionRepository(redisTemplate);
RedisConnectionFactory
の作成方法の詳細については、「Spring Data Redis リファレンス」を参照してください。
@EnableRedisHttpSession(enableIndexingAndEvents = true)
を使用する
Web 環境では、新しい RedisIndexedSessionRepository
を作成する最も簡単な方法は、@EnableRedisHttpSession(enableIndexingAndEvents = true)
を使用することです。完全な使用例はサンプルとガイド (ここから開始) にあります。次の属性を使用して、構成をカスタマイズできます。
enableIndexingAndEvents:
RedisSessionRepository
の代わりにRedisIndexedSessionRepository
を使用するかどうか。デフォルトはfalse
です。maxInactiveIntervalInSeconds: セッションが期限切れになるまでの時間(秒単位)。
redisNamespace: セッションのアプリケーション固有の名前空間を構成できます。Redis キーとチャネル ID は、プレフィックス
<redisNamespace>:
で始まります。flushMode: Redis にデータを書き込むタイミングを指定できます。デフォルトは、
save
がSessionRepository
で呼び出された場合のみです。FlushMode.IMMEDIATE
の値は、できるだけ早く Redis に書き込みます。
Redis TaskExecutor
RedisIndexedSessionRepository
は、RedisMessageListenerContainer
を使用して Redis からイベントを受信するようにサブスクライブされています。springSessionRedisTaskExecutor
、Bean springSessionRedisSubscriptionExecutor
、またはその両方という名前の Bean を作成することにより、これらのイベントのディスパッチ方法をカスタマイズできます。Redis タスクエグゼキュータの設定の詳細については、こちらを参照してください。
ストレージの詳細
次のセクションでは、操作ごとに Redis がどのように更新されるかについて概説します。次の例は、新しいセッションを作成する例を示しています。
HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe creationTime 1404360000000 \ maxInactiveInterval 1800 \ lastAccessedTime 1404360000000 \ sessionAttr:attrName someAttrValue \ sessionAttr:attrName2 someAttrValue2 EXPIRE spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe 2100 APPEND spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe "" EXPIRE spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe 1800 SADD spring:session:expirations:1439245080000 expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe EXPIRE spring:session:expirations1439245080000 2100
以降のセクションでは、詳細について説明します。
セッションの保存
各セッションは、Hash
として Redis に保存されます。各セッションは、HMSET
コマンドを使用して設定および更新されます。次の例は、各セッションがどのように保存されるかを示しています。
HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe creationTime 1404360000000 \ maxInactiveInterval 1800 \ lastAccessedTime 1404360000000 \ sessionAttr:attrName someAttrValue \ sessionAttr:attrName2 someAttrValue2
前の例では、セッションについて次のステートメントが当てはまります。
セッション ID は 33fdd1b6-b496-4b33-9f7d-df96679d32fe です。
セッションは 1404360000000 で作成されました(1/1/1970 GMT の午前 0 時からのミリ秒単位)。
セッションは 1800 秒(30 分)で期限切れになります。
セッションは 1404360000000 で最後にアクセスされました(1/1/1970 GMT の深夜からのミリ秒単位)。
セッションには 2 つの属性があります。1 つ目は
attrName
で、値はsomeAttrValue
です。2 番目のセッション属性はattrName2
という名前で、値はsomeAttrValue2
です。
最適化された書き込み
RedisIndexedSessionRepository
によって管理される Session
インスタンスは、変更されたプロパティを追跡し、それらのみを更新します。つまり、属性が 1 回書き込まれ、何度も読み取られる場合、その属性を 1 回だけ書き込む必要があります。例: 前のセクションの lsiting の attrName2
セッション属性が更新されたと想定します。保存時に次のコマンドが実行されます。
HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe sessionAttr:attrName2 newValue
セッションの有効期限
有効期限は、Session.getMaxInactiveInterval()
に基づいて EXPIRE
コマンドを使用して各セッションに関連付けられます。次の例は、典型的な EXPIRE
コマンドを示しています。
EXPIRE spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe 2100
セッションが実際に期限切れになった後、5 分に設定されている有効期限に注意してください。これは、セッションの有効期限が切れたときにセッションの値にアクセスできるようにするために必要です。有効期限は、実際に有効期限が切れてから 5 分後にセッション自体に設定され、確実にクリーンアップされますが、必要な処理を実行した後でのみです。
SessionRepository.findById(String) メソッドは、期限切れのセッションが返されないことを保証します。これは、セッションを使用する前に有効期限を確認する必要がないことを意味します。 |
Spring Session は、Redis からの削除および期限切れのキースペース通知 (英語) に依存して、それぞれ SessionDeletedEvent
および SessionExpiredEvent
を起動します。SessionDeletedEvent
または SessionExpiredEvent
は、Session
に関連付けられたリソースが確実にクリーンアップされるようにします。例: Spring Session の WebSocket サポートを使用する場合、Redis の期限切れまたは削除イベントにより、セッションに関連付けられている WebSocket 接続がすべて閉じられます。
有効期限は、セッションキー自体で直接追跡されません。これは、セッションデータが利用できなくなることを意味するためです。代わりに、特別なセッションの有効期限キーが使用されます。前の例では、expires キーは次のとおりです。
APPEND spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe "" EXPIRE spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe 1800
セッションの有効期限が切れるキーが削除または期限切れになると、キースペース通知が実際のセッションのルックアップをトリガーし、SessionDestroyedEvent
が起動されます。
Redis の有効期限のみに依存する場合の問題のひとつは、キーにアクセスしていない場合、Redis は期限切れのイベントがいつ発生するかを保証しないことです。具体的には、Redis が期限切れのキーをクリーンアップするために使用するバックグラウンドタスクは優先度の低いタスクであり、キーの期限切れをトリガーしない場合があります。詳細については、Redis ドキュメントの期限切れのイベントのタイミング (英語) セクションを参照してください。
期限切れのイベントが発生することが保証されていないという事実を回避するために、期限切れが予想されるときに各キーにアクセスできるようにすることができます。つまり、キーの TTL が期限切れになると、Redis はキーを削除し、キーにアクセスしようとすると期限切れイベントを発生させます。
このため、各セッションの有効期限も最も近い分まで追跡されます。これにより、バックグラウンドタスクが期限切れの可能性のあるセッションにアクセスして、Redis の期限切れイベントがより決定論的な方法で発生するようになります。次の例は、これらのイベントを示しています。
SADD spring:session:expirations:1439245080000 expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe EXPIRE spring:session:expirations1439245080000 2100
次に、バックグラウンドタスクはこれらのマッピングを使用して、各キーを明示的にリクエストします。キーを削除するのではなくアクセスすることで、TTL が期限切れになった場合にのみ Redis がキーを削除するようにします。
場合によっては、有効期限が切れていないのにキーが期限切れであると誤って識別する競合状態が発生する可能性があるため、キーを明示的に削除しません。分散ロックを使用しないと(パフォーマンスが低下します)、有効期限マッピングの一貫性を確保する方法はありません。キーにアクセスするだけで、そのキーの TTL が期限切れになった場合にのみキーが削除されるようになります。 |
SessionDeletedEvent
および SessionExpiredEvent
SessionDeletedEvent
と SessionExpiredEvent
はどちらも SessionDestroyedEvent
の型です。
RedisIndexedSessionRepository
は、Session
が削除されたときの SessionDeletedEvent
の起動、または Session
の有効期限が切れたときの SessionExpiredEvent
の起動をサポートします。これは、Session
に関連するリソースが適切にクリーンアップされるようにするために必要です。
例: WebSockets と統合する場合、SessionDestroyedEvent
はアクティブな WebSocket 接続のクローズを担当します。
SessionDeletedEvent
または SessionExpiredEvent
の発射は、Redis キースペースイベント (英語) をリッスンする SessionMessageListener
を介して利用可能になります。これを機能させるには、汎用コマンドおよび期限切れイベントの Redis キースペースイベントを有効にする必要があります。次の例は、その方法を示しています。
redis-cli config set notify-keyspace-events Egx
@EnableRedisHttpSession(enableIndexingAndEvents = true)
を使用する場合、SessionMessageListener
の管理と、必要な Redis キースペースイベントの有効化は自動的に行われます。ただし、セキュリティで保護された Redis 環境では、config コマンドは無効になっています。これは、Spring Session が Redis キースペースイベントを構成できないことを意味します。自動構成を無効にするには、ConfigureRedisAction.NO_OP
を Bean として追加します。
例: Java 構成では、以下を使用できます。
@Bean
ConfigureRedisAction configureRedisAction() {
return ConfigureRedisAction.NO_OP;
}
XML 構成では、以下を使用できます。
<util:constant
static-field="org.springframework.session.data.redis.config.ConfigureRedisAction.NO_OP"/>
SessionCreatedEvent
を使用する
セッションが作成されると、イベントは spring:session:channel:created:33fdd1b6-b496-4b33-9f7d-df96679d32fe
のチャネル ID で Redis に送信されます。ここで、33fdd1b6-b496-4b33-9f7d-df96679d32fe
はセッション ID です。イベントの本体は、作成されたセッションです。
MessageListener
(デフォルト)として登録されている場合、RedisIndexedSessionRepository
は Redis メッセージを SessionCreatedEvent
に変換します。
Redis でのセッションの表示
redis-cli をインストールした (英語) 後、redis-cli (英語) を使用して Redis (英語) の値を調べることができます。例: ターミナルに次のように入力できます。
$ redis-cli
redis 127.0.0.1:6379> keys *
1) "spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021" (1)
2) "spring:session:expirations:1418772300000" (2)
1 | このキーのサフィックスは、Spring Session のセッション ID です。 |
2 | このキーには、1418772300000 時に削除する必要があるすべてのセッション ID が含まれています。 |
各セッションの属性を表示することもできます。次の例は、その方法を示しています。
redis 127.0.0.1:6379> hkeys spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021
1) "lastAccessedTime"
2) "creationTime"
3) "maxInactiveInterval"
4) "sessionAttr:username"
redis 127.0.0.1:6379> hget spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021 sessionAttr:username
"\xac\xed\x00\x05t\x00\x03rob"
ReactiveRedisSessionRepository
を使用する
ReactiveRedisSessionRepository
は、Spring Data の ReactiveRedisOperations
を使用して実装される ReactiveSessionRepository
です。Web 環境では、これは通常 WebSessionStore
と組み合わせて使用されます。
ReactiveRedisSessionRepository
のインスタンス化
次の例は、新しいインスタンスを作成する方法を示しています。
// ... create and configure connectionFactory and serializationContext ...
ReactiveRedisTemplate<String, Object> redisTemplate = new ReactiveRedisTemplate<>(connectionFactory,
serializationContext);
ReactiveSessionRepository<? extends Session> repository = new ReactiveRedisSessionRepository(redisTemplate);
ReactiveRedisConnectionFactory
の作成方法の詳細については、「Spring Data Redis リファレンス」を参照してください。
@EnableRedisWebSession
を使用する
Web 環境では、新しい ReactiveRedisSessionRepository
を作成する最も簡単な方法は、@EnableRedisWebSession
を使用することです。次の属性を使用して、構成をカスタマイズできます。
maxInactiveIntervalInSeconds: セッションが期限切れになるまでの時間(秒単位)
redisNamespace: セッションのアプリケーション固有の名前空間を構成できます。Redis キーとチャネル ID は、
<redisNamespace>:
の q プレフィックスで始まります。flushMode: Redis にデータを書き込むタイミングを指定できます。デフォルトは、
save
がReactiveSessionRepository
で呼び出された場合のみです。FlushMode.IMMEDIATE
の値は、できるだけ早く Redis に書き込みます。
Redis でのセッションの表示
redis-cli をインストールした (英語) 後、redis-cli (英語) を使用して Redis (英語) の値を調べることができます。例: ターミナルウィンドウに次のコマンドを入力できます。
$ redis-cli
redis 127.0.0.1:6379> keys *
1) "spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021" (1)
1 | このキーのサフィックスは、Spring Session のセッション ID です。 |
hkeys
コマンドを使用して、各セッションの属性を表示することもできます。次の例は、その方法を示しています。
redis 127.0.0.1:6379> hkeys spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021
1) "lastAccessedTime"
2) "creationTime"
3) "maxInactiveInterval"
4) "sessionAttr:username"
redis 127.0.0.1:6379> hget spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021 sessionAttr:username
"\xac\xed\x00\x05t\x00\x03rob"
MapSessionRepository
を使用する
MapSessionRepository
を使用すると、Session
を Map
に永続化できます。キーは、Session
ID であり、値は Session
です。ConcurrentHashMap
を使用した実装は、テストまたは便利なメカニズムとして使用できます。または、分散 Map
実装で使用することもできます。例: Hazelcast で使用できます。
MapSessionRepository
のインスタンス化
次の例は、新しいインスタンスを作成する方法を示しています。
SessionRepository<? extends Session> repository = new MapSessionRepository(new ConcurrentHashMap<>());
Spring Session と Hazlecast の使用
Hazelcast サンプルは、Spring Session を Hazelcast とともに使用する方法を示す完全なアプリケーションです。
実行するには、次のコマンドを使用します。
./gradlew :samples:hazelcast:tomcatRun
Hazelcast Spring サンプルは、Spring Session を Hazelcast および Spring Security とともに使用する方法を示す完全なアプリケーションです。
これには、SessionCreatedEvent
、SessionDeletedEvent
、SessionExpiredEvent
の起動をサポートする Hazelcast MapListener
実装の例が含まれています。
実行するには、次のコマンドを使用します。
./gradlew :samples:hazelcast-spring:tomcatRun
ReactiveMapSessionRepository
を使用する
ReactiveMapSessionRepository
を使用すると、Session
を Map
に永続化できます。キーは、Session
ID であり、値は Session
です。ConcurrentHashMap
を使用した実装は、テストまたは便利なメカニズムとして使用できます。または、提供された Map
がノンブロッキングである必要があるという要件で、分散 Map
実装で使用することもできます。
JdbcIndexedSessionRepository
を使用する
JdbcIndexedSessionRepository
は、Spring の JdbcOperations
を使用してセッションをリレーショナルデータベースに格納する SessionRepository
実装です。Web 環境では、これは通常 SessionRepositoryFilter
と組み合わせて使用されます。この実装は、セッションイベントの公開をサポートしていないことに注意してください。
JdbcIndexedSessionRepository
のインスタンス化
次の例は、新しいインスタンスを作成する方法を示しています。
JdbcTemplate jdbcTemplate = new JdbcTemplate();
// ... configure jdbcTemplate ...
TransactionTemplate transactionTemplate = new TransactionTemplate();
// ... configure transactionTemplate ...
SessionRepository<? extends Session> repository = new JdbcIndexedSessionRepository(jdbcTemplate,
transactionTemplate);
JdbcTemplate
および PlatformTransactionManager
を作成および構成する方法の詳細については、Spring Framework リファレンスドキュメントを参照してください。
@EnableJdbcHttpSession
を使用する
Web 環境では、新しい JdbcIndexedSessionRepository
を作成する最も簡単な方法は、@EnableJdbcHttpSession
を使用することです。完全な使用例はサンプルとガイド (ここから開始) にあります。次の属性を使用して構成をカスタマイズできます。
tableName: Spring Session がセッションを格納するために使用するデータベーステーブルの名前
maxInactiveIntervalInSeconds: セッションが期限切れになるまでの時間(秒単位)
ストレージの詳細
デフォルトでは、この実装は SPRING_SESSION
テーブルと SPRING_SESSION_ATTRIBUTES
テーブルを使用してセッションを格納します。すでに説明したように、テーブル名をカスタマイズできることに注意してください。その場合、属性の格納に使用されるテーブルには、_ATTRIBUTES
という接尾辞が付いた提供されたテーブル名を使用して名前が付けられます。さらにカスタマイズが必要な場合は、set*Query
setter メソッドを使用して、リポジトリによって使用される SQL クエリをカスタマイズできます。この場合、sessionRepository
Bean を手動で構成する必要があります。
さまざまなデータベースベンダー間の違いのため、特にバイナリデータの保存に関しては、データベースに固有の SQL スクリプトを使用してください。ほとんどの主要なデータベースベンダーのスクリプトは、org/springframework/session/jdbc/schema-*.sql
としてパッケージ化されています。ここで、*
はターゲットデータベース型です。
例: PostgreSQL では、次のスキーマスクリプトを使用できます。
CREATE TABLE SPRING_SESSION (
PRIMARY_ID CHAR(36) NOT NULL,
SESSION_ID CHAR(36) NOT NULL,
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
);
CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,
ATTRIBUTE_NAME VARCHAR(200) NOT NULL,
ATTRIBUTE_BYTES BYTEA NOT NULL,
CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME),
CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE
);
MySQL データベースでは、次のスクリプトを使用できます。
CREATE TABLE SPRING_SESSION (
PRIMARY_ID CHAR(36) NOT NULL,
SESSION_ID CHAR(36) NOT NULL,
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,
ATTRIBUTE_NAME VARCHAR(200) NOT NULL,
ATTRIBUTE_BYTES BLOB NOT NULL,
CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME),
CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
HazelcastIndexedSessionRepository
を使用する
HazelcastIndexedSessionRepository
は、Hazelcast の分散 IMap
にセッションを格納する SessionRepository
実装です。Web 環境では、これは通常 SessionRepositoryFilter
と組み合わせて使用されます。
HazelcastIndexedSessionRepository
のインスタンス化
次の例は、新しいインスタンスを作成する方法を示しています。
Config config = new Config();
// ... configure Hazelcast ...
HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance(config);
HazelcastIndexedSessionRepository repository = new HazelcastIndexedSessionRepository(hazelcastInstance);
Hazelcast インスタンスを作成および構成する方法の詳細については、Hazelcast ドキュメント (英語) を参照してください。
@EnableHazelcastHttpSession
を使用する
Hazelcast (英語) を SessionRepository
のバッキングソースとして使用するには、@EnableHazelcastHttpSession
アノテーションを @Configuration
クラスに追加します。そうすることで、@EnableSpringHttpSession
アノテーションによって提供される機能が拡張されますが、Hazelcast で SessionRepository
が作成されます。構成を機能させるには、単一の HazelcastInstance
Bean を提供する必要があります。完全な構成例はサンプルとガイド (ここから開始) にあります。
基本的なカスタマイズ
@EnableHazelcastHttpSession
で次の属性を使用して、構成をカスタマイズできます。
maxInactiveIntervalInSeconds: セッションが期限切れになるまでの時間(秒単位)。デフォルトは 1800 秒です (30 分)
sessionMapName: セッションデータを格納するために Hazelcast で使用される分散
Map
の名前。
セッションイベント
MapListener
を使用して、分散 Map
から追加、削除、削除されたエントリに応答すると、これらのイベントによって、ApplicationEventPublisher
を介した SessionCreatedEvent
、SessionExpiredEvent
、SessionDeletedEvent
イベントの公開が(それぞれ)トリガーされます。
ストレージの詳細
セッションは、Hazelcast の分散 IMap
に保存されます。IMap
インターフェースメソッドは、get()
および put()
セッションに使用されます。さらに、values()
メソッドは、適切な ValueExtractor
(Hazelcast に登録する必要があります)とともに、FindByIndexNameSessionRepository#findByIndexNameAndIndexValue
操作をサポートします。この構成の詳細については、 Hazelcast Spring サンプルを参照してください。IMap
でのセッションの有効期限は、put()
から IMap
へのエントリの存続時間を設定するための Hazelcast のサポートによって処理されます。存続時間より長くアイドル状態になっているエントリ(セッション)は、IMap
から自動的に削除されます。
Hazelcast 構成内の IMap
に対して、max-idle-seconds
や time-to-live-seconds
などの設定を構成する必要はありません。
Hazelcast の MapStore
を使用してセッション IMap
を永続化する場合、MapStore
からセッションをリロードするときに次の制限が適用されることに注意してください。
リロードにより
EntryAddedListener
がトリガーされ、SessionCreatedEvent
が再公開されます再ロードでは、特定の
IMap
にデフォルトの TTL が使用されるため、セッションは元の TTL を失います。
CookieSerializer
を使用する
CookieSerializer
は、セッション Cookie の書き込み方法を定義するロールを果たします。Spring Session には、DefaultCookieSerializer
を使用したデフォルトの実装が付属しています。
CookieSerializer
を Bean として公開する
CookieSerializer
を Spring として公開する Bean は、@EnableRedisHttpSession
のような構成を使用する場合、既存の構成を拡張します。
次の例は、その方法を示しています。
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("JSESSIONID"); (1)
serializer.setCookiePath("/"); (2)
serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$"); (3)
return serializer;
}
1 | Cookie の名前を JSESSIONID にカスタマイズします。 |
2 | Cookie のパスを(コンテキストルートのデフォルトではなく) / になるようにカスタマイズします。 |
3 | ドメイン名のパターン(正規表現)を ^.?\\.(\\w\\.[a-z]+)$ にカスタマイズします。これにより、ドメインやアプリケーション間でセッションを共有できます。正規表現が一致しない場合、ドメインは設定されず、既存のドメインが使用されます。正規表現が一致する場合、最初のグループ化 [Oracle] (英語) がドメインとして使用されます。これは、child.example.com (英語) へのリクエストがドメインを example.com に設定することを意味します。ただし、localhost:8080/ または 192.168.1.100:8080/ (英語) へのリクエストでは、Cookie が未設定のままであるため、本番環境で変更を加えることなく、開発中も機能します。 |
ドメイン名はレスポンスに反映されるため、有効なドメイン文字のみを照合する必要があります。そうすることで、悪意のあるユーザーが HTTP レスポンスの分割 [Wikipedia] (英語) などの攻撃を実行するのを防ぎます。 |
CookieSerializer
のカスタマイズ
DefaultCookieSerializer
で次の構成オプションのいずれかを使用して、セッション Cookie の書き込み方法をカスタマイズできます。
cookieName
: 使用する Cookie の名前。デフォルト:SESSION
.useSecureCookie
: 安全な Cookie を使用するかどうかを指定します。デフォルト: 作成時のHttpServletRequest.isSecure()
の値を使用します。cookiePath
: クッキーのパス。デフォルト: コンテキストルート。cookieMaxAge
: セッションの作成時に設定される Cookie の最大有効期間を指定します。デフォルト:-1
は、ブラウザーを閉じたときに Cookie を削除する必要があることを示します。jvmRoute
: セッション ID に追加され、Cookie に含まれるサフィックスを指定します。セッションアフィニティのためにルーティングする JVM を識別するために使用されます。一部の実装(つまり、Redis)では、このオプションはパフォーマンス上の利点を提供しません。ただし、特定のユーザーのログを追跡できます。domainName
: Cookie に使用する特定のドメイン名を指定できます。このオプションは理解しやすいですが、多くの場合、開発環境と本番環境の間で異なる構成が必要になります。代わりにdomainNamePattern
を参照してください。domainNamePattern
:HttpServletRequest#getServerName()
からドメイン名を抽出するために使用される大文字と小文字を区別しないパターン。パターンは、Cookie ドメインの値を抽出するために使用される単一のグループ化を提供する必要があります。正規表現が一致しない場合、ドメインは設定されず、既存のドメインが使用されます。正規表現が一致する場合、最初のグループ化 [Oracle] (英語) がドメインとして使用されます。sameSite
:SameSite
cookie ディレクティブの値。SameSite
cookie ディレクティブの直列化を無効にするには、この値をnull
に設定します。デフォルト:Lax
ドメイン名はレスポンスに反映されるため、有効なドメイン文字のみを照合する必要があります。そうすることで、悪意のあるユーザーが HTTP レスポンスの分割 [Wikipedia] (英語) などの攻撃を実行するのを防ぎます。 |
SessionRepository
のカスタマイズ
カスタム SessionRepository
API の実装は、かなり簡単な作業です。カスタム実装を @EnableSpringHttpSession
サポートと組み合わせると、既存の Spring Session 構成機能とインフラストラクチャを再利用できます。ただし、さらに検討する価値のある側面がいくつかあります。
HTTP リクエストのライフサイクル中、HttpSession
は通常 SessionRepository
に 2 回永続化されます。最初の永続化操作は、クライアントがセッション ID にアクセスできるようになるとすぐにセッションが使用可能になるようにすることです。また、セッションにさらに変更が加えられる可能性があるため、セッションがコミットされた後に書き込む必要があります。これを念頭に置いて、通常、SessionRepository
実装は変更を追跡して、デルタのみが保存されるようにすることをお勧めします。これは、複数のリクエストが同じ HttpSession
で動作するため、競合状態が発生し、リクエストがセッション属性への相互の変更を上書きする、高度に並行した環境で特に重要です。Spring Session によって提供されるすべての SessionRepository
実装は、説明されているアプローチを使用してセッションの変更を永続化し、カスタム SessionRepository
を実装する際のガイダンスとして使用できます。
カスタム ReactiveSessionRepository
の実装にも同じ推奨事項が適用されることに注意してください。この場合、@EnableSpringWebSession
を使用する必要があります。