# `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 配置中,这可能如下所示: ``` ```