一方面是因为我们做token验证的时候是在zuul网关这边做的,用户认证通过后,需要对token的刷新来模拟session的功能,但遇到刷新后的值不能直接返回给浏览器的问题,一直没有找到很好的解决办法,第二方面就是大家对于session使用熟练,使用起来比token更加顺手。所以就研究了如何用spring session和MongoDB来实现分布式session的共享。
由于http协议是无状态的协议,为了能够记住请求的状态,于是引入了Session和Cookie的机制。我们应该有一个很明确的概念,那就是Session是存在于服务器端的,它是由tomcat管理的,存在于tomcat的内存中。而Cookie则是存在于客户端,更方便理解的说法,可以说存在于浏览器。http协议允许从服务器返回Response时携带一些Cookie,并且同一个域下对Cookie的数量有所限制,之前说过Session的持久化依赖于服务端的策略,而Cookie的持久化则是依赖于本地文件。有一类特殊的Cookie我们需要额外关注,那便是与Session相关的sessionId,他是真正维系客户端和服务端的桥梁。
由于一般session只存在与单体应用中,当遇到分布式系统时,就会出现session不能共享的问题。这时就需要借助外部存储工具,将服务器上产生的session复制到该外部存储工具上面,以此实现不同服务器上共享同一个session,这就叫做分布式session。
下图是一般单体应用中session的工作原理图
下图是分布式session的工作原理图
核心思路:就是替换掉servlet容器创建和管理session的实现
实现方案:
1.利用Servlet容器提供的插件功能,自定义HttpSession的创建和管理策略,并通过配置的方式替换掉默认的策略。这方面其实早就有开源项目了,例如memcached-session-manager(目前我们的官方商城都是使用这个),以及tomcat-redis-session-manager。
不过这种方式有个缺点,就是需要耦合Tomcat/Jetty等Servlet容器的代码。
2.设计一个Filter,利用HttpServletRequestWrapper,实现自己的 getSession()方法,接管创建和管理Session数据的工作。spring-session就是通过这样的思路实现的。
3.与其自己写方法实现造轮子,不如直接使用轮子spring session。
spring-session是spring旗下的一个项目,把servlet容器实现的httpSession替换为spring-session,专注于解决 session管理问题。可简单快速且无缝的集成到我们的应用中。
特性
1.轻易把session存储到第三方存储容器,框架提供了redis、jvm的map、mongo、gemfire、hazelcast、jdbc等多种存储session的容器的方式。
2.同一个浏览器同一个网站,支持多个session问题。
3.Restful API,不依赖于cookie。可通过header来传递jessionID
4.WebSocket和spring-session结合,同步生命周期管理。
spring-session无缝替换应用服务器的request大概原理是:
1.自定义个Filter,实现doFilter方法
2.继承 HttpServletRequestWrapper 、HttpServletResponseWrapper 类,重写getSession等相关方法(在这些方法里调用相关的 session存储容器操作类)。
3.在 第一步的doFilter中,new 第二步 自定义的request和response的类。并把它们分别传递 到 过滤器链
4.把该filter配置到 过滤器链的第一个位置上
有兴趣研究源码的,可以看看这篇文章:
http://www.infoq.com/cn/articles/Next-Generation-Session-Management-with-Spring-Session
使用起来非常简单,只需四步,我们就能像以前一样使用session了。
第一步:新建一个spring boot项目。
第二步:添加依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-mongodbartifactId>
dependency>
<dependency>
<groupId>org.springframework.sessiongroupId>
<artifactId>spring-sessionartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
第三步:在application.yml配置文件中添加配置
#mongoDB服务相关配置
spring:
data:
mongodb:
host: localhost
port: 27017
database: mydb
第四步:添加一个配置类
@Configuration
@EnableMongoHttpSession
public class HttpSessionConfig {
@Bean
public JdkMongoSessionConverter jdkMongoSessionConverter() {
return new JdkMongoSessionConverter();
}
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("JSESSIONID");
serializer.setCookiePath("/");
serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");
return serializer;
}
}
这样就能使用分布式session了,可以说灰常简单了!!!