对于最新的稳定版本,请使用 Spring Session 3.4.0! |
API 文档
您可以在线浏览完整的 Javadoc。以下部分介绍了关键 API:
用Session
A 是名称值对的简化。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 | 我们使用 our 创建一个新的变量,并将其分配给 类型的变量。Session SessionRepository S |
3 | 我们与 .在我们的示例中,我们演示了将 a 保存到 .Session User Session |
4 | 现在,我们将 .这就是为什么我们需要泛型类型 .唯一允许保存使用同一 .这允许 进行特定于 implementation 的优化(即,仅编写已更改的属性)。Session S SessionRepository Session SessionRepository SessionRepository |
5 | 我们从 中检索 。Session SessionRepository |
6 | 我们从 our 获取 persisted,而无需显式转换我们的 attribute。User Session |
该 API 还提供与实例过期相关的属性。Session
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 | 我们使用 our 创建一个新的变量,并将其分配给 类型的变量。Session SessionRepository S |
3 | 我们与 .
在我们的示例中,我们演示了如何更新 在过期之前可以处于非活动状态的时间。Session Session |
4 | 现在,我们将 .
这就是为什么我们需要泛型类型 .
允许仅保存使用同一 .
这允许 进行特定于 implementation 的优化(即,仅编写已更改的属性)。
上次访问时间会在保存时自动更新。Session S SessionRepository Session SessionRepository SessionRepository Session |
5 | 我们从 中检索 。
如果 已过期,则结果将为 null。Session SessionRepository Session |
用SessionRepository
A 负责创建、检索和持久化实例。SessionRepository
Session
如果可能,您不应直接与 或 .
相反,开发人员应该更喜欢与 HttpSession
和 WebSocket 集成进行交互,并通过 HttpSession 和 WebSocket 集成间接进行交互。SessionRepository
Session
SessionRepository
Session
用FindByIndexNameSessionRepository
Spring Session 使用 的最基本 API 是 .
此 API 特意非常简单,因此您可以轻松地提供具有基本功能的其他实现。Session
SessionRepository
某些实施也可能选择实施 。
例如,Spring 的 Redis、JDBC 和 Hazelcast 支持库都实现了 .SessionRepository
FindByIndexNameSessionRepository
FindByIndexNameSessionRepository
这提供了一种方法来查找具有给定索引名称和索引值的所有会话。
作为所有提供的实现都支持的常见使用案例,您可以使用一种便捷的方法来查找特定用户的所有会话。
这是通过确保 name 为 的 session 属性填充 username 来完成的。
您有责任确保填充该属性,因为 Spring Session 不知道正在使用的身份验证机制。
以下清单中显示了如何使用此功能的示例:FindByIndexNameSessionRepository
FindByIndexNameSessionRepository
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME
String username = "username";
this.session.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username);
的一些实现提供了钩子来自动索引其他 session 属性。
例如,许多实现会自动确保当前 Spring Security 用户名使用索引名称进行索引。FindByIndexNameSessionRepository FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME |
为会话编制索引后,您可以使用类似于以下内容的代码进行查找:
String username = "username";
Map<String, Session> sessionIdToSession = this.sessionRepository.findByPrincipalName(username);
用ReactiveSessionRepository
A 负责以非阻塞和反应式方式创建、检索和持久化实例。ReactiveSessionRepository
Session
如果可能,您不应直接与 或 .
相反,您应该更喜欢与 WebSession 集成进行交互并通过 WebSession 集成间接进行交互。ReactiveSessionRepository
Session
ReactiveSessionRepository
Session
用@EnableSpringHttpSession
您可以将注释添加到类中,以将 Bean 公开为名为 .
为了使用注释,必须提供单个 bean。
以下示例显示了如何执行此操作:@EnableSpringHttpSession
@Configuration
SessionRepositoryFilter
springSessionRepositoryFilter
SessionRepository
@EnableSpringHttpSession
@Configuration
public class SpringHttpSessionConfig {
@Bean
public MapSessionRepository sessionRepository() {
return new MapSessionRepository(new ConcurrentHashMap<>());
}
}
请注意,没有为您配置会话过期的基础设施。 这是因为 session expiration 等内容高度依赖于实现。 这意味着,如果您需要清理过期的会话,则您负责清理过期的会话。
用@EnableSpringWebSession
您可以将注释添加到类中,以将 Bean 公开为名为 .
要使用 Comments,必须提供单个 Bean。
以下示例显示了如何执行此操作:@EnableSpringWebSession
@Configuration
WebSessionManager
webSessionManager
ReactiveSessionRepository
@Configuration(proxyBeanMethods = false)
@EnableSpringWebSession
public class SpringWebSessionConfig {
@Bean
public ReactiveSessionRepository reactiveSessionRepository() {
return new ReactiveMapSessionRepository(new ConcurrentHashMap<>());
}
}
请注意,没有为您配置会话过期的基础设施。 这是因为 session expiration 等内容高度依赖于实现。 这意味着,如果您需要清理过期的会话,则您负责清理过期的会话。
用RedisSessionRepository
RedisSessionRepository
是使用 Spring Data 的 .
在 Web 环境中,这通常与 结合使用。
请注意,此实现不支持发布会话事件。SessionRepository
RedisOperations
SessionRepositoryFilter
实例化RedisSessionRepository
您可以在下面的清单中看到如何创建新实例的典型示例:
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// ... configure redisTemplate ...
SessionRepository<? extends Session> repository = new RedisSessionRepository(redisTemplate);
有关如何创建 的其他信息,请参阅 Spring Data Redis Reference。RedisConnectionFactory
用@EnableRedisHttpSession
在 Web 环境中,创建新的最简单方法是使用 .
您可以在示例和指南(从这里开始)中找到完整的示例用法。
您可以使用以下属性自定义配置:RedisSessionRepository
@EnableRedisHttpSession
enableIndexingAnd事件
* enableIndexingAndEvents:是否使用 a 而不是 .默认值为 .
* maxInactiveIntervalInSeconds:会话过期前的时间量(以秒为单位)。
* redisNamespace:允许为会话配置特定于应用程序的命名空间。Redis 键和频道 ID 以 的前缀 开头。
* flushMode:允许指定何时将数据写入 Redis。默认值仅在 上调用时。
值 write to Redis as soon as soon。RedisIndexedSessionRepository
RedisSessionRepository
false
<redisNamespace>:
save
SessionRepository
FlushMode.IMMEDIATE
在 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 的会话标识符。 |
您还可以使用 command 查看每个会话的属性。
以下示例显示了如何执行此操作: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 的 .
在 Web 环境中,这通常与 结合使用。
实现支持并通过 .SessionRepository
RedisOperations
SessionRepositoryFilter
SessionDestroyedEvent
SessionCreatedEvent
SessionMessageListener
实例化RedisIndexedSessionRepository
您可以在下面的清单中看到如何创建新实例的典型示例:
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// ... configure redisTemplate ...
SessionRepository<? extends Session> repository = new RedisIndexedSessionRepository(redisTemplate);
有关如何创建 的其他信息,请参阅 Spring Data Redis Reference。RedisConnectionFactory
用@EnableRedisHttpSession(enableIndexingAndEvents = true)
在 Web 环境中,创建新的最简单方法是使用 .
您可以在示例和指南(从这里开始)中找到完整的示例用法。
您可以使用以下属性自定义配置:RedisIndexedSessionRepository
@EnableRedisHttpSession(enableIndexingAndEvents = true)
-
enableIndexingAndEvents:是否使用 a 而不是 .默认值为 .
RedisIndexedSessionRepository
RedisSessionRepository
false
-
maxInactiveIntervalInSeconds:会话过期前的时间量(以秒为单位)。
-
redisNamespace:允许为会话配置特定于应用程序的命名空间。Redis 键和频道 ID 以 的前缀 开头。
<redisNamespace>:
-
flushMode:允许指定何时将数据写入 Redis。默认值仅在 上调用时。 值 write to Redis as soon as soon。
save
SessionRepository
FlushMode.IMMEDIATE
RedisTaskExecutor
RedisIndexedSessionRepository
订阅以使用 Redis 接收事件。
您可以通过创建名为 、 bean 或两者的 bean 来自定义这些事件的调度方式。
您可以在此处找到有关配置 Redis 任务执行程序的更多详细信息。RedisMessageListenerContainer
springSessionRedisTaskExecutor
springSessionRedisSubscriptionExecutor
存储详细信息
以下部分概述了如何为每个操作更新 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
后续部分将介绍详细信息。
保存会话
每个会话都以 .
每个会话都使用 command 进行设置和更新。
以下示例显示了每个会话的存储方式:Hash
HMSET
HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe creationTime 1404360000000 \ maxInactiveInterval 1800 \ lastAccessedTime 1404360000000 \ sessionAttr:attrName someAttrValue \ sessionAttr:attrName2 someAttrValue2
在前面的示例中,以下关于会话的陈述为 true:
-
会话 ID 为 33fdd1b6-b496-4b33-9f7d-df96679d32fe。
-
会话是在 1404360000000 创建的(以毫秒为单位,自 1970 年 1 月 1 日格林威治标准时间午夜以来)。
-
会话将在 1800 秒(30 分钟)后过期。
-
上次访问会话的时间是 1404360000000(自 1970 年 1 月 1 日格林威治标准时间午夜以来的毫秒数)。
-
会话有两个属性。 第一个是 ,值为 。 第二个 session 属性名为 ,值为 。
attrName
someAttrValue
attrName2
someAttrValue2
优化的写入
管理的实例会跟踪已更改的属性,并仅更新这些属性。
这意味着,如果一个 attribute 被写入一次并多次读取,我们只需要写入该 attribute 一次。
例如,假设上一节中 lsiting 的 session 属性已更新。
保存时将运行以下命令:Session
RedisIndexedSessionRepository
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
请注意,设置为会话实际过期后 5 分钟的过期时间。 这是必需的,以便在会话过期时可以访问会话的值。 在会话实际过期 5 分钟后,对会话本身设置过期时间,以确保它被清理,但前提是我们执行了任何必要的处理。
该方法确保不会返回任何过期的会话。
这意味着您在使用会话之前无需检查过期时间。SessionRepository.findById(String) |
Spring Session 依赖于来自 Redis 的 delete 和 expired keyspace 通知来分别触发 SessionDeletedEvent
和 SessionExpiredEvent
。 或者确保清理与 关联的资源。
例如,当您使用 Spring Session 的 WebSocket 支持时,Redis expired 或 delete 事件会触发与会话关联的任何 WebSocket 连接被关闭。SessionDeletedEvent
SessionExpiredEvent
Session
Expiration 不会直接在 session key 本身上进行跟踪,因为这意味着 session 数据将不再可用。相反,使用特殊的会话过期密钥。在前面的示例中,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 文档中的 Timing of Expired Events 部分。
为了规避不能保证过期事件发生的事实,我们可以确保每个密钥在预期过期时被访问。 这意味着,如果密钥上的 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
支持在 A 被删除时触发 A 或在 A 过期时触发 A。
这对于确保正确清理与 关联的资源是必要的。SessionDeletedEvent
Session
SessionExpiredEvent
Session
Session
例如,当与 WebSockets 集成时,它负责关闭任何活动的 WebSocket 连接。SessionDestroyedEvent
触发或通过 提供,它侦听 Redis Keyspace 事件。
为此,需要启用 Generic 命令的 Redis Keyspace 事件和 Expired 事件。
以下示例显示了如何执行此操作:SessionDeletedEvent
SessionExpiredEvent
SessionMessageListener
redis-cli config set notify-keyspace-events Egx
如果您使用 ,则管理和启用必要的 Redis Keyspace 事件是自动完成的。
但是,在安全的 Redis 环境中,config 命令被禁用。
这意味着 Spring Session 无法为您配置 Redis Keyspace 事件。
要禁用自动配置,请添加为 Bean。@EnableRedisHttpSession(enableIndexingAndEvents = true)
SessionMessageListener
ConfigureRedisAction.NO_OP
例如,使用 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 为 、 的事件
其中 是会话 ID。事件的正文是创建的会话。spring:session:channel:created:33fdd1b6-b496-4b33-9f7d-df96679d32fe
33fdd1b6-b496-4b33-9f7d-df96679d32fe
如果注册为 a(默认),则将 Redis 消息转换为 .MessageListener
RedisIndexedSessionRepository
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 | 此键包含应在 time 删除的所有会话 ID。1418772300000 |
您还可以查看每个会话的属性。 以下示例显示了如何执行此操作:
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 的 .
在 Web 环境中,这通常与 结合使用。ReactiveSessionRepository
ReactiveRedisOperations
WebSessionStore
实例化ReactiveRedisSessionRepository
以下示例显示如何创建新实例:
// ... create and configure connectionFactory and serializationContext ...
ReactiveRedisTemplate<String, Object> redisTemplate = new ReactiveRedisTemplate<>(connectionFactory,
serializationContext);
ReactiveSessionRepository<? extends Session> repository = new ReactiveRedisSessionRepository(redisTemplate);
有关如何创建 的其他信息,请参阅 Spring Data Redis Reference。ReactiveRedisConnectionFactory
用@EnableRedisWebSession
在 Web 环境中,创建新的最简单方法是使用 .
您可以使用以下属性自定义配置:ReactiveRedisSessionRepository
@EnableRedisWebSession
-
maxInactiveIntervalInSeconds:会话过期前的时间量(以秒为单位)
-
redisNamespace:允许为会话配置特定于应用程序的命名空间。Redis 键和通道 ID 以 q 前缀 .
<redisNamespace>:
-
flushMode:允许指定何时将数据写入 Redis。默认值仅在 上调用时。 值 write to Redis as soon as soon。
save
ReactiveSessionRepository
FlushMode.IMMEDIATE
在 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 的会话标识符。 |
您还可以使用 command 查看每个会话的属性。
以下示例显示了如何执行此操作: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
允许保留在 中,键是 ID,值是 。
您可以将 implementation 与 用作测试或便利机制。
或者,您可以将其与分布式实现一起使用。例如,它可以与 Hazelcast 一起使用。MapSessionRepository
Session
Map
Session
Session
ConcurrentHashMap
Map
实例化MapSessionRepository
以下示例显示如何创建新实例:
SessionRepository<? extends Session> repository = new MapSessionRepository(new ConcurrentHashMap<>());
使用 Spring Session 和 Hazlecast
Hazelcast 示例是一个完整的应用程序,演示了如何将 Spring Session 与 Hazelcast 一起使用。
要运行它,请使用以下命令:
./gradlew :samples:hazelcast:tomcatRun
Hazelcast Spring Sample 是一个完整的应用程序,演示了如何将 Spring Session 与 Hazelcast 和 Spring Security 一起使用。
它包括支持触发、 、 和 的 Hazelcast 实现示例。MapListener
SessionCreatedEvent
SessionDeletedEvent
SessionExpiredEvent
要运行它,请使用以下命令:
./gradlew :samples:hazelcast-spring:tomcatRun
用ReactiveMapSessionRepository
允许保留在 中,键是 ID,值是 。
您可以将 implementation 与 用作测试或便利机制。
或者,你可以将它与分布式实现一起使用,但要求提供的必须是非阻塞的。ReactiveMapSessionRepository
Session
Map
Session
Session
ConcurrentHashMap
Map
Map
用JdbcIndexedSessionRepository
JdbcIndexedSessionRepository
是使用 Spring 将会话存储在关系数据库中的实现。
在 Web 环境中,这通常与 结合使用。
请注意,此实现不支持发布会话事件。SessionRepository
JdbcOperations
SessionRepositoryFilter
实例化JdbcIndexedSessionRepository
以下示例显示如何创建新实例:
JdbcTemplate jdbcTemplate = new JdbcTemplate();
// ... configure jdbcTemplate ...
TransactionTemplate transactionTemplate = new TransactionTemplate();
// ... configure transactionTemplate ...
SessionRepository<? extends Session> repository = new JdbcIndexedSessionRepository(jdbcTemplate,
transactionTemplate);
有关如何创建和配置的更多信息,请参阅 Spring Framework Reference Documentation。JdbcTemplate
PlatformTransactionManager
用@EnableJdbcHttpSession
在 Web 环境中,创建新的最简单方法是使用 .
您可以在 Samples and Guides (从这里开始) 中找到完整的示例用法您可以使用以下属性自定义配置:JdbcIndexedSessionRepository
@EnableJdbcHttpSession
-
tableName:Spring Session 用于存储会话的数据库表的名称
-
maxInactiveIntervalInSeconds:会话过期前的时间(以秒为单位)
存储详细信息
默认情况下,此实现使用 and tables 来存储会话。
请注意,您可以自定义表名称,如前所述。在这种情况下,用于存储属性的表使用提供的表名命名,该表名后缀为 。
如果需要进一步的自定义,您可以使用 setter 方法自定义存储库使用的 SQL 查询。在这种情况下,您需要手动配置 Bean。SPRING_SESSION
SPRING_SESSION_ATTRIBUTES
_ATTRIBUTES
set*Query
sessionRepository
由于各种数据库供应商之间存在差异,尤其是在存储二进制数据时,请确保使用特定于您的数据库的 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 的分布式 .
在 Web 环境中,这通常与 结合使用。SessionRepository
IMap
SessionRepositoryFilter
实例化HazelcastIndexedSessionRepository
以下示例显示如何创建新实例:
Config config = new Config();
// ... configure Hazelcast ...
HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance(config);
HazelcastIndexedSessionRepository repository = new HazelcastIndexedSessionRepository(hazelcastInstance);
有关如何创建和配置 Hazelcast 实例的其他信息,请参阅 Hazelcast 文档。
用@EnableHazelcastHttpSession
要将 Hazelcast 用作 的后备源,可以将 Comments 添加到类中。
这样做扩展了 Comments 提供的功能,但在 Hazelcast 中为您服务。
您必须提供单个 Bean 才能使配置正常工作。
您可以在 Samples and Guides (Start Here) 中找到完整的配置示例。SessionRepository
@EnableHazelcastHttpSession
@Configuration
@EnableSpringHttpSession
SessionRepository
HazelcastInstance
基本自定义
您可以使用以下属性 on 来自定义配置:@EnableHazelcastHttpSession
-
maxInactiveIntervalInSeconds:会话过期前的时间量(以秒为单位)。默认值为 1800 秒(30 分钟)
-
sessionMapName:Hazelcast 中用于存储会话数据的分布式的名称。
Map
会话事件
使用 a 响应从分布式中添加、逐出和删除的条目会导致这些事件触发 、 和 事件(分别)通过 发布。MapListener
Map
SessionCreatedEvent
SessionExpiredEvent
SessionDeletedEvent
ApplicationEventPublisher
存储详细信息
会话存储在 Hazelcast 的分布式中。
接口方法用于 和 Sessions。
此外,该方法还支持操作以及 appropriate (需要向 Hazelcast 注册)。有关此配置的更多详细信息,请参阅 Hazelcast Spring Sample。
会话的过期由 Hazelcast 的支持处理,用于设置条目进入 .空闲时间超过生存时间的条目(会话)将自动从 中删除。IMap
IMap
get()
put()
values()
FindByIndexNameSessionRepository#findByIndexNameAndIndexValue
ValueExtractor
IMap
put()
IMap
IMap
您不需要在 Hazelcast 配置中配置任何设置,例如 或 for 。max-idle-seconds
time-to-live-seconds
IMap
请注意,如果使用 Hazelcast 来保留会话,则从中重新加载会话时,以下限制适用:MapStore
IMap
MapStore
-
重新加载触发器会导致重新发布
EntryAddedListener
SessionCreatedEvent
-
重新加载对给定的 TTL 使用默认 TTL 会导致会话丢失其原始 TTL
IMap
用CookieSerializer
A 负责定义会话 cookie 的写入方式。
Spring Session 附带了一个使用 .CookieSerializer
DefaultCookieSerializer
作为 Bean 公开CookieSerializer
当您使用 .CookieSerializer
@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 的路径自定义为 be(而不是上下文根的默认值)。/ |
3 | 我们将域名模式(正则表达式)自定义为 .
这允许跨域和应用程序共享会话。
如果正则表达式不匹配,则不设置域,并使用现有域。
如果正则表达式匹配,则第一个分组将用作域。
这意味着对 child.example.com 的请求会将域设置为 .
但是,对 localhost:8080/ 或 192.168.1.100:8080/ 的请求会取消设置 cookie,因此,它仍然可以在开发中工作,而无需进行任何生产更改。^.?\\.(\\w\\.[a-z]+)$ example.com |
您只应匹配有效的域字符,因为域名会反映在响应中。 这样做可以防止恶意用户执行 HTTP 响应拆分等攻击。 |
定制CookieSerializer
您可以在 .DefaultCookieSerializer
-
cookieName
:要使用的 Cookie 的名称。 违约:。SESSION
-
useSecureCookie
:指定是否应使用安全 Cookie。 Default:使用 at the time of creation 的值。HttpServletRequest.isSecure()
-
cookiePath
:Cookie 的路径。 Default:上下文根。 -
cookieMaxAge
:指定在创建会话时要设置的 Cookie 的最长期限。 Default: ,这表示在浏览器关闭时应删除 Cookie。-1
-
jvmRoute
:指定要附加到会话 ID 并包含在 Cookie 中的后缀。 用于标识要路由到哪个 JVM 以实现会话关联性。 对于某些实现(即 Redis),此选项不会提供任何性能优势。 但是,它可以帮助跟踪特定用户的日志。 -
domainName
:允许指定要用于 Cookie 的特定域名。 此选项易于理解,但通常需要在开发和生产环境之间使用不同的配置。 请参阅 作为替代项。domainNamePattern
-
domainNamePattern
:一种不区分大小写的模式,用于从 中提取域名。 该模式应提供用于提取 Cookie 域值的单个分组。 如果正则表达式不匹配,则不设置域,并使用现有域。 如果正则表达式匹配,则第一个分组将用作域。HttpServletRequest#getServerName()
-
sameSite
:cookie 指令的值。 要禁用 cookie 指令的序列化,可以将此值设置为 。 违约:SameSite
SameSite
null
Lax
您只应匹配有效的域字符,因为域名会反映在响应中。 这样做可以防止恶意用户执行 HTTP 响应拆分等攻击。 |
定制SessionRepository
实现自定义 SessionRepository
API 应该是一个相当简单的任务。
将自定义实现与 @EnableSpringHttpSession
支持相结合,可以重用现有的 Spring Session 配置工具和基础设施。
但是,有几个方面值得仔细考虑。
在 HTTP 请求的生命周期内,通常会保留两次。
第一个 persist 操作是确保 Client 端可以访问会话 ID 后立即对 Client 端可用,并且还需要在提交 session 后写入,因为可能会对 session 进行进一步的修改。
考虑到这一点,我们通常建议 implementation 跟踪更改,以确保仅保存增量。
这在高度并发的环境中尤其重要,因为在高度并发的环境中,多个请求对同一请求进行操作,因此会导致争用条件,请求会覆盖彼此对会话属性的更改。
Spring Session 提供的所有实现都使用所描述的方法来保留会话更改,并且可以在实现 custom 时用作指导。HttpSession
SessionRepository
SessionRepository
HttpSession
SessionRepository
SessionRepository
请注意,相同的建议也适用于实现自定义 ReactiveSessionRepository
。
在这种情况下,您应该使用 @EnableSpringWebSession
。