# `HttpSession`积分
Spring Session 提供了与`HttpSession`的透明集成。这意味着开发人员可以使用 Spring Session 支持的实现来切换`HttpSession`实现。
## 为什么 Spring session 和`HttpSession`?
我们已经提到, Spring Session 提供了与`HttpSession`的透明集成,但是我们从中得到了什么好处呢?
* **群集会话**: Spring Session 使得在不绑定到特定于应用程序容器的解决方案的情况下支持[群集会话](#httpsession-redis)变得非常简单。
* **RESTful API**: Spring session 让在 header 中提供会话 ID 可以与[RESTful API](#httpsession-rest)一起工作
## `HttpSession`with redis
使用 Spring session with`HttpSession`是通过在使用`HttpSession`之前添加一个 Servlet 过滤器来实现的。你可以通过以下两种方式选择启用此功能:
* [基于 Java 的配置](#httpsession-redis-jc)
* [基于 XML 的配置](#httpsession-redis-xml)
### Redis 基于 Java 的配置
这一节描述了如何使用基于 Java 的配置使用 Redis 来支持`HttpSession`。
| |[HttpSession 示例](samples.html#samples)提供了如何使用 Java 配置集成 Spring Session 和`HttpSession`的工作示例,
你可以在接下来的几节中阅读集成的基本步骤,但是,我们鼓励你在与自己的应用程序集成时遵循详细的 HttpSession 指南。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### Spring Java 配置
在添加了所需的依赖关系之后,我们就可以创建我们的 Spring 配置了。 Spring 配置负责创建一个 Servlet 过滤器,该过滤器将`HttpSession`实现替换为由 Spring Session 支持的实现。为此,添加以下 Spring 配置:
```
@EnableRedisHttpSession (1)
public class Config {
@Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory(); (2)
}
}
```
|**1**|`@EnableRedisHttpSession`注释创建了一个名为`springSessionRepositoryFilter`的 Spring Bean,它实现了`Filter`。
过滤器负责替换要由 Spring Session 支持的`HttpSession`实现。
在此实例中, Spring Session 由 Redis 支持。|
|-----|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|我们创建一个`RedisConnectionFactory`将 Spring Session 连接到 Redis 服务器。
我们将连接配置为在默认端口(6379)上连接到 localhost。
有关配置 Spring 数据 Redis 的更多信息,请参见[参考文献](https://docs.spring.io/spring-data/data-redis/docs/2.6.2/reference/html/)。|
#### Java Servlet 容器初始化
我们的[Spring Configuration](#httpsession-spring-configuration)创建了一个名为`springSessionRepositoryFilter`的 Spring 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`。
这样做可以确保 Spring Bean 以`springSessionRepositoryFilter`的名称在我们的 Servlet 容器中为每个请求注册。|
|-----|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|`AbstractHttpSessionApplicationInitializer`还提供了一种机制,以确保 Spring 加载我们的`Config`。|
### 基于 XML 的 Redis 配置
本节介绍如何使用基于 XML 的配置使用 Redis 来支持`HttpSession`。
| |[HttpSession XML 示例](samples.html#samples)提供了如何使用 XML 配置集成 Spring Session 和`HttpSession`的工作示例,
你可以在接下来的几个部分中阅读集成的基本步骤,但是,我们鼓励你在与自己的应用程序集成时遵循详细的 HttpSession XML 指南。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### Spring xml 配置
在添加了所需的依赖关系之后,我们就可以创建我们的 Spring 配置了。 Spring 配置负责创建一个 Servlet 过滤器,该过滤器将`HttpSession`实现替换为由 Spring Session 支持的实现。为此,添加以下 Spring 配置:
SRC/main/webapp/WEB-INF/ Spring/session.xml
```
(1)
(2)
```
|**1**|我们使用``和`RedisHttpSessionConfiguration`的组合,因为 Spring Session 尚未提供 XML 名称空间支持(参见[gh-104](https://github.com/spring-projects/spring-session/issues/104))。
这将创建一个名称为`springSessionRepositoryFilter`的 Spring Bean 实现`Filter`。
过滤器负责替换要由 Spring Session 支持的`HttpSession`实现。
在此实例中, Spring Session 由 Redis 支持。|
|-----|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|我们创建一个`RedisConnectionFactory`将 Spring Session 连接到 Redis 服务器。
我们将连接配置为在默认端口(6379)
上连接到 localhost。有关配置 Spring 数据 Redis 的更多信息,请参见[参考文献](https://docs.spring.io/spring-data/data-redis/docs/2.6.2/reference/html/)。|
#### xml Servlet 容器初始化
我们的[Spring Configuration](#httpsession-xml-spring-configuration)创建了一个名为`springSessionRepositoryFilter`的 Spring Bean,它实现了`Filter`。`springSessionRepositoryFilter` Bean 负责用 Spring Session 支持的自定义实现替换`HttpSession`。
为了使我们的`Filter`发挥其魔力,我们需要指示 Spring 加载我们的`session.xml`配置。我们可以通过以下配置来实现这一点:
SRC/main/webapp/WEB-INF/web.xml
```
contextConfigLocation
/WEB-INF/spring/session.xml
org.springframework.web.context.ContextLoaderListener
```
[`ContextLoaderListener`](https://DOCS. Spring.io/ Spring/DOCS/5.3.16/ Spring-framework-reference/core.html#context-create)读取 contextconfiglocation 并获取 session.xml 配置。
最后,我们需要确保我们的 Servlet 容器(即 Tomcat)为每个请求使用我们的`springSessionRepositoryFilter`。下面的代码片段为我们执行了最后一步:
SRC/main/webapp/WEB-INF/web.xml
```
springSessionRepositoryFilter
org.springframework.web.filter.DelegatingFilterProxy
springSessionRepositoryFilter
/*
REQUEST
ERROR
```
[`DelegatingFilterProxy`](https://DOCS. Spring.io/ Spring-framework/DOCS/5.3.16/javadoc-api/org/springframework/web/filter/delegatingfilterproxy.html)以`springSessionRepositoryFilter`的名称查找 Bean 并将其强制转换为`Filter`。对于每一个调用`DelegatingFilterProxy`的请求,都会调用`springSessionRepositoryFilter`。
### HttpSession with Mongo
使用 Spring 与`HttpSession`会话是通过在使用`HttpSession`的任何之前添加一个 Servlet 过滤器来实现的。
本节介绍如何使用基于 Java 的配置使用 Mongo 返回`HttpSession`。
| |[HttpSession Mongo 示例](#samples)提供了一个工作示例,说明如何使用 Java 配置集成 Spring Session 和`HttpSession`。
你可以阅读下面的集成基本步骤,但鼓励你在与自己的应用程序集成时遵循详细的 HttpSession 指南。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
你所要做的就是添加以下 Spring 配置:
```
@EnableMongoHttpSession (1)
public class HttpSessionConfig {
@Bean
public JdkMongoSessionConverter jdkMongoSessionConverter() {
return new JdkMongoSessionConverter(Duration.ofMinutes(30)); (2)
}
}
```
|**1**|`@EnableMongoHttpSession`注释创建了一个名为`springSessionRepositoryFilter`的 Spring Bean,它实现了 filter。
这个过滤器用 MongoDB 支持的 Bean 替换了默认的`HttpSession`。|
|-----|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|将会话超时时间配置为 30 分钟。|
#### 会话序列化机制
为了能够在 MongoDB 中持久化会话对象,我们需要提供序列化/反序列化机制。
默认情况下, Spring Session MongoDB 将使用`JdkMongoSessionConverter`。
但是,只需在启动应用程序中添加以下代码,就可以切换到`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 模块,你可以通过显式地注册如下所示的模块来实现这一点:
```
@Configuration
@EnableMongoHttpSession
static class Config extends BaseConfig {
@Bean
AbstractMongoSessionConverter mongoSessionConverter() {
return new JacksonMongoSessionConverter(Collections.singletonList(new GeoModule()));
}
}
```
##### JDKMongosessionConverter
`JdkMongoSessionConverter`使用标准的 Java 序列化来持久化以二进制形式映射到 MongoDB 的会话属性。但是,诸如 ID、访问时间等标准会话元素仍然被写成一个普通的 Mongo 对象,并且可以在不需要额外的努力的情况下进行读取和查询。如果没有明确的`JdkMongoSessionConverter` Bean 定义,则使用`AbstractMongoSessionConverter`。
还有一个构造函数接受`Serializer`和`Deserializer`对象,允许你传递自定义的实现,这在你想要使用非默认的类加载器时尤其重要。
## `HttpSession`with JDBC
通过在使用`HttpSession`的任何内容之前添加一个 Servlet 过滤器,可以使用与`HttpSession`的 Spring Session 。你可以选择以下任何一种方式:
* [基于 Java 的配置](#httpsession-jdbc-jc)
* [基于 XML 的配置](#httpsession-jdbc-xml)
* [Spring Boot-based Configuration](#httpsession-jdbc-boot)
### 基于 Java 的 JDBC 配置
本节描述了在使用基于 Java 的配置时如何使用关系数据库来备份`HttpSession`。
| |[HttpSession JDBC 示例](samples.html#samples)提供了如何通过使用 Java 配置来集成 Spring Session 和`HttpSession`的工作示例,
你可以在接下来的几个部分中阅读集成的基本步骤,但我们鼓励你在与自己的应用程序集成时遵循详细的 HttpSession JDBC 指南。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### Spring Java 配置
在添加了所需的依赖关系之后,我们就可以创建我们的 Spring 配置了。 Spring 配置负责创建一个 Servlet 过滤器,该过滤器用 Spring Session 支持的实现替换`HttpSession`实现。为此,添加以下 Spring 配置:
```
@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`。
过滤器负责替换要由 Spring Session 支持的`HttpSession`实现。
在此实例中, Spring Session 是由关系数据库支持的。|
|-----|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|我们创建了一个`dataSource`,它将 Spring Session 连接到 H2 数据库的嵌入式实例。
我们通过使用 Spring Session 中包含的 SQL 脚本来配置 H2 数据库来创建数据库表。|
|**3**|我们创建一个`transactionManager`,它管理先前配置的`dataSource`的事务。|
有关如何配置与数据访问相关的问题的更多信息,请参见[Spring Framework Reference Documentation](https://docs.spring.io/spring/docs/5.3.16/spring-framework-reference/data-access.html)。
#### Java Servlet 容器初始化
我们的[Spring Configuration](#httpsession-jdbc-spring-configuration)创建了一个名为`springSessionRepositoryFilter`的 Spring 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)
}
}
```
| |类的名称(初始化器)并不重要。
重要的是我们扩展`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**|此限定符声明 Spring Session 将使用 FirstDataSource。|
|-----|-----------------------------------------------------------------------------|
### 基于 XML 的 JDBC 配置
本节描述了在使用基于 XML 的配置时如何使用关系数据库来备份`HttpSession`。
| |[HttpSession JDBC XML 示例](samples.html#samples)提供了如何通过使用 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)
(2)
(3)
```
|**1**|我们使用``的组合和`JdbcHttpSessionConfiguration`因为 Spring session 尚未提供 XML 名称空间支持(参见[gh-104](https://github.com/spring-projects/spring-session/issues/104))。
这将创建一个名为`springSessionRepositoryFilter`的 Spring Bean。
Bean 实现`Filter`。
过滤器负责替换要由 Spring session 支持的`HttpSession`实现。
在此实例中, Spring Session 由关系数据库支持。|
|-----|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|我们创建一个`dataSource`,它将 Spring Session 连接到 H2 数据库的嵌入式实例。
我们通过使用 Spring Session 中包含的 SQL 脚本配置 H2 数据库来创建数据库表。|
|**3**|我们创建一个`transactionManager`,它管理先前配置的`dataSource`的事务。|
有关如何配置与数据访问相关的问题的更多信息,请参见[Spring Framework Reference Documentation](https://docs.spring.io/spring/docs/5.3.16/spring-framework-reference/data-access.html)。
#### xml Servlet 容器初始化
我们的[Spring Configuration](#httpsession-jdbc-xml-spring-configuration)创建了一个名为`springSessionRepositoryFilter`的 Spring Bean,它实现了`Filter`。`springSessionRepositoryFilter` Bean 负责用 Spring Session 支持的自定义实现替换`HttpSession`。
为了使我们的`Filter`发挥其魔力,我们需要指示 Spring 加载我们的`session.xml`配置。我们的配置如下:
SRC/main/webapp/WEB-INF/web.xml
```
contextConfigLocation
/WEB-INF/spring/session.xml
org.springframework.web.context.ContextLoaderListener
```
[`ContextLoaderListener`](https://DOCS. Spring.io/ Spring/DOCS/5.3.16/ Spring-framework-reference/core.html#context-create)读取`contextConfigLocation`并获取 session.xml 配置。
最后,我们需要确保我们的 Servlet 容器(即 Tomcat)为每个请求使用我们的`springSessionRepositoryFilter`。下面的代码片段为我们执行了最后一步:
SRC/main/webapp/WEB-INF/web.xml
```
springSessionRepositoryFilter
org.springframework.web.filter.DelegatingFilterProxy
springSessionRepositoryFilter
/*
REQUEST
ERROR
```
[`DelegatingFilterProxy`](https://DOCS. Spring.io/ Spring-framework/DOCS/5.3.16/javadoc-api/org/springframework/web/filter/delegatingfilterproxy.html)查找一个名为`springSessionRepositoryFilter`的 Bean 并将其强制转换为`Filter`。对于每个调用`DelegatingFilterProxy`的请求,都调用`springSessionRepositoryFilter`。
### JDBC Spring 基于引导的配置
本节描述了在使用 Spring 引导时如何使用关系数据库来备份`HttpSession`。
| |[ HttpSession JDBC Spring Boot Sample](samples.html#samples)提供了如何使用 Spring boot 集成 Spring Session 和`HttpSession`的工作示例,
你可以在接下来的几个部分中阅读集成的基本步骤,但是,我们鼓励你在与自己的应用程序集成时遵循详细的 HttpSession JDBC Spring 引导指南。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### Spring 引导配置
在添加了所需的依赖项之后,我们可以创建我们的 Spring 引导配置。多亏了一流的自动配置支持,在关系数据库支持下设置 Spring Session 就像向`application.properties`添加一个配置属性一样简单。下面的清单展示了如何做到这一点:
SRC/主/资源/应用程序.properties
```
spring.session.store-type=jdbc # Session store type.
```
如果在 Classpath 上存在单个 Spring Session 模块,则 Spring 引导自动使用该存储实现。如果有多个实现,则必须选择要用来存储会话的 StoreType,如上面所示。
Spring Boot 应用的配置相当于手动添加`@EnableJdbcHttpSession`注释。这将创建一个名为`springSessionRepositoryFilter`的 Spring Bean。 Bean 实现`Filter`。过滤器负责替换要由 Spring Session 支持的`HttpSession`实现。
你可以使用`application.properties`来进一步自定义。下面的清单展示了如何做到这一点:
SRC/主/资源/应用程序.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/[email protected]@[email protected]@.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 引导文档的[Spring Session](https://docs.spring.io/spring-boot/docs/2.5.6/reference/htmlsingle/#boot-features-session)部分。
#### 配置`DataSource`
Spring 启动会自动创建`DataSource`,该会话将 Spring Session 连接到 H2 数据库的嵌入式实例。在生产环境中,你需要更新配置以指向关系数据库。例如,你可以在应用程序中包含以下内容:
SRC/主/资源/应用程序.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 引导文档的[配置数据源](https://docs.spring.io/spring-boot/docs/2.5.6/reference/htmlsingle/#boot-features-configure-datasource)部分。
#### Servlet 容器初始化
我们的[Spring Boot Configuration](#httpsession-jdbc-boot-spring-configuration)创建了一个名为`springSessionRepositoryFilter`的 Spring Bean,它实现了`Filter`。`springSessionRepositoryFilter` Bean 负责用 Spring Session 支持的自定义实现替换`HttpSession`。
为了让我们的`Filter`发挥其魔力, Spring 需要加载我们的`Config`类。最后,我们需要确保我们的 Servlet 容器(即 Tomcat)为每个请求使用我们的`springSessionRepositoryFilter`。幸运的是,Boot 为我们解决了这两个步骤。
## HttpSession with Hazelcast
使用 Spring 与`HttpSession`会话是通过在任何使用`HttpSession`的之前添加一个 Servlet 过滤器来实现的。
本节介绍如何使用基于 Java 的配置使用 HazelCast 来支持`HttpSession`。
| |[ Hazelcast Spring Sample](samples.html#samples)提供了如何通过使用 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();
MapAttributeConfig attributeConfig = new MapAttributeConfig()
.setName(HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
.setExtractor(PrincipalNameExtractor.class.getName());
config.getMapConfig(HazelcastIndexedSessionRepository.DEFAULT_SESSION_MAP_NAME) (2)
.addMapAttributeConfig(attributeConfig).addMapIndexConfig(
new MapIndexConfig(HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE, false));
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`。
过滤器负责替换要由 Spring Session 支持的`HttpSession`实现。
在此实例中, Spring Session 由 Hazelcast 支持。|
|-----|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|为了支持通过主名索引检索会话,需要注册一个适当的`ValueExtractor`。
Spring Session 为此提供了`PrincipalNameExtractor`。|
|**3**|为了有效地序列化`MapSession`对象,需要注册`HazelcastSessionSerializer`。如果未设置
,HazelCast 将使用本机 Java 序列化来序列化会话。|
|**4**|我们创建一个`HazelcastInstance`将 Spring Session 连接到 Hazelcast。
默认情况下,应用程序启动并连接到 Hazelcast 的嵌入式实例。
有关配置 Hazelcast 的更多信息,请参见[参考文献](https://docs.hazelcast.org/docs/3.12.12/manual/html-single/index.html#hazelcast-configuration)。|
| |如果`HazelcastSessionSerializer`是首选的,则需要在所有 Hazelcast 集群成员启动之前对其进行配置。
在 Hazelcast 集群中,所有成员都应该对会话使用相同的序列化方法。此外,如果使用了 HazelCast 客户机/服务器拓扑
,那么成员和客户机都必须使用相同的序列化方法。序列化器可以通过`ClientConfig`与`SerializerConfiguration`相同的成员来注册。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
### Servlet 容器初始化
我们的[Spring Configuration](guides/java-security.html#security-spring-configuration)创建了一个名为`springSessionRepositoryFilter`的 Spring Bean,它实现了`Filter`。`springSessionRepositoryFilter` 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 security 的`springSecurityFilterChain`之前调用 Spring session 的`springSessionRepositoryFilter`是极其重要的。这样做可以确保 Spring 安全性使用的`HttpSession`得到 Spring Session 的支持。幸运的是, Spring Session 提供了一个名为`AbstractHttpSessionApplicationInitializer`的实用程序类,这使得这样做很容易。下面的示例展示了如何做到这一点:
SRC/main/java/sample/initializer.java
```
public class Initializer extends AbstractHttpSessionApplicationInitializer {
}
```
| |我们类的名称(`Initializer`)并不重要。重要的是,我们扩展`AbstractHttpSessionApplicationInitializer`。|
|---|---------------------------------------------------------------------------------------------------------------------------------------|
通过扩展`AbstractHttpSessionApplicationInitializer`,我们确保在 Spring Security 的`springSecurityFilterChain`之前的每个请求都将名为`springSessionRepositoryFilter`的 Spring Bean 注册到我们的 Servlet 容器中。
## 集成如何工作
幸运的是,`HttpSession`和`HttpServletRequest`(获取`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 一起工作。
| |[剩余样本](samples.html#samples)提供了一个工作示例,说明如何在 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`。
过滤器负责替换要由 Spring Session 支持的`HttpSession`实现。
在此实例中, Spring Session 由 Redis 支持。|
|-----|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|我们创建一个`RedisConnectionFactory`将 Spring Session 连接到 Redis 服务器。
我们将连接配置为在默认端口(6379)上连接到 localhost。
有关配置 Spring 数据 Redis 的更多信息,请参见[参考文献](https://docs.spring.io/spring-data/data-redis/docs/2.6.2/reference/html/)。|
|**3**|我们定制了 Spring Session 的 HttpSession 集成,以使用 HTTP 头来传递当前会话信息,而不是 Cookie。|
### Servlet 容器初始化
我们的[Spring Configuration](#rest-spring-configuration)创建了一个名为`springSessionRepositoryFilter`的 Spring Bean,它实现了`Filter`。`springSessionRepositoryFilter` 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 通过声明`SessionEventHttpSessionListenerAdapter`将`SessionDestroyedEvent`和`SessionCreatedEvent`转换为`HttpSessionEvent`,会话支持`HttpSessionListener`。要使用此支持,你需要:
* 确保你的`SessionRepository`实现支持并配置为发射`SessionDestroyedEvent`和`SessionCreatedEvent`。
* 将`SessionEventHttpSessionListenerAdapter`配置为 Spring Bean。
* 将每个`HttpSessionListener`注入`SessionEventHttpSessionListenerAdapter`
如果你使用[`HttpSession`with Redis](#HttpSession-Redis)中记录的配置支持,那么你所需要做的就是将每个`HttpSessionListener`注册为 Bean。例如,假设你希望支持 Spring Security 的并发控制,并且需要使用`HttpSessionEventPublisher`。在这种情况下,可以将`HttpSessionEventPublisher`添加为 Bean。在 Java 配置中,这可能如下所示:
```
@Configuration
@EnableRedisHttpSession
public class RedisHttpSessionConfig {
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
// ...
}
```
在 XML 配置中,这可能如下所示:
```
```