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 | 我們建立一個帶有泛型型別 S 的 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 | 我們建立一個帶有泛型型別 S 的 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。預設值是僅在 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 的會話識別符號。 |
您還可以使用 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。預設值是僅在
SessionRepository上呼叫save時寫入。值為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(自 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 的刪除和過期 鍵空間通知 來分別觸發 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
當會話過期鍵被刪除或過期時,鍵空間通知會觸發對實際會話的查詢,並觸發 SessionDestroyedEvent。
完全依賴 Redis 過期的缺點是,如果鍵未被訪問,Redis 無法保證何時觸發過期事件。具體來說,Redis 用於清理過期鍵的後臺任務是低優先順序任務,可能不會觸發鍵過期。有關更多詳細資訊,請參閱 Redis 文件中的 過期事件的時序 部分。
為了規避過期事件不保證發生的實際情況,我們可以確保每個鍵在其預期過期時都被訪問。這意味著,如果鍵上的 TTL 已過期,當我們嘗試訪問該鍵時,Redis 會刪除該鍵並觸發過期事件。
因此,每個會話過期時間也以最接近的分鐘進行跟蹤。這允許後臺任務訪問可能過期的會話,以確保 Redis 過期事件以更確定的方式觸發。以下示例顯示了這些事件。
SADD spring:session:expirations:1439245080000 expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe EXPIRE spring:session:expirations1439245080000 2100
然後後臺任務使用這些對映來顯式請求每個鍵。透過訪問鍵而不是刪除它,我們確保 Redis 僅在 TTL 過期時才為我們刪除鍵。
| 我們不會顯式刪除鍵,因為在某些情況下,可能會出現競爭條件,錯誤地將鍵標識為已過期而實際上沒有過期。除了使用分散式鎖(這會降低我們的效能)之外,沒有辦法確保過期對映的一致性。透過簡單地訪問鍵,我們確保只有當該鍵上的 TTL 過期時才刪除該鍵。 |
SessionDeletedEvent 和 SessionExpiredEvent
SessionDeletedEvent 和 SessionExpiredEvent 都是 SessionDestroyedEvent 的型別。
RedisIndexedSessionRepository 支援在 Session 被刪除時觸發 SessionDeletedEvent,或者在 Session 過期時觸發 SessionExpiredEvent。這對於確保與 Session 關聯的資源得到正確清理是必要的。
例如,與 WebSocket 整合時,SessionDestroyedEvent 負責關閉任何活動的 WebSocket 連線。
透過 SessionMessageListener 提供 SessionDeletedEvent 或 SessionExpiredEvent 的觸發,該監聽器監聽 Redis 鍵空間事件。為了使其工作,需要啟用 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
建立會話時,會向 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>:為字首。 -
flushMode:允許指定何時將資料寫入 Redis。預設值是僅在
ReactiveSessionRepository上呼叫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 的會話識別符號。 |
您還可以使用 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 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 允許將 Session 持久化到 Map 中,其中鍵是 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 表來儲存會話。請注意,您可以自定義表名,如前所述。在這種情況下,用於儲存屬性的表名使用提供的表名加上 _ATTRIBUTES 字尾。如果需要進一步的自定義,您可以使用 set*Query setter 方法自定義儲存庫使用的 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 中。在 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、被逐出和從分散式 Map 中刪除的條目會導致這些事件透過 ApplicationEventPublisher 觸發 SessionCreatedEvent、SessionExpiredEvent 和 SessionDeletedEvent 事件(分別)。
儲存詳情
會話儲存在 Hazelcast 的分散式 IMap 中。IMap 介面方法用於 get() 和 put() 會話。此外,values() 方法支援 FindByIndexNameSessionRepository#findByIndexNameAndIndexValue 操作,以及適當的 ValueExtractor(需要註冊到 Hazelcast)。有關此配置的更多詳細資訊,請參閱 Hazelcast Spring 示例。IMap 中會話的過期由 Hazelcast 對條目在 put() 到 IMap 時設定生存時間的支援來處理。閒置時間超過生存時間的條目(會話)將自動從 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 暴露
當您使用 @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 的路徑定製為 /(而不是預設的上下文根)。 |
| 3 | 我們將域名模式(正則表示式)定製為 `^.?\\.(\\w\\.[a-z]+)$`。這允許在多個域和應用程式之間共享會話。如果正則表示式不匹配,則不設定任何域並使用現有域。如果正則表示式匹配,則第一個分組用作域。這意味著對 child.example.com 的請求將域設定為 `example.com`。然而,對 localhost:8080/ 或 192.168.1.100:8080/ 的請求則不設定 Cookie,因此在開發中仍然有效,無需對生產環境進行任何更改。 |
| 您應該只匹配有效的域名字元,因為域名會反映在響應中。這樣做可以防止惡意使用者執行諸如 HTTP 響應拆分之類的攻擊。 |
自定義 CookieSerializer
您可以使用 DefaultCookieSerializer 上的以下任何配置選項來自定義會話 cookie 的寫入方式。
-
cookieName:要使用的 Cookie 名稱。預設值:SESSION。 -
useSecureCookie:指定是否應使用安全 Cookie。預設值:在建立時使用HttpServletRequest.isSecure()的值。 -
cookiePath:Cookie 的路徑。預設值:上下文根。 -
cookieMaxAge:指定在會話建立時要設定的 Cookie 的最大年齡。預設值:-1,表示當瀏覽器關閉時應刪除 Cookie。 -
jvmRoute:指定一個字尾,該字尾將附加到會話 ID 幷包含在 Cookie 中。用於識別要路由到哪個 JVM 以實現會話親和性。對於某些實現(即 Redis),此選項不提供效能優勢。但是,它可以幫助跟蹤特定使用者的日誌。 -
domainName:允許指定用於 Cookie 的特定域名。此選項易於理解,但通常需要在開發和生產環境之間進行不同的配置。請參閱domainNamePattern作為替代方案。 -
domainNamePattern:一個不區分大小寫的模式,用於從HttpServletRequest#getServerName()中提取域名。該模式應提供一個用於提取 Cookie 域值的分組。如果正則表示式不匹配,則不設定任何域並使用現有域。如果正則表示式匹配,則第一個分組用作域。 -
sameSite:SameSiteCookie 指令的值。要停用SameSiteCookie 指令的序列化,您可以將此值設定為null。預設值:Lax
| 您應該只匹配有效的域名字元,因為域名會反映在響應中。這樣做可以防止惡意使用者執行諸如 HTTP 響應拆分之類的攻擊。 |
自定義 SessionRepository
實現自定義 SessionRepository API 應該是一項相當直接的任務。將自定義實現與 @EnableSpringHttpSession 支援相結合,可以重用現有的 Spring Session 配置設施和基礎設施。然而,有幾個方面值得更密切的考慮。
在 HTTP 請求的生命週期中,HttpSession 通常會被持久化到 SessionRepository 兩次。第一次持久化操作是為了確保客戶端在訪問會話 ID 後立即可以使用會話,並且在會話提交後也需要寫入,因為可能會對會話進行進一步的修改。考慮到這一點,我們通常建議 SessionRepository 實現跟蹤更改,以確保只儲存增量。這在高度併發的環境中尤為重要,其中多個請求操作相同的 HttpSession,因此會導致競爭條件,請求會覆蓋彼此對會話屬性的更改。Spring Session 提供的所有 SessionRepository 實現都使用上述方法持久化會話更改,並可在您實現自定義 SessionRepository 時用作指導。
請注意,對於實現自定義 ReactiveSessionRepository,也適用相同的建議。在這種情況下,您應該使用 @EnableSpringWebSession。