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 | 我們建立一個具有泛型類型 SessionRepository 實例 S ,其擴充 Session 。泛型類型在我們的類別中定義。 |
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 | 我們建立一個具有泛型類型 SessionRepository 實例 S ,其擴充 Session 。泛型類型在我們的類別中定義。 |
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
Spring Session 使用 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:是否使用 RedisIndexedSessionRepository
而不是 RedisSessionRepository
。預設值為 false
。 * maxInactiveIntervalInSeconds:會期過期前的時間量,以秒為單位。 * redisNamespace:允許為會期組態應用程式特定的命名空間。Redis 金鑰和通道 ID 以 <redisNamespace>:
字首開頭。 * flushMode:允許指定何時將資料寫入 Redis。預設值僅在 save
在 SessionRepository
上叫用時。值為 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 的會期識別碼。 |
您也可以使用 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
結合使用。此實作透過 SessionMessageListener
支援 SessionDestroyedEvent
和 SessionCreatedEvent
。
實例化 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:是否使用
RedisIndexedSessionRepository
而不是RedisSessionRepository
。預設值為false
。 -
maxInactiveIntervalInSeconds:會期過期前的時間量,以秒為單位。
-
redisNamespace:允許為會期組態應用程式特定的命名空間。Redis 金鑰和通道 ID 以
<redisNamespace>:
字首開頭。 -
flushMode:允許指定何時將資料寫入 Redis。預設值僅在
save
在SessionRepository
上叫用時。值為FlushMode.IMMEDIATE
會盡快寫入 Redis。
Redis TaskExecutor
RedisIndexedSessionRepository
已訂閱以使用 RedisMessageListenerContainer
接收來自 Redis 的事件。您可以透過建立名為 springSessionRedisTaskExecutor
的 bean、bean springSessionRedisSubscriptionExecutor
或兩者來客製化這些事件的分派方式。您可以在 此處 找到有關組態 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 (自 1970 年 1 月 1 日午夜 GMT 以來的毫秒數)。
-
會期在 1800 秒 (30 分鐘) 後過期。
-
會期上次存取時間為 1404360000000 (自 1970 年 1 月 1 日午夜 GMT 以來的毫秒數)。
-
會期有兩個屬性。第一個是
attrName
,值為someAttrValue
。第二個會期屬性名為attrName2
,值為someAttrValue2
。
最佳化寫入
由 RedisIndexedSessionRepository
管理的 Session
實例會追蹤已變更的屬性,並且僅更新這些屬性。這表示,如果屬性寫入一次並讀取多次,我們只需要寫入該屬性一次。例如,假設已更新前一節清單中的 attrName2
會期屬性。儲存時會執行以下命令
HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe sessionAttr:attrName2 newValue
會期過期
過期會使用 EXPIRE
命令與每個會期相關聯,根據 Session.getMaxInactiveInterval()
。以下範例顯示典型的 EXPIRE
命令
EXPIRE spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe 2100
請注意,過期時間設定為在會期實際過期後五分鐘。這是必要的,以便在會期過期時可以存取會期的值。會期的過期時間設定為在實際過期後五分鐘,以確保會期已清除,但僅在我們執行任何必要的處理之後。
SessionRepository.findById(String) 方法可確保不會傳回過期的會期。這表示您無需在使用會期之前檢查過期時間。 |
Spring Session 依賴來自 Redis 的刪除和過期 keyspace 通知 來觸發 SessionDeletedEvent
和 SessionExpiredEvent
。SessionDeletedEvent
或 SessionExpiredEvent
可確保與 Session
相關聯的資源已清除。例如,當您使用 Spring Session 的 WebSocket 支援時,Redis 過期或刪除事件會觸發與會期相關聯的任何 WebSocket 連線關閉。
過期時間不會直接在會期金鑰本身上追蹤,因為這表示會期資料將不再可用。而是使用特殊的會期過期金鑰。在上述範例中,過期金鑰如下
APPEND spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe "" EXPIRE spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe 1800
當會期過期金鑰刪除或過期時,keyspace 通知會觸發實際會期的查閱,並觸發 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
的類型。
當 Session
刪除時,RedisIndexedSessionRepository
支援觸發 SessionDeletedEvent
;當 Session
過期時,支援觸發 SessionExpiredEvent
。這是必要的,以確保與 Session
相關聯的資源已正確清除。
例如,與 WebSockets 整合時,SessionDestroyedEvent
負責關閉任何作用中的 WebSocket 連線。
觸發 SessionDeletedEvent
或 SessionExpiredEvent
可透過 SessionMessageListener
取得,後者會監聽 Redis Keyspace 事件。為了使其運作,需要啟用 Generic 命令和 Expired 事件的 Redis Keyspace 事件。以下範例顯示如何執行此操作
redis-cli config set notify-keyspace-events Egx
如果您使用 @EnableRedisHttpSession(enableIndexingAndEvents = true)
,則會自動完成管理 SessionMessageListener
和啟用必要的 Redis Keyspace 事件。但是,在安全的 Redis 環境中,config 命令已停用。這表示 Spring Session 無法為您組態 Redis Keyspace 事件。若要停用自動組態,請新增 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
建立會期時,會將事件傳送至 Redis,通道 ID 為 spring:session:channel:created:33fdd1b6-b496-4b33-9f7d-df96679d32fe
,其中 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 的會期識別碼。 |
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 的會期識別碼。 |
您也可以使用 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
允許在 Map
中持久化 Session
,其中金鑰為 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 搭配使用。
它包含範例 Hazelcast MapListener
實作,支援觸發 SessionCreatedEvent
、SessionDeletedEvent
和 SessionExpiredEvent
。
若要執行它,請使用以下命令
./gradlew :samples:hazelcast-spring:tomcatRun
使用 ReactiveMapSessionRepository
ReactiveMapSessionRepository
允許在 Map
中持久化 Session
,其中金鑰為 Session
ID,值為 Session
。您可以搭配 ConcurrentHashMap
使用此實作作為測試或便利機制。或者,您可以將其與分散式 Map
實作搭配使用,但要求提供的 Map
必須是非封鎖的。
使用 JdbcIndexedSessionRepository
JdbcIndexedSessionRepository
是 SessionRepository
實作,使用 Spring 的 JdbcOperations
將會期儲存在關聯式資料庫中。在 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
資料表來儲存 Session。請注意,您可以自訂資料表名稱,如前所述。在這種情況下,用於儲存屬性的資料表名稱會使用提供的資料表名稱加上 _ATTRIBUTES
後綴。如果需要進一步的自訂,您可以透過使用 set*Query
Setter 方法來自訂 Repository 使用的 SQL 查詢。在這種情況下,您需要手動配置 sessionRepository
Bean。
由於各家資料庫廠商之間的差異,尤其是在儲存二進制資料方面,請務必使用特定於您的資料庫的 SQL 腳本。大多數主要資料庫廠商的腳本都打包為 org/springframework/session/jdbc/schema-*.sql
,其中 *
是目標資料庫類型。
例如,對於 PostgreSQL,您可以使用以下 Schema 腳本
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
是一個 SessionRepository
實作,用於在 Hazelcast 的分散式 IMap
中儲存 Session。在 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:Session 過期前的時間量,以秒為單位。預設值為 1800 秒(30 分鐘)
-
sessionMapName:在 Hazelcast 中用於儲存 Session 資料的分散式
Map
的名稱。
Session 事件
使用 MapListener
來回應從分散式 Map
中新增、逐出和移除的條目,會導致這些事件觸發透過 ApplicationEventPublisher
發布 SessionCreatedEvent
、SessionExpiredEvent
和 SessionDeletedEvent
事件(分別)。
儲存細節
Session 儲存在 Hazelcast 的分散式 IMap
中。IMap
介面方法用於 get()
和 put()
Session。此外,values()
方法支援 FindByIndexNameSessionRepository#findByIndexNameAndIndexValue
操作,以及適當的 ValueExtractor
(需要向 Hazelcast 註冊)。有關此配置的更多詳細資訊,請參閱 Hazelcast Spring 範例。IMap
中 Session 的過期是由 Hazelcast 對在條目 put()
到 IMap
時設定存活時間的支援來處理的。閒置時間超過存活時間的條目(Session)會自動從 IMap
中移除。
您應該不需要為 Hazelcast 配置中的 IMap
配置任何設定,例如 max-idle-seconds
或 time-to-live-seconds
。
請注意,如果您使用 Hazelcast 的 MapStore
來持久化您的 Session IMap
,則從 MapStore
重新載入 Session 時,以下限制適用
-
重新載入會觸發
EntryAddedListener
,導致SessionCreatedEvent
重新發布 -
重新載入針對給定的
IMap
使用預設 TTL,導致 Session 遺失其原始 TTL
使用 CookieSerializer
CookieSerializer
負責定義如何寫入 Session Cookie。Spring Session 隨附使用 DefaultCookieSerializer
的預設實作。
將 CookieSerializer
作為 Bean 公開
當您使用諸如 @EnableRedisHttpSession
之類的配置時,將 CookieSerializer
作為 Spring Bean 公開會擴增現有的配置。
以下範例示範如何執行此操作
@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 的路徑為 / (而不是預設的 Context Root)。 |
3 | 我們自訂網域名稱模式(正規表示式)為 ^.?\\.(\\w\\.[a-z]+)$ 。這允許跨網域和應用程式共用 Session。如果正規表示式不符,則不設定網域並使用現有的網域。如果正規表示式相符,則第一個群組會用作網域。這表示對 child.example.com 的請求會將網域設定為 example.com 。但是,對 localhost:8080/ 或 192.168.1.100:8080/ 的請求會使 Cookie 保持未設定狀態,因此,在開發中仍然可以運作,而無需對生產環境進行任何變更。 |
您應該僅比對有效的網域字元,因為網域名稱會反映在回應中。這樣做可以防止惡意使用者執行諸如 HTTP 回應分割 之類的攻擊。 |
自訂 CookieSerializer
您可以使用 DefaultCookieSerializer
上的以下任何配置選項來自訂 Session Cookie 的寫入方式。
-
cookieName
:要使用的 Cookie 名稱。預設值:SESSION
。 -
useSecureCookie
:指定是否應使用安全 Cookie。預設值:在建立時使用HttpServletRequest.isSecure()
的值。 -
cookiePath
:Cookie 的路徑。預設值:Context Root。 -
cookieMaxAge
:指定在建立 Session 時要設定的 Cookie 的最大存活時間。預設值:-1
,表示在瀏覽器關閉時應移除 Cookie。 -
jvmRoute
:指定要附加到 Session ID 並包含在 Cookie 中的後綴。用於識別要路由到哪個 JVM 以進行 Session Affinity。對於某些實作(即 Redis),此選項不提供效能優勢。但是,它可以幫助追蹤特定使用者的記錄。 -
domainName
:允許指定要用於 Cookie 的特定網域名稱。此選項易於理解,但通常需要在開發和生產環境之間進行不同的配置。請參閱domainNamePattern
作為替代方案。 -
domainNamePattern
:用於從HttpServletRequest#getServerName()
中提取網域名稱的不區分大小寫的模式。該模式應提供一個群組,用於提取 Cookie 網域的值。如果正規表示式不符,則不設定網域並使用現有的網域。如果正規表示式相符,則第一個群組會用作網域。 -
sameSite
:SameSite
Cookie 指令的值。要停用SameSite
Cookie 指令的序列化,您可以將此值設定為null
。預設值:Lax
您應該僅比對有效的網域字元,因為網域名稱會反映在回應中。這樣做可以防止惡意使用者執行諸如 HTTP 回應分割 之類的攻擊。 |
自訂 SessionRepository
實作自訂的 SessionRepository
API 應該是一項相當簡單的任務。將自訂實作與 @EnableSpringHttpSession
支援結合使用,可讓您重複使用現有的 Spring Session 配置設施和基礎架構。但是,有幾個方面值得更仔細地考慮。
在 HTTP 請求的生命週期中,HttpSession
通常會持久化到 SessionRepository
兩次。第一次持久化操作是為了確保在用戶端可以存取 Session ID 後,Session 即可供用戶端使用,並且在 Session 提交後也必須寫入,因為可能會對 Session 進行進一步的修改。記住這一點,我們通常建議 SessionRepository
實作追蹤變更,以確保僅儲存差異。這在高度並發的環境中尤其重要,在這些環境中,多個請求對同一個 HttpSession
進行操作,因此會導致競爭條件,請求會覆寫彼此對 Session 屬性的變更。Spring Session 提供的所有 SessionRepository
實作都使用所描述的方法來持久化 Session 變更,並且可以在您實作自訂 SessionRepository
時用作指導。
請注意,相同的建議也適用於實作自訂的 ReactiveSessionRepository
。在這種情況下,您應該使用 @EnableSpringWebSession
。