API 文档

Session

一个Session是简化的Map的名称值对。spring-doc.cadn.net.cn

典型用法可能类似于下面的清单:spring-doc.cadn.net.cn

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 我们创建一个新的Session通过使用我们的SessionRepository并将其分配给S.
3 我们与Session.在我们的示例中,我们演示了如何保存UserSession.
4 现在,我们将Session.这就是为什么我们需要泛型S.这SessionRepository仅允许保存Session使用同一SessionRepository.这允许SessionRepository进行特定于实现的优化(即,仅写入已更改的属性)。
5 我们检索SessionSessionRepository.
6 我们获取持久化的User从我们的Session而无需显式转换我们的 attribute。

SessionAPI 还提供与Session实例的过期时间。spring-doc.cadn.net.cn

典型用法可能类似于下面的清单:spring-doc.cadn.net.cn

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 我们创建一个新的Session通过使用我们的SessionRepository并将其分配给S.
3 我们与Session. 在我们的示例中,我们演示了更新Session可以在过期之前处于非活动状态。
4 现在,我们将Session. 这就是为什么我们需要泛型S. 这SessionRepository仅允许保存Session使用同一SessionRepository. 这允许SessionRepository进行特定于实现的优化(即,仅写入已更改的属性)。 上次访问时间会自动更新。Session已保存。
5 我们检索SessionSessionRepository. 如果Session过期,则结果将为 null。

SessionRepository

一个SessionRepository负责创建、检索和持久化Session实例。spring-doc.cadn.net.cn

如果可能,您不应直接与SessionRepositorySession. 相反,开发人员应该更喜欢与SessionRepositorySession间接通过HttpSessionWebSocket 集成。spring-doc.cadn.net.cn

FindByIndexNameSessionRepository

Spring Session 最基本的 API 用于使用SessionSessionRepository. 此 API 特意非常简单,因此您可以轻松地提供具有基本功能的其他实现。spring-doc.cadn.net.cn

一些SessionRepositoryimplementation 也可以选择实现FindByIndexNameSessionRepository. 例如,Spring 的 Redis、JDBC 和 Hazelcast 支持库都实现了FindByIndexNameSessionRepository.spring-doc.cadn.net.cn

FindByIndexNameSessionRepository提供一种方法来查找具有给定索引名称和索引值的所有会话。 作为所有提供FindByIndexNameSessionRepositoryimplementations,您可以使用一种便捷的方法来查找特定用户的所有会话。 这是通过确保名称为FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME中填充了用户名。 您有责任确保填充该属性,因为 Spring Session 不知道正在使用的身份验证机制。 以下清单中显示了如何使用此功能的示例:spring-doc.cadn.net.cn

String username = "username";
this.session.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username);
的一些 implementationFindByIndexNameSessionRepository提供钩子以自动索引其他会话属性。 例如,许多实现会自动确保当前 Spring Security 用户名使用索引名称FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME.

为会话编制索引后,您可以使用类似于以下内容的代码进行查找:spring-doc.cadn.net.cn

String username = "username";
Map<String, Session> sessionIdToSession = this.sessionRepository.findByPrincipalName(username);

ReactiveSessionRepository

一个ReactiveSessionRepository负责创建、检索和持久化Session实例。spring-doc.cadn.net.cn

如果可能,您不应直接与ReactiveSessionRepositorySession. 相反,您应该更喜欢与ReactiveSessionRepositorySession间接通过 WebSession 集成。spring-doc.cadn.net.cn

@EnableSpringHttpSession

您可以添加@EnableSpringHttpSession注解添加到@Configuration类来公开SessionRepositoryFilter作为名为springSessionRepositoryFilter. 要使用注释,您必须提供单个SessionRepository豆。 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

@EnableSpringHttpSession
@Configuration
public class SpringHttpSessionConfig {

	@Bean
	public MapSessionRepository sessionRepository() {
		return new MapSessionRepository(new ConcurrentHashMap<>());
	}

}

请注意,没有为您配置会话过期的基础设施。 这是因为 session expiration 等内容高度依赖于实现。 这意味着,如果您需要清理过期的会话,则您负责清理过期的会话。spring-doc.cadn.net.cn

@EnableSpringWebSession

您可以添加@EnableSpringWebSession注解添加到@Configuration类来公开WebSessionManager作为名为webSessionManager. 要使用注释,您必须提供单个ReactiveSessionRepository豆。 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

@Configuration(proxyBeanMethods = false)
@EnableSpringWebSession
public class SpringWebSessionConfig {

	@Bean
	public ReactiveSessionRepository reactiveSessionRepository() {
		return new ReactiveMapSessionRepository(new ConcurrentHashMap<>());
	}

}

请注意,没有为您配置会话过期的基础设施。 这是因为 session expiration 等内容高度依赖于实现。 这意味着,如果您需要清理过期的会话,则您负责清理过期的会话。spring-doc.cadn.net.cn

RedisSessionRepository

RedisSessionRepository是一个SessionRepository这是通过使用 Spring Data 的RedisOperations. 在 Web 环境中,这通常与SessionRepositoryFilter. 请注意,此实现不支持发布会话事件。spring-doc.cadn.net.cn

实例化RedisSessionRepository

您可以在下面的清单中看到如何创建新实例的典型示例:spring-doc.cadn.net.cn

RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();

// ... configure redisTemplate ...

SessionRepository<? extends Session> repository = new RedisSessionRepository(redisTemplate);

有关如何创建RedisConnectionFactory,请参阅 Spring Data Redis 参考。spring-doc.cadn.net.cn

@EnableRedisHttpSession

在 Web 环境中,创建新RedisSessionRepository是使用@EnableRedisHttpSession. 您可以在示例和指南(从这里开始)中找到完整的示例用法。 您可以使用以下属性自定义配置:spring-doc.cadn.net.cn

enableIndexingAnd事件 * enableIndexingAndEvents:是否使用RedisIndexedSessionRepository而不是RedisSessionRepository.默认值为false. * maxInactiveIntervalInSeconds:会话过期前的时间量(以秒为单位)。 * redisNamespace:允许为会话配置特定于应用程序的命名空间。Redis 键和频道 ID 以<redisNamespace>:. * flushMode:允许指定何时将数据写入 Redis。默认值仅在save调用SessionRepository. 值FlushMode.IMMEDIATE写入 Redis 的请求。spring-doc.cadn.net.cn

习惯RedisSerializer

您可以通过创建一个名为springSessionDefaultRedisSerializer实现RedisSerializer<Object>.spring-doc.cadn.net.cn

在 Redis 中查看会话

安装 redis-cli 后,您可以使用 redis-cli 检查 Redis 中的值。 例如,您可以在终端窗口中输入以下命令:spring-doc.cadn.net.cn

$ redis-cli
redis 127.0.0.1:6379> keys *
1) "spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021" (1)
1 此键的后缀是 Spring Session 的会话标识符。

您还可以使用hkeys命令。 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

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是一个SessionRepository这是通过使用 Spring Data 的RedisOperations. 在 Web 环境中,这通常与SessionRepositoryFilter. 实现支持SessionDestroyedEventSessionCreatedEvent通过SessionMessageListener.spring-doc.cadn.net.cn

实例化RedisIndexedSessionRepository

您可以在下面的清单中看到如何创建新实例的典型示例:spring-doc.cadn.net.cn

RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();

// ... configure redisTemplate ...

SessionRepository<? extends Session> repository = new RedisIndexedSessionRepository(redisTemplate);

有关如何创建RedisConnectionFactory,请参阅 Spring Data Redis 参考。spring-doc.cadn.net.cn

@EnableRedisHttpSession(enableIndexingAndEvents = true)

在 Web 环境中,创建新RedisIndexedSessionRepository是使用@EnableRedisHttpSession(enableIndexingAndEvents = true). 您可以在示例和指南(从这里开始)中找到完整的示例用法。 您可以使用以下属性自定义配置:spring-doc.cadn.net.cn

  • enableIndexingAndEvents:是否使用RedisIndexedSessionRepository而不是RedisSessionRepository.默认值为false.spring-doc.cadn.net.cn

  • maxInactiveIntervalInSeconds:会话过期前的时间量(以秒为单位)。spring-doc.cadn.net.cn

  • redisNamespace:允许为会话配置特定于应用程序的命名空间。Redis 键和频道 ID 以<redisNamespace>:.spring-doc.cadn.net.cn

  • flushMode:允许指定何时将数据写入 Redis。默认值仅在save调用SessionRepository. 值FlushMode.IMMEDIATE写入 Redis 的请求。spring-doc.cadn.net.cn

习惯RedisSerializer

您可以通过创建一个名为springSessionDefaultRedisSerializer实现RedisSerializer<Object>.spring-doc.cadn.net.cn

RedisTaskExecutor

RedisIndexedSessionRepository订阅以使用RedisMessageListenerContainer. 您可以通过创建一个名为springSessionRedisTaskExecutor、一个 beanspringSessionRedisSubscriptionExecutor和/或两者。 您可以在此处找到有关配置 Redis 任务执行程序的更多详细信息。spring-doc.cadn.net.cn

存储详细信息

以下部分概述了如何为每个作更新 Redis。 以下示例显示了创建新会话的示例:spring-doc.cadn.net.cn

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

后续部分将介绍详细信息。spring-doc.cadn.net.cn

保存会话

每个会话都作为Hash. 每个会话都使用HMSET命令。 以下示例显示了每个会话的存储方式:spring-doc.cadn.net.cn

HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe creationTime 1404360000000 \
	maxInactiveInterval 1800 \
	lastAccessedTime 1404360000000 \
	sessionAttr:attrName someAttrValue \
	sessionAttr:attrName2 someAttrValue2

在前面的示例中,以下关于会话的陈述为 true:spring-doc.cadn.net.cn

  • 会话 ID 为 33fdd1b6-b496-4b33-9f7d-df96679d32fe。spring-doc.cadn.net.cn

  • 会话是在 1404360000000 创建的(以毫秒为单位,自 1970 年 1 月 1 日格林威治标准时间午夜以来)。spring-doc.cadn.net.cn

  • 会话将在 1800 秒(30 分钟)后过期。spring-doc.cadn.net.cn

  • 上次访问会话的时间是 1404360000000(自 1970 年 1 月 1 日格林威治标准时间午夜以来的毫秒数)。spring-doc.cadn.net.cn

  • 会话有两个属性。 首先是attrName,值为someAttrValue. 第二个 session 属性名为attrName2,值为someAttrValue2.spring-doc.cadn.net.cn

优化的写入

Session管理的实例RedisIndexedSessionRepository跟踪已更改的属性,并仅更新这些属性。 这意味着,如果一个 attribute 被写入一次并多次读取,我们只需要写入该 attribute 一次。 例如,假设attrName2更新了上一节中 LSITING 的 session 属性。 保存时将运行以下命令:spring-doc.cadn.net.cn

HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe sessionAttr:attrName2 newValue

会话过期

过期时间使用EXPIRE命令,基于Session.getMaxInactiveInterval(). 以下示例显示了一个典型的EXPIRE命令:spring-doc.cadn.net.cn

EXPIRE spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe 2100

请注意,设置为会话实际过期后 5 分钟的过期时间。 这是必需的,以便在会话过期时可以访问会话的值。 在会话实际过期 5 分钟后,对会话本身设置过期时间,以确保它被清理,但前提是我们执行了任何必要的处理。spring-doc.cadn.net.cn

SessionRepository.findById(String)方法确保不会返回任何过期的会话。 这意味着您在使用会话之前无需检查过期时间。

Spring Session 依赖于来自 Redis 的 delete 和 expired keyspace 通知来触发SessionDeletedEvent以及SessionExpiredEvent分别。SessionDeletedEventSessionExpiredEvent确保与Session被清理干净。 例如,当您使用 Spring Session 的 WebSocket 支持时,Redis expired 或 delete 事件会触发与会话关联的任何 WebSocket 连接被关闭。spring-doc.cadn.net.cn

Expiration 不会直接在 session key 本身上进行跟踪,因为这意味着 session 数据将不再可用。相反,使用特殊的会话过期密钥。在前面的示例中,expires 键如下所示:spring-doc.cadn.net.cn

APPEND spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe ""
EXPIRE spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe 1800

当会话过期密钥被删除或过期时,密钥空间通知会触发对实际会话的查找,并且会触发一个SessionDestroyedEvent被解雇。spring-doc.cadn.net.cn

完全依赖 Redis 过期的一个问题是,如果尚未访问密钥,Redis 无法保证何时触发过期事件。 具体来说,Redis 用于清理过期密钥的后台任务是低优先级任务,可能不会触发密钥过期。 有关更多详细信息,请参阅 Redis 文档中的 Timing of Expired Events 部分。spring-doc.cadn.net.cn

为了规避不能保证过期事件发生的事实,我们可以确保每个密钥在预期过期时被访问。 这意味着,如果密钥上的 TTL 过期,Redis 会删除该密钥,并在我们尝试访问该密钥时触发过期事件。spring-doc.cadn.net.cn

因此,每个会话过期时间也会跟踪到最接近的分钟数。 这允许后台任务访问可能过期的会话,以确保以更具确定性的方式触发 Redis 过期事件。 以下示例显示了这些事件:spring-doc.cadn.net.cn

SADD spring:session:expirations:1439245080000 expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe
EXPIRE spring:session:expirations1439245080000 2100

然后,后台任务使用这些映射显式请求每个密钥。 通过访问密钥而不是删除密钥,我们确保 Redis 仅在 TTL 过期时为我们删除密钥。spring-doc.cadn.net.cn

我们不会明确删除密钥,因为在某些情况下,可能存在争用条件,导致密钥错误地标识为已过期,而密钥未过期。 如果不使用分布式锁(这会降低我们的性能),就无法确保过期映射的一致性。 通过简单地访问密钥,我们确保只有在该密钥的 TTL 过期时才会删除该密钥。

SessionDeletedEventSessionExpiredEvent

SessionDeletedEventSessionExpiredEvent都是SessionDestroyedEvent.spring-doc.cadn.net.cn

RedisIndexedSessionRepository支持发射SessionDeletedEventSession已删除,或者SessionExpiredEventSession到期。 这是确保与Session得到适当的清理。spring-doc.cadn.net.cn

例如,在与 WebSockets 集成时,SessionDestroyedEvent负责关闭任何活动的 WebSocket 连接。spring-doc.cadn.net.cn

开火SessionDeletedEventSessionExpiredEvent通过SessionMessageListener,它监听 Redis Keyspace 事件。 为此,需要启用 Generic 命令的 Redis Keyspace 事件和 Expired 事件。 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

redis-cli config set notify-keyspace-events Egx

如果您使用@EnableRedisHttpSession(enableIndexingAndEvents = true),管理SessionMessageListener并且启用必要的 Redis Keyspace 事件是自动完成的。 但是,在安全的 Redis 环境中,config 命令被禁用。 这意味着 Spring Session 无法为您配置 Redis Keyspace 事件。 要禁用自动配置,请添加ConfigureRedisAction.NO_OP作为 Bean 进行。spring-doc.cadn.net.cn

例如,使用 Java 配置,您可以使用以下内容:spring-doc.cadn.net.cn

@Bean
ConfigureRedisAction configureRedisAction() {
	return ConfigureRedisAction.NO_OP;
}

在 XML 配置中,您可以使用以下内容:spring-doc.cadn.net.cn

<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。事件的正文是创建的会话。spring-doc.cadn.net.cn

如果注册为MessageListener(默认值)、RedisIndexedSessionRepository然后将 Redis 消息转换为SessionCreatedEvent.spring-doc.cadn.net.cn

在 Redis 中查看会话

安装 redis-cli 后,您可以使用 redis-cli 检查 Redis 中的值。 例如,您可以在终端中输入以下内容:spring-doc.cadn.net.cn

$ 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 此键包含当时应删除的所有会话 ID1418772300000.

您还可以查看每个会话的属性。 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

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是一个ReactiveSessionRepository这是通过使用 Spring Data 的ReactiveRedisOperations. 在 Web 环境中,这通常与WebSessionStore.spring-doc.cadn.net.cn

实例化ReactiveRedisSessionRepository

以下示例显示如何创建新实例:spring-doc.cadn.net.cn

// ... 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 参考。spring-doc.cadn.net.cn

@EnableRedisWebSession

在 Web 环境中,创建新ReactiveRedisSessionRepository是使用@EnableRedisWebSession. 您可以使用以下属性自定义配置:spring-doc.cadn.net.cn

  • maxInactiveIntervalInSeconds:会话过期前的时间量(以秒为单位)spring-doc.cadn.net.cn

  • redisNamespace:允许为会话配置特定于应用程序的命名空间。Redis 键和频道 ID 以 q 开头<redisNamespace>:.spring-doc.cadn.net.cn

  • flushMode:允许指定何时将数据写入 Redis。默认值仅在save调用ReactiveSessionRepository. 值FlushMode.IMMEDIATE写入 Redis 的请求。spring-doc.cadn.net.cn

优化的写入

Session管理的实例ReactiveRedisSessionRepository跟踪已更改的属性,并仅更新这些属性。 这意味着,如果一个 attribute 被写入一次并多次读取,我们只需要写入该 attribute 一次。spring-doc.cadn.net.cn

在 Redis 中查看会话

安装 redis-cli 后,您可以使用 redis-cli 检查 Redis 中的值。 例如,您可以在终端窗口中输入以下命令:spring-doc.cadn.net.cn

$ redis-cli
redis 127.0.0.1:6379> keys *
1) "spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021" (1)
1 此键的后缀是 Spring Session 的会话标识符。

您还可以使用hkeys命令。 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

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允许持久化SessionMap,其中 key 是SessionID 和 ID 的值为Session. 您可以将该实现与ConcurrentHashMap作为一种测试或便利机制。 或者,您可以将它与分布式Map实现。例如,它可以与 Hazelcast 一起使用。spring-doc.cadn.net.cn

实例化MapSessionRepository

以下示例显示如何创建新实例:spring-doc.cadn.net.cn

SessionRepository<? extends Session> repository = new MapSessionRepository(new ConcurrentHashMap<>());

使用 Spring Session 和 Hazlecast

Hazelcast 示例是一个完整的应用程序,演示了如何将 Spring Session 与 Hazelcast 一起使用。spring-doc.cadn.net.cn

要运行它,请使用以下命令:spring-doc.cadn.net.cn

	./gradlew :samples:hazelcast:tomcatRun

Hazelcast Spring Sample 是一个完整的应用程序,演示了如何将 Spring Session 与 Hazelcast 和 Spring Security 一起使用。spring-doc.cadn.net.cn

它包括示例 HazelcastMapListener支持 fireing 的实现SessionCreatedEvent,SessionDeletedEventSessionExpiredEvent.spring-doc.cadn.net.cn

要运行它,请使用以下命令:spring-doc.cadn.net.cn

	./gradlew :samples:hazelcast-spring:tomcatRun

ReactiveMapSessionRepository

ReactiveMapSessionRepository允许持久化SessionMap,其中 key 是SessionID 和 ID 的值为Session. 您可以将该实现与ConcurrentHashMap作为一种测试或便利机制。 或者,您可以将它与分布式Map实现中实现,并要求提供的Map必须是非阻塞的。spring-doc.cadn.net.cn

JdbcIndexedSessionRepository

JdbcIndexedSessionRepository是一个SessionRepository使用 Spring 的JdbcOperations将会话存储在关系数据库中。 在 Web 环境中,这通常与SessionRepositoryFilter. 请注意,此实现不支持发布会话事件。spring-doc.cadn.net.cn

实例化JdbcIndexedSessionRepository

以下示例显示如何创建新实例:spring-doc.cadn.net.cn

JdbcTemplate jdbcTemplate = new JdbcTemplate();

// ... configure jdbcTemplate ...

TransactionTemplate transactionTemplate = new TransactionTemplate();

// ... configure transactionTemplate ...

SessionRepository<? extends Session> repository = new JdbcIndexedSessionRepository(jdbcTemplate,
		transactionTemplate);

有关如何创建和配置的其他信息JdbcTemplatePlatformTransactionManager,请参阅 Spring Framework 参考文档spring-doc.cadn.net.cn

@EnableJdbcHttpSession

在 Web 环境中,创建新JdbcIndexedSessionRepository是使用@EnableJdbcHttpSession. 您可以在 Samples and Guides (从这里开始) 中找到完整的示例用法您可以使用以下属性自定义配置:spring-doc.cadn.net.cn

定制LobHandler

您可以通过创建名为springSessionLobHandler实现LobHandler.spring-doc.cadn.net.cn

定制ConversionService

您可以通过提供ConversionService实例。 在典型的 Spring 环境中工作时,默认的ConversionServiceBean(名为conversionService) 被自动选取并用于序列化和反序列化。 但是,您可以覆盖默认值ConversionService通过提供一个名为springSessionConversionService.spring-doc.cadn.net.cn

存储详细信息

默认情况下,此实现使用SPRING_SESSIONSPRING_SESSION_ATTRIBUTEStables 来存储会话。 请注意,您可以自定义表名称,如前所述。在这种情况下,用于存储属性的表使用提供的表名命名,该表名后缀为_ATTRIBUTES. 如果需要进一步的自定义,您可以使用set*Querysetter 方法。在这种情况下,您需要手动配置sessionRepository豆。spring-doc.cadn.net.cn

由于各种数据库供应商之间存在差异,尤其是在存储二进制数据时,请确保使用特定于您的数据库的 SQL 脚本。 大多数主要数据库供应商的脚本都打包为org/springframework/session/jdbc/schema-*.sql,其中 是目标数据库类型。*spring-doc.cadn.net.cn

例如,对于 PostgreSQL,您可以使用以下架构脚本:spring-doc.cadn.net.cn

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 数据库,您可以使用以下脚本:spring-doc.cadn.net.cn

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;

事务管理

中的所有 JDBC作JdbcIndexedSessionRepository以事务性方式执行。 执行事务时,propagation 设置为REQUIRES_NEW为了避免由于干扰现有事务而导致意外行为(例如,运行save作)。spring-doc.cadn.net.cn

HazelcastIndexedSessionRepository

HazelcastIndexedSessionRepository是一个SessionRepository将会话存储在 Hazelcast 的分布式IMap. 在 Web 环境中,这通常与SessionRepositoryFilter.spring-doc.cadn.net.cn

实例化HazelcastIndexedSessionRepository

以下示例显示如何创建新实例:spring-doc.cadn.net.cn

Config config = new Config();

// ... configure Hazelcast ...

HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance(config);

HazelcastIndexedSessionRepository repository = new HazelcastIndexedSessionRepository(hazelcastInstance);

有关如何创建和配置 Hazelcast 实例的其他信息,请参阅 Hazelcast 文档spring-doc.cadn.net.cn

@EnableHazelcastHttpSession

要使用 Hazelcast 作为SessionRepository中,您可以添加@EnableHazelcastHttpSession注解添加到@Configuration类。 这样做扩展了@EnableSpringHttpSession注解,但会使SessionRepository在 Hazelcast 为您服务。 您必须提供单个HazelcastInstancebean 才能使配置正常工作。 您可以在 Samples and Guides (Start Here) 中找到完整的配置示例。spring-doc.cadn.net.cn

基本自定义

您可以在@EnableHazelcastHttpSession要自定义配置,请执行以下作:spring-doc.cadn.net.cn

  • maxInactiveIntervalInSeconds:会话过期前的时间量(以秒为单位)。默认值为 1800 秒(30 分钟)spring-doc.cadn.net.cn

  • sessionMapName:分布式Map在 Hazelcast 中用于存储会话数据。spring-doc.cadn.net.cn

会话事件

使用MapListener响应从分布式Map使这些事件触发SessionCreatedEvent,SessionExpiredEventSessionDeletedEvent事件(分别)通过ApplicationEventPublisher.spring-doc.cadn.net.cn

存储详细信息

会话存储在分布式IMap在 Hazelcast。 这IMapinterface 方法用于get()put()会话。 此外,values()方法支持FindByIndexNameSessionRepository#findByIndexNameAndIndexValue作,以及适当的ValueExtractor(需要在 Hazelcast 中注册)。有关此配置的更多详细信息,请参阅 Hazelcast Spring Sample。 会话在IMap由 Hazelcast 的支持处理,用于在条目上设置生存时间put()IMap.空闲时间超过生存时间的条目(会话)将自动从IMap.spring-doc.cadn.net.cn

您不需要配置任何设置,例如max-idle-secondstime-to-live-seconds对于IMap在 Hazelcast 配置中。spring-doc.cadn.net.cn

请注意,如果您使用 Hazelcast 的MapStore保留会话IMap,从MapStore:spring-doc.cadn.net.cn

CookieSerializer

一个CookieSerializer负责定义会话 Cookie 的写入方式。 Spring Session 带有一个默认实现,使用DefaultCookieSerializer.spring-doc.cadn.net.cn

暴露CookieSerializer作为 Bean

公开CookieSerializer,因为 Spring Bean 会在您使用@EnableRedisHttpSession.spring-doc.cadn.net.cn

以下示例显示了如何执行此作:spring-doc.cadn.net.cn

	@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 我们将域名模式(正则表达式)自定义为^.?\\.(\\w\\.[a-z]+)$. 这允许跨域和应用程序共享会话。 如果正则表达式不匹配,则不设置域,并使用现有域。 如果正则表达式匹配,则第一个分组将用作域。 这意味着对 child.example.com 的请求会将域设置为example.com. 但是,对 localhost:8080/192.168.1.100:8080/ 的请求会取消设置 cookie,因此,它仍然可以在开发中工作,而无需进行任何生产更改。
您只应匹配有效的域字符,因为域名会反映在响应中。 这样做可以防止恶意用户执行 HTTP 响应拆分等攻击。

定制CookieSerializer

您可以通过在DefaultCookieSerializer.spring-doc.cadn.net.cn

  • cookieName:要使用的 Cookie 的名称。 违约:SESSION.spring-doc.cadn.net.cn

  • useSecureCookie:指定是否应使用安全 Cookie。 默认值:使用HttpServletRequest.isSecure()在创建时。spring-doc.cadn.net.cn

  • cookiePath:Cookie 的路径。 Default:上下文根。spring-doc.cadn.net.cn

  • cookieMaxAge:指定在创建会话时要设置的 Cookie 的最长期限。 违约:-1,这表示应在浏览器关闭时删除 Cookie。spring-doc.cadn.net.cn

  • jvmRoute:指定要附加到会话 ID 并包含在 Cookie 中的后缀。 用于标识要路由到哪个 JVM 以实现会话关联性。 对于某些实现(即 Redis),此选项不会提供任何性能优势。 但是,它可以帮助跟踪特定用户的日志。spring-doc.cadn.net.cn

  • domainName:允许指定要用于 Cookie 的特定域名。 此选项易于理解,但通常需要在开发和生产环境之间使用不同的配置。 看domainNamePattern作为替代方案。spring-doc.cadn.net.cn

  • domainNamePattern:一种不区分大小写的模式,用于从HttpServletRequest#getServerName(). 该模式应提供用于提取 Cookie 域值的单个分组。 如果正则表达式不匹配,则不设置域,并使用现有域。 如果正则表达式匹配,则第一个分组将用作域。spring-doc.cadn.net.cn

  • sameSite:该值SameSitecookie 指令。 要禁用SameSitecookie 指令,您可以将此值设置为null. 违约:Laxspring-doc.cadn.net.cn

您只应匹配有效的域字符,因为域名会反映在响应中。 这样做可以防止恶意用户执行 HTTP 响应拆分等攻击。

定制SessionRepository

实现自定义SessionRepositoryAPI 应该是一项相当简单的任务。 将自定义实现与@EnableSpringHttpSessionsupport 允许您重用现有的 Spring Session 配置工具和基础设施。 但是,有几个方面值得仔细考虑。spring-doc.cadn.net.cn

在 HTTP 请求的生命周期内,HttpSession通常持久化为SessionRepository两次。 第一个 persist作是确保 Client 端可以访问会话 ID 后立即对 Client 端可用,并且还需要在提交 session 后写入,因为可能会对 session 进行进一步的修改。 考虑到这一点,我们通常建议SessionRepositoryimplementation 跟踪更改以确保仅保存增量。 这在高度并发的环境中尤为重要,因为在这种环境中,多个请求对同一请求进行作HttpSession因此,会导致争用条件,请求会覆盖彼此对会话属性的更改。 所有SessionRepositorySpring Session 提供的实现使用所描述的方法来持久化会话更改,并且可以在实现自定义时用作指导SessionRepository.spring-doc.cadn.net.cn

请注意,相同的建议也适用于实现自定义ReactiveSessionRepository也。 在这种情况下,您应该使用@EnableSpringWebSession.spring-doc.cadn.net.cn


APP信息