Spring Session - raymond-zhao/cat-mall GitHub Wiki
- 同一个服务,复制多份,Session 不同步;
- 不同服务,Session不共享。
- Session复制
- 客户端存储
- Nginx负载均衡之 ip_hash
- 统一存储
本系统 Session 共享解决方案:利用 Redis 统一存储,当以后 Session 数据量较大的时候可以选择扩充 Redis 集群,主从、哨兵等。另外,由于 Redis 是纯内存操作,速度极快。
- 第一次使用 Session,浏览器将会保存相应信息 JSESSIONID 这个 Cookie;
- 以后浏览器访问网站时将会带上这个网站的 Cookie;
- 在子域时进行作用域放大,同时 JSON 序列化对象存储到 Redis;
导入依赖
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
配置
spring.session.store-type=redis
@EnableRedisHttpSession
@Configuration
public class MallSessionConfig {
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
// 放大作用域
cookieSerializer.setDomainName("catmall.com");
cookieSerializer.setCookieName("MALLSESSION");
return cookieSerializer;
}
@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
// JSON 序列化
return new GenericJackson2JsonRedisSerializer();
}
}
-
@EnableRedisHttpSession
注解导入了RedisHttpSessionConfiguration
配置 -
RedisHttpSessionConfiguration
给容器中添加了一个组件RedisIndexedSessionRepository
,这个类封装了Redis对Session的大量操作 -
RedisHttpSessionConfiguration
继承的SpringHttpSessionConfiguration
中有一个类型为SessionRepositoryFilter
的Bean
,这个Bean
最上层的接口就是javax.servlet
下的Filter
- 在
SessionRepositoryFilter
继承了抽象类OncePerRequestFilter
,这个类下有一个名为doFilterInternal
的受保护的抽象方法,SessionRepositoryFilter
重写了这个方法,它是Session
存储过滤器,每个请求都必须经过filter
,它- 创建的时候,自动从容器中获取
SessionRepository
- 原始的
request
被包装成SessionRepositoryRequestWrapper
- 原始的
response
被包装成SessionRepositoryResponseWrapper
- 以后再获取
Session
,使用wrappedRequest.getSession()
从SessionRepository
中获取(从Redis
中获取),而不用从HttpServletRequest.getSession()
获取 - 使用了装饰者
Decorator
模式
- 创建的时候,自动从容器中获取
- 使用
Spring Session
时,Redis
中存储的Session
有默认时间,但是如果页面有活动的话也会自动续期。
// 1. 查看入口注解
@EnableRedisHttpSession
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(RedisHttpSessionConfiguration.class) // 2. 引入一个配置类
@Configuration(proxyBeanMethods = false)
public @interface EnableRedisHttpSession { ... }
// 3. RedisHttpSessionConfiguration 向容器中加入了一个 Bean
// RedisIndexedSessionRepository 是 Redis 对 Session 增删改查的工具类
// 4. 这个类还继承了 SpringHttpSessionConfiguration
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware {
@Bean
public RedisIndexedSessionRepository sessionRepository() {
RedisTemplate<Object, Object> redisTemplate = createRedisTemplate();
RedisIndexedSessionRepository sessionRepository = new RedisIndexedSessionRepository(redisTemplate);
sessionRepository.setApplicationEventPublisher(this.applicationEventPublisher);
if (this.indexResolver != null) {
sessionRepository.setIndexResolver(this.indexResolver);
}
if (this.defaultRedisSerializer != null) {
sessionRepository.setDefaultSerializer(this.defaultRedisSerializer);
}
sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
if (StringUtils.hasText(this.redisNamespace)) {
sessionRepository.setRedisKeyNamespace(this.redisNamespace);
}
sessionRepository.setFlushMode(this.flushMode);
sessionRepository.setSaveMode(this.saveMode);
int database = resolveDatabase();
sessionRepository.setDatabase(database);
this.sessionRepositoryCustomizers
.forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository));
return sessionRepository;
}
}
// SpringHttpSessionConfiguration
public class SpringHttpSessionConfiguration implements ApplicationContextAware {
// ...
// 5. Session过滤器,这个类又继承了 SessionRepositoryFilter
@Bean
public <S extends Session> SessionRepositoryFilter<? extends Session> springSessionRepositoryFilter(
SessionRepository<S> sessionRepository) {
SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<>(sessionRepository);
sessionRepositoryFilter.setHttpSessionIdResolver(this.httpSessionIdResolver);
return sessionRepositoryFilter;
}
// ...
}
// SessionRepositoryFilter 继承了 OncePerRequestFilter
public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFilter {
// ...
}
// 实现了 Filter 接口
abstract class OncePerRequestFilter implements Filter {
// ...
}
// 这个其实就是位于 javax.servlet 下的原生 Filter
public interface Filter {
// ...
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 将原生的请求、上下文信息等包装为请求对象
request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response);
SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest,
response);
try {
// 包装后的对象应用到了后面的整个执行链
filterChain.doFilter(wrappedRequest, wrappedResponse);
}
finally {
wrappedRequest.commitSession();
}
}