Spring Session -- HttpSession Integration HttpSession集成
官网:https://spring.io/projects/spring-session
Samples:https://docs.spring.io/spring-session/reference/samples.html#samples
指引
- Spring Session – HttpSession Integration HttpSession集成
- Spring Session – WebSocket Integration WebSocket 集成
- Spring Session – WebSession Integration WebSession 集成
- Spring Session – Spring Security Integration Spring Security 集成
HttpSession Integration HttpSession 集成
- 集群会话:Spring Session 使支持集群会话变得简单,而无需绑定到特定于应用程序容器的解决方案
- RESTful API:Spring Session 允许在标头中提供会话 ID 与 RESTful API 一起使用
HttpSession 与 Redis
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
Java 配置
@EnableRedisHttpSession
public class Config {
@Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
}
- @EnableRedisHttpSession 注释创建一个名称为 springSessionRepositoryFilter 的 Spring Bean 来实现负责替换由 Spring Session 支持的 HttpSession 实现的过滤器。在该实例 Spring Session 由 Redis 支持
- 创建 RedisConnectionFactory 连接 Spring Session 和 Redis Server 将连接配置为连接到默认端口(6379)上的 localhost 有关配置 Spring Data Redis
Java Servlet 容器初始化
Spring Session 替换 Redis 实现集群会话
public class Initializer extends AbstractHttpSessionApplicationInitializer {
public Initializer() {
super(Config.class);
}
}
- AbstractHttpSessionApplicationInitializer 确保每个请求的 Spring Bean 都已注册到我们的 Servlet 容器中
- AbstractHttpSessionApplicationInitializer 提供了一种机制来确保 Spring 加载
XML 配置
src/main/webapp/WEB-INF/spring/session.xml
a
context:annotation-config/
<bean class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"/>
XML Servlet 容器初始化
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>
<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>
HttpSession 与 MongoDB
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-mongodb</artifactId>
</dependency>
Java 配置
@EnableMongoHttpSession
public class HttpSessionConfig {
@Bean
public JdkMongoSessionConverter jdkMongoSessionConverter() {
return new JdkMongoSessionConverter(Duration.ofMinutes(30));
}
}
Spring Session MongoDB 默认使用 JdkMongoSessionConverter
@Bean
public JdkMongoSessionConverter jdkMongoSessionConverter() {
return new JdkMongoSessionConverter(Duration.ofMinutes(30));
}
Spring Session MongoDB 使用 Jackson 将会话对象序列化为 JSON
@Bean
JacksonMongoSessionConverter mongoSessionConverter() {
return new JacksonMongoSessionConverter();
}
Spring Session MongoDB
@Configuration
@EnableMongoHttpSession
static class Config extends BaseConfig {
@Bean
AbstractMongoSessionConverter mongoSessionConverter() {
return new JacksonMongoSessionConverter(Collections.singletonList(new GeoModule()));
}
}
HttpSession 与 JDBC
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-jdbc</artifactId>
</dependency>
Java 配置
@EnableJdbcHttpSession
public class Config {
@Bean
public EmbeddedDatabase dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2).addScript("org/springframework/session/jdbc/schema-h2.sql").build();
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
Java Servlet 容器初始化
src/main/java/sample/Initializer.java
public class Initializer extends AbstractHttpSessionApplicationInitializer {
public Initializer() {
super(Config.class);
}
}
Multiple DataSources 多数据源
使用 @SpringSessionDataSource 定义 Spring Session 的数据源
Config.java
@EnableJdbcHttpSession
public class Config {
@Bean
@SpringSessionDataSource
public EmbeddedDatabase firstDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2).addScript("org/springframework/session/jdbc/schema-h2.sql").build();
}
@Bean
public HikariDataSource secondDataSource() {
// ...
}
}
HttpSession 与 Hazelcast
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-hazelcast</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-hazelcast</artifactId>
</dependency>
Java 配置
@EnableHazelcastHttpSession
@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)
.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);
return Hazelcast.newHazelcastInstance(config);
}
}
Servlet 容器初始化
src/main/java/sample/SecurityInitializer.java
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
public SecurityInitializer() {
super(SecurityConfig.class, SessionConfig.class);
}
}
确保 Spring Security 使用的 Spring Session 支持
src/main/java/sample/Initializer.java
public class Initializer extends AbstractHttpSessionApplicationInitializer {
}
HttpSession 集成的工作原理
创建 SessionRepositoryRequestWrapper 继承 HttpServletRequestWrapper 重写 getSession 方法,创建 SessionRepositoryFilter 实现 Filter 接口 重写 doFilter 方法
HttpSession HttpServletRequest
创建 SessionRepositoryRequestWrapper 继承 HttpServletRequestWrapper 实现
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 ...
}
HttpServletRequest Filter SessionRepositoryFilter
创建 SessionRepositoryFilter 实现 Filter 接口以注册使用
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);
}
// ...
}
HttpSession 和 RESTful API
Java 配置
@Configuration
@EnableRedisHttpSession
public class HttpSessionConfig {
@Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
@Bean
public HttpSessionIdResolver httpSessionIdResolver() {
return HeaderHttpSessionIdResolver.xAuthToken();
}
}
Servlet 容器初始化
src/main/java/sample/mvc/MvcInitializer.java
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { SecurityConfig.class, HttpSessionConfig.class };
}
src/main/java/sample/Initializer.java
public class Initializer extends AbstractHttpSessionApplicationInitializer {
}
使用 HttpSessionListener
Spring Session 通过声明 SessionEventHttpSessionListenerAdapter,将 SessionDestroyedEvent 和 SessionCreatedEvent 转换为 HttpSessionEvent 支持 HttpSessionListener
- 确保实现支持和已配置:SessionRepository SessionDestroyedEvent SessionCreatedEvent
- 配置 Spring Bean:SessionEventHttpSessionListenerAdapter
- 将 every 注入:HttpSessionListener SessionEventHttpSessionListenerAdapter
如果使用 Redis 支持,将 enableIndexingAndEvents 设置为 true, @EnableRedisHttpSession(enableIndexingAndEvents = true),所需要做的就是将每个 HttpSessionEventPublisher 注册为bean
支持 Spring Security 的并发控制,并且需要使用 HttpSessionEventPublisher。可如下配置:
Java 配置
@Configuration
@EnableRedisHttpSession
public class RedisHttpSessionConfig {
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
// ...
}
XML 配置
<bean class="org.springframework.security.web.session.HttpSessionEventPublisher"/>
Cookie 序列化器
// tag::cookie-serializer[]
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("JSESSIONID");
serializer.setCookiePath("/");
serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");
return serializer;
}
// end::cookie-serializer[]
- 将cookie的名称定制为JSESSIONID
- 将cookie的路径定制为/(而不是默认的上下文根目录)
- 将cookie的域名模式定制为^.+?\.(\w+\.[a-z]+)$