HttpSession 集成

Spring Session 提供与 HttpSession 的透明集成。这意味着开发人员可以使用由 Spring Session 支持的实现来替换 HttpSession 实现。

为什么选择 Spring Session 和 HttpSession

我们已经提到 Spring Session 提供与 HttpSession 的透明集成,但我们从中获得了什么好处呢?

  • 集群会话:Spring Session 使支持 集群会话 变得非常容易,而无需绑定到特定于应用程序容器的解决方案。

  • RESTful API:Spring Session 允许在标头中提供会话 ID,以便与 RESTful API 协同工作。

HttpSession 与 Redis

使用 Spring Session 与 HttpSession 需要在使用 HttpSession 的任何内容之前添加一个 Servlet 过滤器。您可以选择通过以下两种方式启用此功能:

Redis 基于 Java 的配置

本节介绍如何使用基于 Java 的配置使用 Redis 来支持 HttpSession

HttpSession 示例 提供了一个工作示例,说明如何使用 Java 配置集成 Spring Session 和 HttpSession。您可以在接下来的几节中阅读集成的基本步骤,但我们建议您在与自己的应用程序集成时参考详细的 HttpSession 指南。

Spring Java 配置

添加必要的依赖项后,我们可以创建 Spring 配置。Spring 配置负责创建一个 servlet 过滤器,该过滤器用由 Spring Session 支持的实现替换 HttpSession 实现。为此,请添加以下 Spring 配置

@Configuration(proxyBeanMethods = false)
@EnableRedisHttpSession (1)
public class Config {

	@Bean
	public LettuceConnectionFactory connectionFactory() {
		return new LettuceConnectionFactory(); (2)
	}

}
1 @EnableRedisHttpSession 注解创建一个名为 springSessionRepositoryFilter 的 Spring Bean,该 Bean 实现 Filter。该过滤器负责替换 HttpSession 实现,使其由 Spring Session 支持。在本例中,Spring Session 由 Redis 支持。
2 我们创建一个 RedisConnectionFactory,将 Spring Session 连接到 Redis 服务器。我们配置连接以连接到默认端口 (6379) 上的 localhost。有关配置 Spring Data Redis 的更多信息,请参阅 参考文档

Java Servlet 容器初始化

我们的 Spring 配置 创建了一个名为 springSessionRepositoryFilter 的 Spring Bean,该 Bean 实现 FilterspringSessionRepositoryFilter bean 负责用由 Spring Session 支持的自定义实现替换 HttpSession

为了让我们的 Filter 发挥作用,Spring 需要加载我们的 Config 类。最后,我们需要确保我们的 Servlet 容器(即 Tomcat)对每个请求都使用我们的 springSessionRepositoryFilter。幸运的是,Spring Session 提供了一个名为 AbstractHttpSessionApplicationInitializer 的实用程序类,可以轻松完成这两个步骤。以下是一个示例

src/main/java/sample/Initializer.java
public class Initializer extends AbstractHttpSessionApplicationInitializer { (1)

	public Initializer() {
		super(Config.class); (2)
	}

}
我们类的名称(Initializer)并不重要。重要的是我们扩展了AbstractHttpSessionApplicationInitializer
1 第一步是扩展AbstractHttpSessionApplicationInitializer。这样做可以确保名为springSessionRepositoryFilter 的 Spring Bean 在每个请求中都注册到我们的 Servlet 容器。
2 AbstractHttpSessionApplicationInitializer 还提供了一种机制来确保 Spring 加载我们的Config

基于 XML 的 Redis 配置

本节介绍如何使用 Redis 通过基于 XML 的配置来支持HttpSession

HttpSession XML 示例中,提供了一个使用 XML 配置集成 Spring Session 和HttpSession 的工作示例。您可以在接下来的几节中阅读集成的基本步骤,但我们建议您在与自己的应用程序集成时,参考详细的 HttpSession XML 指南。

Spring XML 配置

添加必要的依赖项后,我们可以创建 Spring 配置。Spring 配置负责创建一个 servlet 过滤器,该过滤器用由 Spring Session 支持的实现替换 HttpSession 实现。为此,请添加以下 Spring 配置

src/main/webapp/WEB-INF/spring/session.xml
(1)
<context:annotation-config/>
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>

(2)
<bean class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"/>
1 我们使用<context:annotation-config/>RedisHttpSessionConfiguration 的组合,因为 Spring Session 尚未提供 XML 命名空间支持(参见gh-104)。这将创建一个名为springSessionRepositoryFilter 的 Spring Bean,它实现了Filter。该过滤器负责替换HttpSession 实现,使其由 Spring Session 支持。在本例中,Spring Session 由 Redis 支持。
2 我们创建了一个RedisConnectionFactory,它将 Spring Session 连接到 Redis 服务器。我们配置连接以连接到默认端口(6379)上的 localhost。有关配置 Spring Data Redis 的更多信息,请参见参考文档

XML Servlet 容器初始化

我们的Spring 配置创建了一个名为springSessionRepositoryFilter 的 Spring Bean,它实现了FilterspringSessionRepositoryFilter bean 负责用由 Spring Session 支持的自定义实现替换HttpSession

为了让我们的Filter 发挥作用,我们需要指示 Spring 加载我们的session.xml 配置。我们可以使用以下配置来实现这一点

src/main/webapp/WEB-INF/web.xml
<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>
		/WEB-INF/spring/session.xml
	</param-value>
</context-param>
<listener>
	<listener-class>
		org.springframework.web.context.ContextLoaderListener
	</listener-class>
</listener>

ContextLoaderListener 会读取 contextConfigLocation 并加载我们的 session.xml 配置文件。

最后,我们需要确保我们的 Servlet 容器(即 Tomcat)在每个请求中都使用我们的 springSessionRepositoryFilter。 以下代码片段完成了最后一步。

src/main/webapp/WEB-INF/web.xml
<filter>
	<filter-name>springSessionRepositoryFilter</filter-name>
	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
	<filter-name>springSessionRepositoryFilter</filter-name>
	<url-pattern>/*</url-pattern>
	<dispatcher>REQUEST</dispatcher>
	<dispatcher>ERROR</dispatcher>
</filter-mapping>

DelegatingFilterProxy 会查找名为 springSessionRepositoryFilter 的 Bean 并将其转换为 Filter。 对于 DelegatingFilterProxy 被调用的每个请求,都会调用 springSessionRepositoryFilter

使用 Mongo 的 HttpSession

通过在使用 HttpSession 的任何内容之前添加一个 Servlet 过滤器,可以启用使用 Spring Session 的 HttpSession

本节介绍如何使用 Java 基于配置的方式使用 Mongo 来支持 HttpSession

HttpSession Mongo 示例 提供了一个使用 Java 配置集成 Spring Session 和 HttpSession 的工作示例。 你可以阅读下面的基本集成步骤,但建议你在将 Spring Session 集成到自己的应用程序时,参考详细的 HttpSession 指南。

你只需要添加以下 Spring 配置。

@Configuration(proxyBeanMethods = false)
@EnableMongoHttpSession (1)
public class HttpSessionConfig {

	@Bean
	public JdkMongoSessionConverter jdkMongoSessionConverter() {
		return new JdkMongoSessionConverter(Duration.ofMinutes(30)); (2)
	}

}
1 @EnableMongoHttpSession 注解会创建一个名为 springSessionRepositoryFilter 的 Spring Bean,该 Bean 实现 Filter 接口。 该过滤器会用 MongoDB 支持的 Bean 替换默认的 HttpSession
2 将会话超时时间配置为 30 分钟。

会话序列化机制

为了能够将会话对象持久化到 MongoDB 中,我们需要提供序列化/反序列化机制。

默认情况下,Spring Session MongoDB 会使用 JdkMongoSessionConverter

但是,你可以通过在 Boot 应用程序中添加以下代码来切换到 JacksonMongoSessionConverter

@Bean
JacksonMongoSessionConverter mongoSessionConverter() {
    return new JacksonMongoSessionConverter();
}
JacksonMongoSessionConverter

该机制使用 Jackson 将会话对象序列化/反序列化为 JSON。

通过创建以下 Bean

@Bean
JacksonMongoSessionConverter mongoSessionConverter() {
    return new JacksonMongoSessionConverter();
}

…​你可以从默认的(基于 JDK 的序列化)切换到使用 Jackson。

如果你将 Spring Session 与 Spring Security 集成(通过将会话存储在 MongoDB 中),此配置将注册适当的允许列表组件,以便 Spring Security 正确工作。

如果您想提供自定义的 Jackson 模块,可以通过以下所示显式注册模块来实现。

Unresolved include directive in modules/ROOT/pages/http-session.adoc - include::example$spring-session-data-mongodb-dir/src/integration-test/java/org/springframework/session/data/mongo/integration/MongoRepositoryJacksonITest.java[]
JdkMongoSessionConverter

JdkMongoSessionConverter 使用标准的 Java 序列化将会话属性映射持久化到 MongoDB 中的二进制形式。但是,标准的会话元素,如 id、访问时间等,仍然以普通 Mongo 对象的形式写入,并且可以在没有额外工作的情况下读取和查询。如果未定义显式的 AbstractMongoSessionConverter Bean,则使用 JdkMongoSessionConverter

还有一个带 SerializerDeserializer 对象的构造函数,允许您传递自定义实现,这在您想使用非默认类加载器时尤其重要。

HttpSession 与 JDBC

您可以通过在使用 HttpSession 的任何内容之前添加一个 servlet 过滤器来使用 Spring Session 与 HttpSession。您可以选择以下任何一种方式进行操作。

基于 JDBC 的 Java 配置

本节介绍如何在使用基于 Java 的配置时使用关系数据库来支持 HttpSession

HttpSession JDBC 示例 提供了一个关于如何通过使用 Java 配置来集成 Spring Session 和 HttpSession 的工作示例。您可以阅读以下几节中关于集成的基本步骤,但我们建议您在与自己的应用程序集成时,遵循详细的 HttpSession JDBC 指南。

Spring Java 配置

添加了必要的依赖项后,我们可以创建 Spring 配置。Spring 配置负责创建一个 Servlet 过滤器,该过滤器将 HttpSession 实现替换为由 Spring Session 支持的实现。为此,请添加以下 Spring 配置。

@Configuration(proxyBeanMethods = false)
@EnableJdbcHttpSession (1)
public class Config {

	@Bean
	public EmbeddedDatabase dataSource() {
		return new EmbeddedDatabaseBuilder() (2)
			.setType(EmbeddedDatabaseType.H2)
			.addScript("org/springframework/session/jdbc/schema-h2.sql")
			.build();
	}

	@Bean
	public PlatformTransactionManager transactionManager(DataSource dataSource) {
		return new DataSourceTransactionManager(dataSource); (3)
	}

}
1 @EnableJdbcHttpSession 注解创建一个名为 springSessionRepositoryFilter 的 Spring Bean。该 bean 实现 Filter。该过滤器负责将 HttpSession 实现替换为由 Spring Session 支持的实现。在本例中,Spring Session 由关系数据库支持。
2 我们创建一个 dataSource,它将 Spring Session 连接到嵌入式 H2 数据库实例。我们配置 H2 数据库以使用 Spring Session 中包含的 SQL 脚本创建数据库表。
3 我们创建一个 transactionManager,它管理先前配置的 dataSource 的事务。

有关如何配置数据访问相关问题的更多信息,请参阅Spring 框架参考文档

Java Servlet 容器初始化

我们的Spring 配置创建了一个名为springSessionRepositoryFilter的 Spring bean,它实现了FilterspringSessionRepositoryFilter bean 负责用由 Spring Session 支持的自定义实现替换HttpSession

为了让我们的Filter发挥作用,Spring 需要加载我们的Config类。最后,我们需要确保我们的 Servlet 容器(即 Tomcat)对每个请求都使用我们的springSessionRepositoryFilter。幸运的是,Spring Session 提供了一个名为AbstractHttpSessionApplicationInitializer的实用程序类,可以轻松完成这两个步骤。以下示例展示了如何做到这一点

src/main/java/sample/Initializer.java
public class Initializer extends AbstractHttpSessionApplicationInitializer { (1)

	public Initializer() {
		super(Config.class); (2)
	}

}
我们类的名称(Initializer)并不重要。重要的是我们扩展了AbstractHttpSessionApplicationInitializer
1 第一步是扩展AbstractHttpSessionApplicationInitializer。这样做可以确保名为springSessionRepositoryFilter的 Spring bean 为每个请求注册到我们的 Servlet 容器。
2 AbstractHttpSessionApplicationInitializer 还提供了一种机制来确保 Spring 加载我们的Config

多个数据源

Spring Session 提供了@SpringSessionDataSource限定符,允许您明确声明应在JdbcIndexedSessionRepository中注入哪个DataSource bean。这在应用程序上下文中存在多个DataSource bean 的情况下特别有用。

以下示例展示了如何做到这一点

Config.java
@EnableJdbcHttpSession
public class Config {

	@Bean
	@SpringSessionDataSource (1)
	public EmbeddedDatabase firstDataSource() {
		return new EmbeddedDatabaseBuilder()
				.setType(EmbeddedDatabaseType.H2).addScript("org/springframework/session/jdbc/schema-h2.sql").build();
	}

	@Bean
	public HikariDataSource secondDataSource() {
		// ...
	}
}
1 此限定符声明 firstDataSource 将由 Spring Session 使用。

基于 XML 的 JDBC 配置

本节介绍如何在使用基于 XML 的配置时使用关系数据库来支持HttpSession

HttpSession JDBC XML 示例提供了一个关于如何使用 XML 配置集成 Spring Session 和HttpSession的工作示例。您可以在接下来的几节中阅读集成的基本步骤,但我们建议您在与自己的应用程序集成时,遵循详细的 HttpSession JDBC XML 指南。

Spring XML 配置

添加了必要的依赖项后,我们可以创建我们的 Spring 配置。Spring 配置负责创建一个 servlet 过滤器,该过滤器用由 Spring Session 支持的实现替换HttpSession实现。以下清单展示了如何添加以下 Spring 配置

src/main/webapp/WEB-INF/spring/session.xml
(1)
<context:annotation-config/>
<bean class="org.springframework.session.jdbc.config.annotation.web.http.JdbcHttpSessionConfiguration"/>

(2)
<jdbc:embedded-database id="dataSource" database-name="testdb" type="H2">
	<jdbc:script location="classpath:org/springframework/session/jdbc/schema-h2.sql"/>
</jdbc:embedded-database>

(3)
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<constructor-arg ref="dataSource"/>
</bean>
1 我们使用<context:annotation-config/>JdbcHttpSessionConfiguration的组合,因为 Spring Session 尚未提供 XML 命名空间支持(请参阅gh-104)。这将创建一个名为springSessionRepositoryFilter的 Spring bean。该 bean 实现Filter。该过滤器负责替换由 Spring Session 支持的HttpSession实现。在本例中,Spring Session 由关系数据库支持。
2 我们创建一个 dataSource,它将 Spring Session 连接到嵌入式 H2 数据库实例。我们配置 H2 数据库以使用 Spring Session 中包含的 SQL 脚本创建数据库表。
3 我们创建一个 transactionManager,它管理先前配置的 dataSource 的事务。

有关如何配置数据访问相关问题的更多信息,请参阅Spring 框架参考文档

XML Servlet 容器初始化

我们的Spring 配置创建了一个名为springSessionRepositoryFilter的 Spring bean,它实现了FilterspringSessionRepositoryFilter bean 负责用由 Spring Session 支持的自定义实现替换HttpSession

为了让我们的Filter发挥作用,我们需要指示 Spring 加载我们的session.xml配置。我们使用以下配置来实现这一点

src/main/webapp/WEB-INF/web.xml
<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>
		/WEB-INF/spring/session.xml
	</param-value>
</context-param>
<listener>
	<listener-class>
		org.springframework.web.context.ContextLoaderListener
	</listener-class>
</listener>

ContextLoaderListener读取contextConfigLocation并获取我们的 session.xml 配置。

最后,我们需要确保我们的 Servlet 容器(即 Tomcat)在每个请求中都使用我们的 springSessionRepositoryFilter。 以下代码片段完成了最后一步。

src/main/webapp/WEB-INF/web.xml
<filter>
	<filter-name>springSessionRepositoryFilter</filter-name>
	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
	<filter-name>springSessionRepositoryFilter</filter-name>
	<url-pattern>/*</url-pattern>
	<dispatcher>REQUEST</dispatcher>
	<dispatcher>ERROR</dispatcher>
</filter-mapping>

DelegatingFilterProxy查找名为springSessionRepositoryFilter的 bean 并将其转换为Filter。对于调用DelegatingFilterProxy的每个请求,都会调用springSessionRepositoryFilter

基于 JDBC Spring Boot 的配置

本节介绍如何在使用 Spring Boot 时使用关系型数据库来支持HttpSession

HttpSession JDBC Spring Boot 示例提供了一个关于如何使用 Spring Boot 集成 Spring Session 和HttpSession的工作示例。您可以在接下来的几节中阅读集成基本步骤,但我们建议您在与自己的应用程序集成时,参考详细的 HttpSession JDBC Spring Boot 指南。

Spring Boot 配置

添加必要的依赖项后,我们可以创建我们的 Spring Boot 配置。由于一流的自动配置支持,只需添加依赖项,Spring Boot 就会为我们设置由关系型数据库支持的 Spring Session。

如果类路径中存在单个 Spring Session 模块,Spring Boot 会自动使用该存储实现。如果您有多个实现,则必须选择要用于存储会话的 StoreType,如上所示。

在幕后,Spring Boot 应用了等效于手动添加@EnableJdbcHttpSession注释的配置。这将创建一个名为springSessionRepositoryFilter的 Spring bean。该 bean 实现Filter。该过滤器负责替换由 Spring Session 支持的HttpSession实现。

您可以通过使用application.properties进行进一步自定义。以下清单展示了如何操作。

src/main/resources/application.properties
server.servlet.session.timeout= # Session timeout. If a duration suffix is not specified, seconds are used.
spring.session.jdbc.initialize-schema=embedded # Database schema initialization mode.
spring.session.jdbc.schema=classpath:org/springframework/session/jdbc/schema-@@platform@@.sql # Path to the SQL file to use to initialize the database schema.
spring.session.jdbc.table-name=SPRING_SESSION # Name of the database table used to store sessions.

有关更多信息,请参阅 Spring Boot 文档的 Spring Session 部分。

配置DataSource

Spring Boot 自动创建一个DataSource,将 Spring Session 连接到嵌入式 H2 数据库实例。在生产环境中,您需要更新配置以指向您的关系数据库。例如,您可以在您的 application.properties 中包含以下内容。

src/main/resources/application.properties
spring.datasource.url= # JDBC URL of the database.
spring.datasource.username= # Login username of the database.
spring.datasource.password= # Login password of the database.

有关更多信息,请参阅 Spring Boot 文档的 配置 DataSource 部分。

Servlet 容器初始化

我们的 Spring Boot 配置 创建了一个名为springSessionRepositoryFilter的 Spring bean,它实现了FilterspringSessionRepositoryFilter bean 负责用 Spring Session 支持的自定义实现替换HttpSession

为了让我们的Filter发挥作用,Spring 需要加载我们的Config类。最后,我们需要确保我们的 Servlet 容器(即 Tomcat)对每个请求都使用我们的springSessionRepositoryFilter。幸运的是,Spring Boot 为我们处理了这两个步骤。

使用 Hazelcast 的 HttpSession

通过在使用 HttpSession 的任何内容之前添加一个 Servlet 过滤器,可以启用使用 Spring Session 的 HttpSession

本节介绍如何使用基于 Java 的配置将 Hazelcast 用作HttpSession的后端。

Hazelcast Spring 示例 提供了一个使用 Java 配置集成 Spring Session 和HttpSession的工作示例。您可以在接下来的几节中阅读集成的基本步骤,但我们建议您在与自己的应用程序集成时参考详细的 Hazelcast Spring 指南。

Spring 配置

添加必要的依赖项后,我们可以创建 Spring 配置。Spring 配置负责创建一个 servlet 过滤器,该过滤器用由 Spring Session 支持的实现替换 HttpSession 实现。为此,请添加以下 Spring 配置

@EnableHazelcastHttpSession (1)
@Configuration
public class HazelcastHttpSessionConfig {

	@Bean
	public HazelcastInstance hazelcastInstance() {
		Config config = new Config();
		AttributeConfig attributeConfig = new AttributeConfig()
			.setName(HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
			.setExtractorClassName(PrincipalNameExtractor.class.getName());
		config.getMapConfig(HazelcastIndexedSessionRepository.DEFAULT_SESSION_MAP_NAME) (2)
			.addAttributeConfig(attributeConfig)
			.addIndexConfig(
					new IndexConfig(IndexType.HASH, HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE));
		SerializerConfig serializerConfig = new SerializerConfig();
		serializerConfig.setImplementation(new HazelcastSessionSerializer()).setTypeClass(MapSession.class);
		config.getSerializationConfig().addSerializerConfig(serializerConfig); (3)
		return Hazelcast.newHazelcastInstance(config); (4)
	}

}
1 @EnableHazelcastHttpSession注解创建一个名为springSessionRepositoryFilter的 Spring bean,它实现了Filter。该过滤器负责替换HttpSession实现,使其由 Spring Session 支持。在本例中,Spring Session 由 Hazelcast 支持。
2 为了支持通过主体名称索引检索会话,需要注册一个合适的ValueExtractor。Spring Session为此提供了PrincipalNameExtractor
3 为了有效地序列化MapSession对象,需要注册HazelcastSessionSerializer。如果没有设置,Hazelcast将使用原生Java序列化来序列化会话。
4 我们创建一个HazelcastInstance,它将Spring Session连接到Hazelcast。默认情况下,应用程序启动并连接到Hazelcast的嵌入式实例。有关配置Hazelcast的更多信息,请参阅参考文档
如果首选HazelcastSessionSerializer,则需要在所有Hazelcast集群成员启动之前为它们配置。在Hazelcast集群中,所有成员都应使用相同的序列化方法来处理会话。此外,如果使用Hazelcast客户端/服务器拓扑,则成员和客户端都必须使用相同的序列化方法。序列化器可以通过ClientConfig使用与成员相同的SerializerConfiguration进行注册。

Servlet容器初始化

我们的Spring配置创建了一个名为springSessionRepositoryFilter的Spring bean,它实现了FilterspringSessionRepositoryFilter bean负责用由Spring Session支持的自定义实现替换HttpSession

为了使我们的Filter发挥作用,Spring需要加载我们的SessionConfig类。由于我们的应用程序已经通过使用我们的SecurityInitializer类加载Spring配置,因此我们可以将我们的SessionConfig类添加到其中。以下清单展示了如何做到这一点

src/main/java/sample/SecurityInitializer.java
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {

	public SecurityInitializer() {
		super(SecurityConfig.class, SessionConfig.class);
	}

}

最后,我们需要确保我们的Servlet容器(即Tomcat)对每个请求都使用我们的springSessionRepositoryFilter。Spring Session的springSessionRepositoryFilter在Spring Security的springSecurityFilterChain之前调用这一点非常重要。这样做可以确保Spring Security使用的HttpSession由Spring Session支持。幸运的是,Spring Session提供了一个名为AbstractHttpSessionApplicationInitializer的实用程序类,这使得这样做变得容易。以下示例展示了如何做到这一点

src/main/java/sample/Initializer.java
public class Initializer extends AbstractHttpSessionApplicationInitializer {

}
我们类的名称(Initializer)并不重要。重要的是我们扩展了AbstractHttpSessionApplicationInitializer

通过扩展AbstractHttpSessionApplicationInitializer,我们确保名为springSessionRepositoryFilter的Spring Bean在Spring Security的springSecurityFilterChain之前为每个请求注册到我们的servlet容器中。

HttpSession集成工作原理

幸运的是,HttpSessionHttpServletRequest(获取HttpSession的API)都是接口。这意味着我们可以为这两个API提供我们自己的实现。

本节描述了Spring Session如何与HttpSession透明集成。我们提供此内容是为了让你了解幕后发生的事情。此功能已经集成,你不需要自己实现此逻辑。

首先,我们创建一个自定义的 HttpServletRequest,它返回 HttpSession 的自定义实现。它看起来像这样

public class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper {

	public SessionRepositoryRequestWrapper(HttpServletRequest original) {
		super(original);
	}

	public HttpSession getSession() {
		return getSession(true);
	}

	public HttpSession getSession(boolean createNew) {
		// create an HttpSession implementation from Spring Session
	}

	// ... other methods delegate to the original HttpServletRequest ...
}

任何返回 HttpSession 的方法都被覆盖。所有其他方法都由 HttpServletRequestWrapper 实现,并委托给原始的 HttpServletRequest 实现。

我们使用名为 SessionRepositoryFilter 的 servlet Filter 替换 HttpServletRequest 实现。以下伪代码展示了它的工作原理

public class SessionRepositoryFilter implements Filter {

	public doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		SessionRepositoryRequestWrapper customRequest =
			new SessionRepositoryRequestWrapper(httpRequest);

		chain.doFilter(customRequest, response, chain);
	}

	// ...
}

通过将自定义 HttpServletRequest 实现传递到 FilterChain 中,我们确保在我们的 Filter 之后调用的任何内容都使用自定义的 HttpSession 实现。这突出了为什么 Spring Session 的 SessionRepositoryFilter 必须放在任何与 HttpSession 交互的内容之前。

HttpSession 和 RESTful API

Spring Session 可以通过在标头中提供会话来与 RESTful API 协同工作。

REST 示例 提供了一个工作示例,说明如何在 REST 应用程序中使用 Spring Session 来支持使用标头进行身份验证。您可以按照接下来的几节中描述的集成基本步骤进行操作,但我们建议您在与自己的应用程序集成时参考详细的 REST 指南。

Spring 配置

添加必要的依赖项后,我们可以创建 Spring 配置。Spring 配置负责创建一个 servlet 过滤器,该过滤器用由 Spring Session 支持的实现替换 HttpSession 实现。为此,请添加以下 Spring 配置

@Configuration
@EnableRedisHttpSession (1)
public class HttpSessionConfig {

	@Bean
	public LettuceConnectionFactory connectionFactory() {
		return new LettuceConnectionFactory(); (2)
	}

	@Bean
	public HttpSessionIdResolver httpSessionIdResolver() {
		return HeaderHttpSessionIdResolver.xAuthToken(); (3)
	}

}
1 @EnableRedisHttpSession 注解创建了一个名为 springSessionRepositoryFilter 的 Spring bean,它实现了 Filter。该过滤器负责替换 HttpSession 实现,使其由 Spring Session 支持。在本例中,Spring Session 由 Redis 支持。
2 我们创建一个 RedisConnectionFactory,将 Spring Session 连接到 Redis 服务器。我们配置连接以连接到默认端口 (6379) 上的 localhost。有关配置 Spring Data Redis 的更多信息,请参阅 参考文档
3 我们自定义 Spring Session 的 HttpSession 集成,以使用 HTTP 标头来传递当前会话信息,而不是使用 cookie。

Servlet 容器初始化

我们的 Spring 配置 创建了一个名为 springSessionRepositoryFilter 的 Spring Bean,它实现了 FilterspringSessionRepositoryFilter bean 负责用由 Spring Session 支持的自定义实现替换 HttpSession

为了让我们的 Filter 发挥作用,Spring 需要加载我们的 Config 类。我们在 Spring MvcInitializer 中提供配置,如下例所示

src/main/java/sample/mvc/MvcInitializer.java
@Override
protected Class<?>[] getRootConfigClasses() {
	return new Class[] { SecurityConfig.class, HttpSessionConfig.class };
}

最后,我们需要确保我们的 Servlet 容器(即 Tomcat)为每个请求使用我们的 springSessionRepositoryFilter。幸运的是,Spring Session 提供了一个名为 AbstractHttpSessionApplicationInitializer 的实用程序类,这使得这样做变得很容易。为此,请使用默认构造函数扩展该类,如下例所示

src/main/java/sample/Initializer.java
public class Initializer extends AbstractHttpSessionApplicationInitializer {

}
我们类的名称(Initializer)并不重要。重要的是我们扩展了AbstractHttpSessionApplicationInitializer

使用 HttpSessionListener

Spring Session 通过将 SessionDestroyedEventSessionCreatedEvent 转换为 HttpSessionEvent 来支持 HttpSessionListener,方法是声明 SessionEventHttpSessionListenerAdapter。要使用此支持,您需要

  • 确保您的 SessionRepository 实现支持并配置为触发 SessionDestroyedEventSessionCreatedEvent

  • SessionEventHttpSessionListenerAdapter 配置为 Spring bean。

  • 将每个 HttpSessionListener 注入到 SessionEventHttpSessionListenerAdapter

如果您使用 Redis 支持并将 enableIndexingAndEvents 设置为 true@EnableRedisHttpSession(enableIndexingAndEvents = true),您只需将每个 HttpSessionListener 注册为 bean 即可。例如,假设您想支持 Spring Security 的并发控制,并且需要使用 HttpSessionEventPublisher。在这种情况下,您可以将 HttpSessionEventPublisher 添加为 bean。在 Java 配置中,这可能看起来像这样

@Configuration
@EnableRedisHttpSession
public class RedisHttpSessionConfig {

	@Bean
	public HttpSessionEventPublisher httpSessionEventPublisher() {
		return new HttpSessionEventPublisher();
	}

	// ...

}

在 XML 配置中,这可能看起来像这样

<bean class="org.springframework.security.web.session.HttpSessionEventPublisher"/>