对于最新稳定版本,请使用 Spring Session 4.0.2spring-doc.cadn.net.cn

API 文档

使用Session

一个 Session 是名称值对的简化版 Mapspring-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实例,并使用扩展了Session的泛型类型S。泛型类型在我们的类中定义。
2 我们通过使用我们的SessionRepository来创建一个新的Session,并将其赋值给一个类型为S的变量。
3 我们与Session进行交互。在我们的示例中,我们展示了将一个User保存到Session的过程。
4 我们现在保存了Session。这就是我们需要泛型类型S的原因。SessionRepository只允许保存由使用相同的SessionRepository创建或检索的Session实例。这使得SessionRepository能够进行实现特定的优化(也就是说,只写已经改变的属性)。
5 我们从SessionRepository中检索到了Session
6 我们从Session中获得了持久化的User,而无需显式地将属性进行类型转换。

The Session API 也提供了与 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实例,并使用扩展了Session的泛型类型S。泛型类型在我们的类中定义。
2 我们通过使用我们的SessionRepository来创建一个新的Session,并将其赋值给一个类型为S的变量。
3 我们与Session进行交互。 在我们的示例中,我们将展示如何更新Session在失效前可以保持不活跃的时间长度。
4 我们现在保存了Session。 这就是我们需要泛型类型S的原因。 SessionRepository允许只保存使用同一个SessionRepository创建或检索的Session实例。 这使得SessionRepository可以进行实现特定的优化(即,仅写入已更改的属性)。 当Session被保存时,最后访问时间会自动更新。
5 我们从SessionRepository中检索到了Session。 如果Session过期了,结果将会是null。

使用SessionRepository

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

如果可能,您不应直接与 SessionRepositorySession 交互。 相反,开发人员应倾向于通过 HttpSessionWebSocket 集成间接与 SessionRepositorySession 进行交互。spring-doc.cadn.net.cn

使用FindByIndexNameSessionRepository

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

有些SessionRepository实现也可能选择实现FindByIndexNameSessionRepository。 例如,Spring的Redis、JDBC和Hazelcast支持库都实现了FindByIndexNameSessionRepositoryspring-doc.cadn.net.cn

FindByIndexNameSessionRepository 提供了一个方法,用于查找具有给定索引名称和索引值的所有会话。 作为一种由所有提供的 FindByIndexNameSessionRepository 实现支持的常见用例,您可以使用一个方便的方法来查找特定用户的所有会话。 这可以通过确保带有名称为 FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME 的会话属性被填充用户名来实现。 由于 Spring Session 并不了解正在使用的身份验证机制,因此您有责任确保该属性被正确填充。 以下是如何使用此功能的一个示例: 在以下代码段中可以看到如何使用它。 spring-doc.cadn.net.cn

String username = "username";
this.session.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username);
一些实现FindByIndexNameSessionRepository提供了自动索引其他会话属性的钩子。 例如,许多实现会自动确保当前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进行交互。 相反,您应该通过WebSession集成间接地与ReactiveSessionRepositorySession进行交互。spring-doc.cadn.net.cn

使用@EnableSpringHttpSession

您可以将@EnableSpringHttpSession注解添加到@Configuration类中,以使SessionRepositoryFilter作为名为springSessionRepositoryFilter的bean暴露出来。 要使用该注解,您必须提供一个单一的SessionRepository bean。 以下示例展示了如何做到这一点:spring-doc.cadn.net.cn

@EnableSpringHttpSession
@Configuration
public class SpringHttpSessionConfig {

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

}

注意,Spring框架未为您配置会话过期的基础设施。 这是因为像会话过期这样的事情高度依赖于具体的实现。 这意味着,如果您需要清理已过期的会话,则必须负责清理这些已过期的会话。spring-doc.cadn.net.cn

使用@EnableSpringWebSession

您可以将@EnableSpringWebSession注解添加到@Configuration类中,以暴露WebSessionManager作为名为webSessionManager的bean。 要使用该注解,您必须提供一个单一的ReactiveSessionRepository bean。下面的例子展示了如何实现这一点:spring-doc.cadn.net.cn

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

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

}

请注意,Spring框架并未为您配置会话过期的基础设施。 这是因为像会话过期这样的事情高度依赖于具体的实现方式。 这意味着,如果您需要清理过期的会话,请自行处理这些过期的会话。spring-doc.cadn.net.cn

使用RedisSessionRepository

RedisSessionRepository 是一个由 Spring Data 的 RedisOperations 实现的 SessionRepository。 在 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

enableIndexingAndEvents * enableIndexingAndEvents: 是否使用RedisIndexedSessionRepository代替RedisSessionRepository。默认值是false。 * maxInactiveIntervalInSeconds: 会话在秒数内失效之前的时间长度。 * redisNamespace: 允许为会话配置应用程序特定的命名空间。Redis键和频道ID以<redisNamespace>:前缀开始。 * flushMode: 允许指定何时将数据写入Redis。默认情况下,只有在调用SessionRepository上的save时才会写入。一个值为FlushMode.IMMEDIATE会尽可能快地写入Redis。spring-doc.cadn.net.cn

自定义RedisSerializer

您可以自定义序列化方式,通过创建名为springSessionDefaultRedisSerializer的bean来实现,该bean需实现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 是一个由 Spring Data 的 RedisOperations 实现的 SessionRepository。 在 web 环境中,这通常与 SessionRepositoryFilter 结合使用。 实现支持通过 SessionMessageListener 提供的 SessionDestroyedEventSessionCreatedEventspring-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。默认值是falsespring-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的bean来实现,该bean需实现RedisSerializer<Object>spring-doc.cadn.net.cn

RedisTaskExecutor

RedisIndexedSessionRepository 是订阅从 Redis 接收事件的。它使用的是一个 RedisMessageListenerContainer。 你可以通过创建名为 springSessionRedisTaskExecutor 的 bean、springSessionRedisSubscriptionExecutor 的 bean,或者同时创建两者来自定义这些事件的分发方式。 你可以在 这里 找到更多关于配置 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

保存会话

每个会话都存储在 Redis 中,作为 `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

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

  • session ID是 33fdd1b6-b496-4b33-9f7d-df96679d32fe。spring-doc.cadn.net.cn

  • 会话创建于1404360000000(自1970年1月1日格林尼治时间午夜以来的毫秒数)。spring-doc.cadn.net.cn

  • The session expires in 1800 seconds (30 minutes).spring-doc.cadn.net.cn

  • The session was last accessed at 1404360000000 (in milliseconds since midnight of 1/1/1970 GMT).spring-doc.cadn.net.cn

  • 该会话有两个属性。 第一个是attrName,其值为someAttrValue。 第二个会话属性名为attrName2,其值为someAttrValue2spring-doc.cadn.net.cn

优化的写入

The Session 实例由 RedisIndexedSessionRepository 管理,会跟踪已更改的属性并仅更新这些属性。 这意味着,如果一个属性只写入一次而被读取多次,则我们只需写入该属性一次。 例如,假设前一节列表中的 attrName2 会话属性被更新。在保存时将运行以下命令: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

注意,设置的过期时间是在会话实际过期后五分钟。这是必要的,以便在会话过期时仍能访问会话值。 为此,在会话实际过期后的五分钟内设置了过期时间以确保其被清理,但在此之前我们还需要执行任何必要的处理。spring-doc.cadn.net.cn

The SessionRepository.findById(String) method ensures that no expired sessions are returned. This means that you need not check the expiration before using a session.

Spring Session 依赖 Redis 的删除和过期 键空间通知,分别触发 SessionDeletedEventSessionExpiredEventSessionDeletedEventSessionExpiredEvent 确保与 Session 关联的资源被清理。 例如,当您使用 Spring Session 的 WebSocket 支持时,Redis 的过期或删除事件会触发与该会话关联的任何 WebSocket 连接被关闭。spring-doc.cadn.net.cn

会话密钥本身不直接跟踪过期时间,因为这意味着会话数据将不再可用。相反,使用一个特殊的会话过期键。在前面的示例中,会话过期键如下所示: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

当会话过期或键被删除或过期时,键空间通知会触发实际会话的查找,并发送一个SessionDestroyedEventspring-doc.cadn.net.cn

依赖于Redis过期时间独占地一个问题是,如果键没有被访问,Redis不会保证在何时触发过期事件。spring-doc.cadn.net.cn

spring-doc.cadn.net.cn

具体来说,Redis用于清理过期键的后台任务是一个低优先级的任务,并且可能不会触发键的过期。spring-doc.cadn.net.cn

有关更多详细信息,请参阅Redis文档中的过期事件时间部分。spring-doc.cadn.net.cn

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

然后后台任务会使用这些映射来显式地请求每个键。 通过访问键而不是删除它,我们可以确保只有在超时(TTL)过期时 Redis 才会自动删除该键。spring-doc.cadn.net.cn

我们不显式地删除键,因为在某些情况下,可能会出现竞态条件,错误地将一个未过期的键识别为已过期。 除非使用分布式锁(这会严重影响我们的性能),否则无法保证过期映射的一致性。 通过简单地访问该键,我们可以确保只有在该键的 TTL 过期时才会删除该键。

SessionDeletedEventSessionExpiredEvent

SessionDeletedEventSessionExpiredEvent 都是 SessionDestroyedEvent 的类型。spring-doc.cadn.net.cn

RedisIndexedSessionRepository 支持在删除一个 Session 时触发一个 SessionDeletedEvent,或者在 Session 过期时触发一个 SessionExpiredEvent。 这确保了与 Session 关联的资源能够正确清理。spring-doc.cadn.net.cn

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

firing SessionDeletedEventSessionExpiredEvent 是通过 SessionMessageListener 实现的,SessionMessageListener 监听 Redis 键空间事件。 为了使这一功能正常工作,需要启用通用命令和过期事件的 Redis 键空间事件。以下示例展示了如何实现这一点:spring-doc.cadn.net.cn

redis-cli config set notify-keyspace-events Egx

如果使用@EnableRedisHttpSession(enableIndexingAndEvents = true),Spring会自动管理SessionMessageListener并启用必要的Redis键空间事件。 但在安全的Redis环境中,配置命令被禁用。 这意味着Spring Session无法为您配置Redis键空间事件。 为了禁用自动配置,请添加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消息转换为SessionCreatedEventspring-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 此密钥包含所有应在时间1418772300000时被删除的会话ID。

您也可以查看每个会话的属性。以下示例展示了如何做到这一点: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 是一个由 Spring Data 的 ReactiveRedisOperations 实现的 ReactiveSessionRepository。在 Web 环境中,这通常与 WebSessionStore 一起使用。spring-doc.cadn.net.cn

实例化一个ReactiveRedisSessionRepository

以下示例展示了如何创建一个新的实例:<br>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以<redisNamespace>:前缀q开头。spring-doc.cadn.net.cn

  • flushMode: 允许指定何时将数据写入Redis。默认情况下,只有在调用save时才写入ReactiveSessionRepository。 一个值为FlushMode.IMMEDIATE的设置会在可能的情况下尽快写入Redis。spring-doc.cadn.net.cn

优化的写入

ReactiveRedisSessionRepository管理的Session个实例会跟踪已更改的属性并仅更新那些属性。 这意味着,如果一个属性只被写入一次而被读取多次,我们只需要写入该属性一次。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

The MapSessionRepository 允许将 Session 持久化到 Map 中,其中键为 Session ID,值为 Session。 您可以使用此实现作为测试或方便的机制与 ConcurrentHashMap 一起工作。 此外,您也可以将其与分布式 Map 实现一起使用。例如,可以与 Hazelcast 配合使用。spring-doc.cadn.net.cn

实例化MapSessionRepository

以下示例展示了如何创建一个新的实例:<br>spring-doc.cadn.net.cn

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

使用 Spring Session 和 Hazelcast

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

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

	./gradlew :samples:hazelcast:tomcatRun

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

它包括支持触发SessionCreatedEventSessionDeletedEventSessionExpiredEvent的示例Hazelcast MapListener实现。spring-doc.cadn.net.cn

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

	./gradlew :samples:hazelcast-spring:tomcatRun

使用ReactiveMapSessionRepository

The ReactiveMapSessionRepository 允许将 Session 保存在 Map 中,其中键是 Session ID,值是 Session。 你可以使用此实现作为测试或方便机制与 ConcurrentHashMap。 或者,你也可以使用它与分布式 Map 实现中,要求提供的 Map 必须是非阻塞的。spring-doc.cadn.net.cn

使用JdbcIndexedSessionRepository

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

实例化一个JdbcIndexedSessionRepository

以下示例展示了如何创建一个新的实例:<br>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 框架参考文档spring-doc.cadn.net.cn

使用@EnableJdbcHttpSession

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

自定义LobHandler

您可以自定义BLOB处理,通过创建一个名为springSessionLobHandler的bean并使其实现LobHandler接口。spring-doc.cadn.net.cn

自定义ConversionService

您可以通过提供一个ConversionService实例来自定义会话的默认序列化和反序列化。 在典型的Spring环境中,系统会自动拾取并使用名为conversionService的默认ConversionService bean进行序列化和反序列化。 不过,您可以通过提供一个名为springSessionConversionService的bean来覆盖默认的ConversionServicespring-doc.cadn.net.cn

存储详情

默认情况下,此实现使用SPRING_SESSION表和SPRING_SESSION_ATTRIBUTES表来存储会话。 请注意,您可以自定义表名,如已描述。在这种情况下,用于存储属性的表名称为提供的表名后缀_ATTRIBUTES。 如果需要进一步自定义,则可以使用set*Query个设置方法来自定义仓库使用的SQL查询。在这种情况下,您需要手动配置sessionRepository bean。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 数据库,您可以使用以下脚本:<br>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;

事务管理

所有在JdbcIndexedSessionRepository中的JDBC操作都是以事务方式进行的。 事务通过将传播设置为REQUIRES_NEW来执行,以便避免由于与其他现有事务的干扰而导致意外行为(例如,在已经参与只读事务的线程中运行save操作)。spring-doc.cadn.net.cn

使用HazelcastIndexedSessionRepository

HazelcastIndexedSessionRepository 是一个 SessionRepository 实现,用于在 Hazelcast 的分布式 IMap 中存储会话。
在 web 环境中,这通常与 SessionRepositoryFilter 一起使用。spring-doc.cadn.net.cn

实例化一个HazelcastIndexedSessionRepository

以下示例展示了如何创建一个新的实例:<br>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 注解提供的功能,并为您在 Hazelcast 中处理 SessionRepository。 您必须提供一个单独的 HazelcastInstance Bean 才能使配置生效。 您可以在 示例与指南(从这里开始) 中找到完整的配置示例。spring-doc.cadn.net.cn

基础定制

您可以使用以下属性来自定义@EnableHazelcastHttpSession的配置:spring-doc.cadn.net.cn

  • maxInactiveIntervalInSeconds: 在秒数内会话过期的时间。默认值为 1800 秒(30 分钟)。spring-doc.cadn.net.cn

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

会话事件

使用一个MapListener来响应条目被添加、移除或从分布式的Map中删除,会导致这些事件触发发布SessionCreatedEventSessionExpiredEventSessionDeletedEvent事件(分别对应上述操作)通过ApplicationEventPublisherspring-doc.cadn.net.cn

存储详情

会话存储在 Hazelcast 的分布式 IMap 中。 IMap 接口的方法用于 get()put() 会话。 此外,values() 方法支持 FindByIndexNameSessionRepository#findByIndexNameAndIndexValue 操作,并配合适当的 ValueExtractor(需要注册到 Hazelcast)。有关此配置的更多详细信息,请参阅 Hazelcast Spring 示例IMap 中会话的过期由 Hazelcast 支持在条目被 put()IMap 时设置生存时间(TTL)来处理。空闲时间超过生存时间的条目(会话)会自动从 IMap 中移除。spring-doc.cadn.net.cn

您无需在Hazelcast配置中为IMap设置任何类似max-idle-secondstime-to-live-seconds这样的参数。spring-doc.cadn.net.cn

注意,如果你使用 Hazelcast 的 MapStore 来持久化你的会话 IMap,从 MapStore 加载会话时存在以下限制:spring-doc.cadn.net.cn

使用CookieSerializer

一个 CookieSerializer 负责定义如何写会话cookie。
Spring Session 提供了一个默认实现,使用 DefaultCookieSerializerspring-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的路径为/(而不是默认的上下文根路径)。
3 我们自定义域名模式(正则表达式)为 ^.?\\.(\\w\\.[a-z]+)$。 这允许在不同域和应用程序之间共享会话。 如果正则表达式不匹配,则不设置域名,而是使用现有域名。 如果正则表达式匹配,则使用第一个 分组 作为域名。 这意味着对 child.example.com 的请求会将域名设置为 example.com。 然而,对 localhost:8080/192.168.1.100:8080/ 的请求不会设置 Cookie,因此在开发环境中无需任何更改即可正常工作,同时也适用于生产环境。
您应该仅匹配有效的域名字符,因为域名会在响应中被反映出来。 这样做可以防止恶意用户执行如HTTP响应拆分等攻击。

自定义CookieSerializer

您可以使用以下配置选项之一来自定义会话cookie的写入方式。spring-doc.cadn.net.cn

  • cookieName: 使用的cookie名称。 默认值: SESSION.spring-doc.cadn.net.cn

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

  • cookiePath: Cookie 的路径。 默认值:上下文根路径。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: The value for the 123. Default: Laxspring-doc.cadn.net.cn

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

自定义SessionRepository

实现自定义 SessionRepository API 应该是一项相当直接的任务。 将自定义实现与 @EnableSpringHttpSession 支持相结合,可让您重用现有的 Spring Session 配置功能和基础设施。 然而,有几个方面值得仔细考虑。spring-doc.cadn.net.cn

在HTTP请求的生命周期中,HttpSession通常会被持久化到SessionRepository两次。 第一次持久化操作是在客户端获取会话ID后确保会话可用,同时由于会在提交会话之后进行进一步修改,因此也需要在会话提交后写入。 考虑到这一点,我们一般建议SessionRepository实现跟踪更改以确保仅保存增量更新。 这对于高并发环境尤为重要,在这种环境中,多个请求可能在同一HttpSession上操作,从而导致竞态条件,一个请求覆盖另一个请求对会话属性所做的修改。 Spring Session提供的所有SessionRepository实现都使用了上述方法来持久化会话更改,并且当您自定义SessionRepository时可以作为指导。spring-doc.cadn.net.cn

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