HttpSession 集成

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

为什么选择 Spring Session 和 HttpSession

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

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

  • RESTful API:Spring Session 允许在标头中提供会话 ID 与 RESTful API 配合使用

使用 Redis 的 HttpSession

通过在使用 HttpSession 之前添加 Servlet 过滤器,可以启用使用 Spring Session 与 HttpSession。您可以选择通过使用以下任一方式启用此功能:

Redis 基于 Java 的配置

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

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

Spring Java 配置

添加必需的依赖项后,我们可以创建 Spring 配置。Spring 配置负责创建一个 Servlet 过滤器,该过滤器将 HttpSession 实现替换为 Spring Session 支持的实现。为此,请添加以下 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 服务器。我们配置连接以连接到 localhost 上的默认端口 (6379)。有关配置 Spring Data Redis 的更多信息,请参阅 参考文档

Java Servlet 容器初始化

我们的 Spring 配置 创建了一个名为 springSessionRepositoryFilter 的 Spring Bean,该 Bean 实现 Filter 接口。springSessionRepositoryFilter Bean 负责将 HttpSession 替换为 Spring Session 支持的自定义实现。

为了使我们的 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

Redis 基于 XML 的配置

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

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

Spring XML 配置

添加必需的依赖项后,我们可以创建 Spring 配置。Spring 配置负责创建一个 Servlet 过滤器,该过滤器将 HttpSession 实现替换为 Spring Session 支持的实现。为此,请添加以下 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,该 Bean 实现 Filter 接口。过滤器负责将 HttpSession 实现替换为 Spring Session 支持的实现。在本例中,Spring Session 由 Redis 支持。
2 我们创建了一个 RedisConnectionFactory,它将 Spring Session 连接到 Redis 服务器。我们配置连接以连接到 localhost 上的默认端口 (6379)。有关配置 Spring Data Redis 的更多信息,请参阅 参考文档

XML Servlet 容器初始化

我们的 Spring 配置 创建了一个名为 springSessionRepositoryFilter 的 Spring Bean,该 Bean 实现 Filter 接口。springSessionRepositoryFilter Bean 负责将 HttpSession 替换为 Spring Session 支持的自定义实现。

为了使我们的 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 的工作示例。您可以在下面阅读集成的基本步骤,但我们建议您在与自己的应用程序集成时,参考详细的 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 接口。此过滤器用于替换默认的 HttpSession,并使用 MongoDB 支持的 Bean。
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 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 对象,允许您传递自定义实现,这在您想要使用非默认类加载器时尤其重要。

使用 JDBC 的 HttpSession

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

基于 JDBC Java 的配置

本节介绍如何在使用基于 Java 的配置时使用关系数据库作为 HttpSession 的后端。

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

Spring Java 配置

添加必要的依赖项后,我们可以创建 Spring 配置。Spring 配置负责创建 Servlet 过滤器,该过滤器用 Spring Session 支持的实现替换 HttpSession 实现。为此,请添加以下 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,该 Bean 实现 Filter 接口。springSessionRepositoryFilter 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 限定符,允许您显式声明哪个 DataSource Bean 应该注入 JdbcIndexedSessionRepository 中。这在应用程序上下文中存在多个 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 使用。

基于 JDBC XML 的配置

本节介绍如何在使用基于 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 接口。过滤器负责将 HttpSession 实现替换为由 Spring Session 支持的实现。在本例中,Spring Session 由关系数据库支持。
2 我们创建了一个 dataSource,它将 Spring Session 连接到 H2 数据库的嵌入实例。我们配置 H2 数据库以使用 Spring Session 中包含的 SQL 脚本创建数据库表。
3 我们创建了一个 transactionManager,用于管理先前配置的 dataSource 的事务。

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

XML Servlet 容器初始化

我们的 Spring 配置 创建了一个名为 springSessionRepositoryFilter 的 Spring Bean,该 Bean 实现 Filter 接口。springSessionRepositoryFilter 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 接口。过滤器负责将 HttpSession 实现替换为由 Spring Session 支持的实现。

您可以使用 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 文档中的 配置数据源 部分。

Servlet 容器初始化

我们的 Spring Boot 配置 创建了一个名为 springSessionRepositoryFilter 的 Spring Bean,该 Bean 实现 Filter 接口。springSessionRepositoryFilter 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 过滤器,该过滤器将 HttpSession 实现替换为 Spring Session 支持的实现。为此,请添加以下 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,该 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 过滤器,该过滤器将 HttpSession 实现替换为 Spring Session 支持的实现。为此,请添加以下 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 服务器。我们配置连接以连接到 localhost 上的默认端口 (6379)。有关配置 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(通过声明SessionEventHttpSessionListenerAdapter)来支持HttpSessionListener。要使用此支持,您需要

  • 确保您的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"/>