Session原理
- 浏览器第一次访问服务器,登录成功后服务器会保存一个session对象(可看成map对象),session对象里以键值对的方式保存了用户的登录信息,并且每一个session对象都会对应一个独特的sessionId
- 登录成功后服务器会返回一个JsessionId=xxx的Cookie给浏览器,浏览器下次访问时带上这个cookie,服务器就可以根据sessionid判断用户有没有登录。
session共享问题
-
服务器发回来的cookie默认作用域domain是本服务器域名,例如让认证服务器返回的是auth.gulimall.com,而cookie作用域不同是不能共享的,也就是说auth.gulimall.com的作用域不能作用于gulimall.com。
-
分布式下session共享问题,对于集群里的服务器(服务有多个相同的),有以下几种解决方案:
-
使用springSession解决(底层使用redis)(推荐使用) ,对于不同服务下不用域名的问题,使用自定义cookie的作用域domain解决。
-
session复制,让集群中的服务器互相同步session
优点:web-server(tomcat)原生支持,只需要修改tomcat配置文件即可
缺点:1. session同步需要传输数据,占用集群之间的网络带宽
2.任意一台服务器都需要保存所有服务器的session数据,如果session数据量大的话会导致内存浪费严重,内存限制。
3.客户端存储,把数据存储在浏览器中的cookie里。
优点:1. 服务器不需要保存session,节省服务器资源
缺点:1.不安全,存在泄漏、篡改、窃取等安全问题
2. 浏览器cookie长度有限制,一般为4k,不同浏览器限制不同,如果session数据过大,浏览器会保存不了
SpringSession原理:
首先导入 RedisHttpSessionConfiguration 配置类,这个类在容器中添加了 RedisIndexedSessionRepository 组件, 这个组件就是用来存让装饰后的session的bean,相当于是redis操作session的dao[增删改查],同时它又继承了 SpringHttpSessionConfiguration 配置类, 这个类对包装后的Cookie进行了初始化.当服务启动的时候 这个类会自动注入 SessionRepository [我们自己写的配置文件就实现了这个接口]这个组件又在容器中注册了一个springSessionRepositoryFilter 过滤器 ;这个过滤器将原生的request、response、session包装成 SessionRepositoryRequestWrapper,以后对session的操作都将在redis进行了
源码一步步分析:
- @EnableRedi
- @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(RedisHttpSessionConfiguration.class)
@Configuration(proxyBeanMethods = false)
public @interface EnableRedisHttpSession
RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
这个类是用来配置redis作为存储的分布式session配置类,类似的还有RedissonHttpSessionConfiguration用来做分布式锁
@Bean
public RedisIndexedSessionRepository sessionRepository
这个配置类向容器中加入了一个RedisIndexedSessionRepository
,这个组件的作用是存储“装饰后的”RedisSessionRepository,相当于操作redis的session dao接口。
- 同时还继承了SpringHttpSessionConfiguration
- SpringHttpSessionConfiguration会初始化一个cookie序列化器,这为我们配置自定义cookie做了一个铺垫
@PostConstruct
public void init() {
CookieSerializer cookieSerializer = (this.cookieSerializer != null) ? this.cookieSerializer
: createDefaultCookieSerializer();
this.defaultHttpSessionIdResolver.setCookieSerializer(cookieSerializer);
}
- 还向容器中注入了一个SessionRepositoryFilter,相当于注入了一个filter
@Bean
public <S extends Session> SessionRepositoryFilter<? extends Session> springSessionRepositoryFilter(
SessionRepository<S> sessionRepository) {
SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<>(sessionRepository);
sessionRepositoryFilter.setHttpSessionIdResolver(this.httpSessionIdResolver);
return sessionRepositoryFilter;
}
里面有个方法覆盖了父类filter的doFilterInternal
@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