redis实现session共享的一些细节

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了redis实现session共享的一些细节相关的知识,希望对你有一定的参考价值。

参考技术A 如果仅仅是写demo,对于sprintboot项目,只要在启动类加上@EnableRedisHttpSession注解就可以实现session共享(参考网上教程),但是,如果企业项目,还有很多细节需要考虑。

每一个会话在redis中对应3个key

其中spring:session:sessions:32d0d3b2-0f04-469b-a917-3a55e60f7393存放的是具体内容,sessionAttr开头的field与setAttribute()一一对应

第一次创建SESSION时(一般是登录的时候),后端把sessionId set-cookie到前端

之后的请求,前端把cookie中的SESSION传到后端,后端根据cookie识别session

后端的接口一般会有token认证机制(jwt是常见的token实现方案),token会有一定的有效期,如果token过期,后端返回401状态码(unauthorized),前端的公共js方法看到状态码为401 ,提示用户“登陆已过期”并跳转到登录页。

假设token有效期默认为30分钟,用户A登录后生成一个token,30分钟内用户不停的操作,这种情况下,假如token依然过期,提示用户“登陆已过期”并跳转到登录页,用户体验就会非常差。

因此,token通常会有自动续期的机制,每次用户调用接口时,把redis中该token的ttl重置为30min。

假设token的有效期比session的长,session过期了但是token没过期,那么用户仍处于登录状态,这时如果调用一些需要从session取数据的接口,就会有问题。

因此,session的有效期,至少要跟token一样长,但是token有自动续期机制,所以session也要有自动续期机制。

经测试,springboot项目,使用redis实现session共享,session的有效期默认为2100s,即35分钟,并且,springboot已经实现了自动续期,每次访问session(getSession或者存取数据),都会把ttl重置为2100s。

看起来已经完美了,其实还有问题。

假如用户35min内的操作,都不涉及session,那么session就会过期,但是token依然没过期,还是会有问题。

解决方案是:每次token校验成功后,调用一次getSession(false)方法,重置session的ttl。

如果某一次请求时,后端创建了新的session,就会把新的sessionId set-cookie到前端,之后前端发起请求时,cookie会带上新的sessionId,后端也根据这个新的sessionId寻找会话。

但是,这个新的sessionId并没有对应的内容(一般只会在登录的时候,把用户信息等内容set到session)。

因此,仅当登录的时候,允许创建新的session,其余的地方,如果需要获取session,需要用getSession(false),false表示不创建新session,若session为空返回null。

注意:getSession()等价于getSession(true),在没有session时,会创建新session。

如果是通过session的setAttribute()和getAttribute() api实现数据存取,springboot会帮我们实现序列化和反序列化,但是,假如我们需要自己实现数据存取(比如我们是开发人员,想要查看用户session里的信息,就需要自己实现反序列化),该怎么办?

以获取session中的内容为例,session的内容在redis中以hash格式存放,而redis对该hash的value使用的serializer是JdkSerializationRedisSerializer,因此,如果要把session的内容(字节数组)转化为java对象(即反序列化过程),需要设置serializer为JdkSerializationRedisSerializer。

每次执行session.setAttribute(),并不会马上把数据写到redis,而是先写到本地内存缓存,等本次请求结束后,再写到redis。

path属性为glcs,代表请求路径需要包含glcs,才会把该cookie带到后端;
httponly属性,表示该cookie无法通过js读取/修改,比如document.cookie无法读取,只有发起http请求的时候才会自动带到后端

也可以使用EnableMongoHttpSession注解用MongoDB来管理session

spring boot + redis 实现session共享

这次带来的是spring boot + redis 实现session共享的教程。

 

在spring boot的文档中,告诉我们添加@EnableRedisHttpSession来开启spring session支持,配置如下:

Java代码  技术分享
  1. @Configuration  
  2. @EnableRedisHttpSession  
  3. public class RedisSessionConfig {  
  4. }  

而@EnableRedisHttpSession这个注解是由spring-session-data-redis提供的,所以在pom.xml文件中添加:

Java代码  技术分享
  1. <dependency>  
  2.         <groupId>org.springframework.boot</groupId>  
  3.         <artifactId>spring-boot-starter-redis</artifactId>  
  4. </dependency>  
  5. <dependency>  
  6.         <groupId>org.springframework.session</groupId>  
  7.         <artifactId>spring-session-data-redis</artifactId>  
  8. </dependency>  

 

 

接下来,则需要在application.properties中配置redis服务器的位置了,在这里,我们就用本机:

Java代码  技术分享
  1. spring.redis.host=localhost  
  2. spring.redis.port=6379  

这样以来,最简单的spring boot + redis实现session共享就完成了,下面进行下测试。

 

首先我们开启两个tomcat服务,端口分别为8080和9090,在application.properties中进行设置【下载地址】   

Java代码  技术分享
  1. server.port=8080  

 

接下来定义一个Controller: 

Java代码  技术分享
  1. @RestController  
  2. @RequestMapping(value = "/admin/v1")  
  3. public class QuickRun {  
  4.     @RequestMapping(value = "/first", method = RequestMethod.GET)  
  5.     public Map<String, Object> firstResp (HttpServletRequest request){  
  6.         Map<String, Object> map = new HashMap<>();  
  7.         request.getSession().setAttribute("request Url", request.getRequestURL());  
  8.         map.put("request Url", request.getRequestURL());  
  9.         return map;  
  10.     }  
  11.   
  12.     @RequestMapping(value = "/sessions", method = RequestMethod.GET)  
  13.     public Object sessions (HttpServletRequest request){  
  14.         Map<String, Object> map = new HashMap<>();  
  15.         map.put("sessionId", request.getSession().getId());  
  16.         map.put("message", request.getSession().getAttribute("map"));  
  17.         return map;  
  18.     }  
  19. }  

 

启动之后进行访问测试,首先访问8080端口的tomcat,返回 获取【下载地址】   

Java代码  技术分享
  1. {"request Url":"http://localhost:8080/admin/v1/first"}  

 接着,我们访问8080端口的sessions,返回:

Java代码  技术分享
  1. {"sessionId":"efcc85c0-9ad2-49a6-a38f-9004403776b5","message":"http://localhost:8080/admin/v1/first"}  

最后,再访问9090端口的sessions,返回:

Java代码  技术分享
  1. {"sessionId":"efcc85c0-9ad2-49a6-a38f-9004403776b5","message":"http://localhost:8080/admin/v1/first"}  

可见,8080与9090两个服务器返回结果一样,实现了session的共享

 

如果此时再访问9090端口的first的话,首先返回:

Java代码  技术分享
  1. {"request Url":"http://localhost:9090/admin/v1/first"}  

而两个服务器的sessions都是返回:

Java代码  技术分享
  1. {"sessionId":"efcc85c0-9ad2-49a6-a38f-9004403776b5","message":"http://localhost:9090/admin/v1/first"}  

 

通过spring boot + redis来实现session的共享非常简单,而且用处也极大,配合nginx进行负载均衡,便能实现分布式的应用了。

本次的redis并没有进行主从、读写分离等等配置(_(:з」∠)_其实是博主懒,还没尝试过.......)

而且,nginx的单点故障也是我们应用的障碍......以后可能会有对此次博客的改进版本,比如使用zookeeper进行负载均衡,敬请期待。

 

以上是关于redis实现session共享的一些细节的主要内容,如果未能解决你的问题,请参考以下文章

Tomcat8+Redis+Nginx实现集群

单点登录实现(spring session+redis完成session共享)

转载tomcat+nginx+redis实现均衡负载session共享

Spring-SESSION+Redis 实现Session共享

spring boot + redis 实现session共享

SpringSpringBoot + SpringSession + Redis 实现Session共享