上篇文章介绍了springboot中集成spring-session实现了将session分布式存到redis中。这篇在深入介绍一些spring-session的细节。

1、session超时:

在tomcat中,如果要设置session的超时,我们可以在web.xml或者springboot的application.properties中直接配置即可,例如在springboot中设置:

server.session.timeout=1800

但引入了spring-session后,这个配置将不再起作用, 我们需要写一个如下的配置类:


  1. import org.springframework.context.annotation.Configuration;
  2. import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
  3. @Configuration
  4. //maxInactiveIntervalInSeconds 默认是1800秒过期,这里测试修改为60秒
  5. @EnableRedisHttpSession(maxInactiveIntervalInSeconds=60)
  6. public class RedisSessionConfig{
  7. }

注:如果不修改session超时,可以不用该配置类。

2、在springboot中使用spring-session完成登录、登出等功能:

1)定义User实体类:


  1. public class User implements Serializable {
  2. private static final long serialVersionUID = 1629629499205758251L;
  3. private Long id;
  4. private String name;
  5. private String pwd;
  6. private String note;
  7. private Integer dateAuth;
  8. private Integer tableAuth;
  9. //set/get 方法

注:该类需要序列化,因为spring-session会将该对象序列化后保存到redis中。

2)UserController:


  1. @RequestMapping("/user")
  2. @Controller
  3. public class UserController {
  4. private static final Logger logger = LoggerFactory.getLogger(UserController.class);
  5. @Autowired
  6. private UserService userService;
  7. /**
  8. * 退出
  9. * @param request
  10. * @return
  11. */
  12. @RequestMapping("/loginOut")
  13. @ResponseBody
  14. public ResponseMessage loginOut(HttpServletRequest request, HttpServletResponse response) {
  15. HttpSession session = request.getSession();
  16. if (session != null) {
  17. session.setAttribute(session.getId(), null);
  18. }
  19. return ResponseMessage.ok(Constants.CODE_SUCCESS,null);
  20. }
  21. /**
  22. * 登录验证
  23. * @param request
  24. * @return
  25. */
  26. @RequestMapping("/login")
  27. public ModelAndView login(HttpServletRequest request,Model model) {
  28. String name = request.getParameter("username");
  29. String password = request.getParameter("password");
  30. //TODO校验
  31. Map<String,String> map = new HashMap<>();
  32. map.put("name",name);
  33. map.put("pwd",password);
  34. User user = null;
  35. try {
  36. user = userService.login(map);
  37. } catch (Exception e) {
  38. logger.error("user login is error...",e);
  39. }
  40. if (user != null) {
  41. HttpSession session = request.getSession();
  42. session.setAttribute(session.getId(),user);
  43. model.addAttribute("user", user);
  44. logger.info("user login is success,{}",name);
  45. return new ModelAndView("redirect:/index");
  46. } else {
  47. request.setAttribute("errorInfo", "验证失败");
  48. return new ModelAndView("login/login");
  49. }
  50. }
  51. }

:spring-session会通过拦截器的方式往session对象中存放、移除sessionId(session.getId()),所以我们在登录、登出、拦截器中会调用session.setAttribute(session.getId(),user);来判断。

3)session拦截器:


  1. public class SessionInterceptor extends HandlerInterceptorAdapter {
  2. private static String[] IGNORE_URI = {"/login.jsp", "/login/","/login","/loginIndex", "/error"};
  3. private static Logger log = LoggerFactory.getLogger(SessionInterceptor.class);
  4. @Override
  5. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  6. boolean flag = false;
  7. String url = request.getRequestURL().toString();
  8. /*String currentURL = request.getRequestURI(); // 取得根目录所对应的绝对路径:
  9. String targetURL = currentURL.substring(currentURL.lastIndexOf("/"), currentURL.length());// 截取到当前文件名用于比较
  10. String currentURLTemp = currentURL.replaceAll("/iis/", "");*/
  11. for (String s : IGNORE_URI) {
  12. if (url.contains(s)) {
  13. flag = true;
  14. break;
  15. }
  16. }
  17. if (!flag) {
  18. HttpSession session = request.getSession();
  19. Object obj = session.getAttribute(session.getId());//Constants.SESSION_USER
  20. if (null == obj) {//未登录
  21. String servletPath = request.getServletPath();
  22. log.error("session失效,当前url:" + url+";module Paht:"+servletPath);
  23. if (request.getHeader("x-requested-with") != null &&
  24. request.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest")){
  25. response.setHeader("sessionstatus", "timeout");//在响应头设置session状态
  26. response.setCharacterEncoding("UTF-8");
  27. response.setContentType("text/html;charset=UTF-8");
  28. response.getWriter().print("error");
  29. } else {
  30. response.sendRedirect(request.getContextPath()+"/user/loginIndex");
  31. }
  32. return false;
  33. } else {
  34. /*User user = (User)obj;
  35. if(!RightUtil.hasRight(currentURLTemp, request)){
  36. if(!"iisAdminTmp".equals(user.getName()) && !"/index".equals(targetURL)){
  37. //response.sendRedirect(request.getContextPath()+"/login/login");//应该返回到没有权限的页面
  38. //request.getRequestDispatcher("/login/login").forward(request, response);
  39. return false;
  40. }
  41. }*/
  42. }
  43. }
  44. return super.preHandle(request, response, handler);
  45. }
  46. @Override
  47. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  48. super.postHandle(request, response, handler, modelAndView);
  49. }
  50. }

说明:

我们知道spring-session会自动注入springSessionRepositoryFilter过滤器,每一次的请求都由他来过滤,其本质是:对每一个请求的request进行了一次封装。那么,在Controller里面拿出的request实际上是封装后的request,

调用request.getSession()的时候,实际上拿到是Spring封装后的session。这个session则存储在redis数据库中。

应用通过 getSession(boolean create) 方法来获取 session 数据,参数 create 表示 session 不存在时是否创建新的 session 。 getSession 方法首先从请求的 “.CURRENT_SESSION” 属性来获取 currentSession ,没有 currentSession ,则从 request 取出 sessionId ,然后读取 spring:session:sessions:[sessionId] 的值,同时根据 lastAccessedTime 和 MaxInactiveIntervalInSeconds 来判断这个 session 是否过期。如果 request 中没有 sessionId ,说明该用户是第一次访问,会根据不同的实现,如 RedisSession ,MongoExpiringSession ,GemFireSession 等来创建一个新的 session 。​ 另外, 从 request 取 sessionId 依赖具体的 HttpSessionStrategy 的实现,spring session 给了两个默认的实现 CookieHttpSessionStrategy 和 HeaderHttpSessionStrategy ,即从 cookie 和 header 中取出 sessionId 。

3、spring-session在redis中的存储结构:

spring:session是默认的Redis HttpSession前缀(redis中,我们常用’:’作为分割符)。如上图,每一个session都会创建3组数据:

1)spring:session:sessions:6e4fb910-34f7-453d-a8c6-2b3cd192e051

hash结构,存储了session信息(实体类的序列化数据)、maxInactiveInterval、创建时间、lastAccessedTime四部分信息。

2)spring:session:sessions:expires:6e4fb910-34f7-453d-a8c6-2b3cd192e051

string结构,value为空。

3)spring:session:expirations:1529395440000:

set结构,存储过期时间记录

注:在spring-session中提到,由于redis的ttl删除key是一个被动行为,所以才会引入了expirations这个key,来主动进行session的过期行为判断。

springsession相关参考:

https://segmentfault.com/a/1190000011091273#articleHeader14

https://blog.csdn.net/lxhjh/article/details/78048201

http://www.infoq.com/cn/articles/Next-Generation-Session-Management-with-Spring-Session
https://qbgbook.gitbooks.io/spring-boot-reference-guide-zh/IV.%20Spring%20Boot%20features/38.%20Spring%20Session.html

原文地址:https://blog.csdn.net/liuxiao723846/article/details/80733565

springboot中使用spring-session实现共享会话到redis(二)的更多相关文章

  1. SpringBoot中使用Spring Data Jpa 实现简单的动态查询的两种方法

    软件152 尹以操 首先谢谢大佬的简书文章:http://www.jianshu.com/p/45ad65690e33# 这篇文章中讲的是spring中使用spring data jpa,使用了xml ...

  2. 使用Spring Session做分布式会话管理

    在Web项目开发中,会话管理是一个很重要的部分,用于存储与用户相关的数据.通常是由符合session规范的容器来负责存储管理,也就是一旦容器关闭,重启会导致会话失效.因此打造一个高可用性的系统,必须将 ...

  3. (十九)SpringBoot之使用Spring Session集群-redis

    一.引入maven依赖 <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEnc ...

  4. springboot中使用spring security,登录url就出现403错误

    参考链接:https://segmentfault.com/q/1010000012743613 有两个controller,一个是所有用户可以访问的@RequestMapping("use ...

  5. SpringBoot中获取spring.profiles.active的值

    一.网上很多采用@Profile("dev")的方式获取,但是这个是类级别的 二.开发中可能需要代码级别 1.刚开始我想通过classpath下的文件读取方式,麻烦死了,于是换了个 ...

  6. SpringBoot中实现Spring容器中注入类型相同但名不同Bean

    @Bean(autowire = Autowire.BY_NAME,value = "kaptchaProducer") public Producer kaptchaProduc ...

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

    一.前言 项目中用到的SSO,使用开源框架cas做的.简单的了解了一下cas,并学习了一下 单点登录的原理,有兴趣的同学也可以学习一下,写个demo玩一玩. 二.工程结构 我模拟了 sso的客户端和s ...

  8. 大话 Spring Session 共享

    javaweb中我们项目稍微正规点,都会用到单点登录这个技术.实现它的方法各家有各界的看法.这几天由于公司项目需求正在研究.下面整理一下最近整理的心得. 简介 在分布式项目中另我们头疼的是多项目之间的 ...

  9. Spring Session解决Session共享

    1. 分布式Session共享   在分布式集群部署环境下,使用Session存储用户信息,往往出现Session不能共享问题.   例如:服务集群部署后,分为服务A和服务B,当用户登录时负载到服务A ...

随机推荐

  1. 2019-1-17-一段能让-VisualStudio-炸掉的代码

    title author date CreateTime categories 一段能让 VisualStudio 炸掉的代码 lindexi 2019-01-17 09:55:29 +0800 20 ...

  2. Linux ar命令介绍 和常用示例

    制作静态库要用到ar命令,命令格式: ar [-]{dmpqrtx}[abcfilNoPsSuvV] [membername] [count] archive files... {dmpqrtx}中的 ...

  3. 如何用最暴力的方法改写Liferay的原生portlet

    最近在论坛上看到有人问如何改写Liferay原有的calendar portlet. 然后研究了一下,直接从portal中把calendar portlet的源码拷贝出来,然后修改再部署上去,但是这个 ...

  4. 关于JSON的个人理解

    1.比xml更易于解析的数据存储方式 2.主要是用键值对的方式进行存储 3.可以用来存储对象或者是对象数组 个人感觉W3C上给的教程很好

  5. Significance A and B for protein ratios

    实验设计中,一般会做三个生物学重复来确保结果的准确性,尤其在下游分析中.但有时会遇到没有生物学重复,而又需要进行差异分析的情况,这时一般建议考虑foldchange即可,因为根本无法进行T-test等 ...

  6. 【JZOJ4922】【NOIP2017提高组模拟12.17】环

    题目描述 小A有一个环,环上有n个正整数.他有特殊的能力,能将环切成k段,每段包含一个或者多个数字.对于一个切分方案,小A将以如下方式计算优美程度: 首先对于每一段,求出他们的数字和.然后对于每段的和 ...

  7. 二.python数据结构的性能分析

    目录: 1.引言 2.列表 3.字典 一.引言 - 现在大家对 大O 算法和不同函数之间的差异有了了解.本节的目标是告诉你 Python 列表和字典操作的 大O 性能.然后我们将做一些基于时间的实验来 ...

  8. 【Leetcode栈】有效的括号(20)

    题目 给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效. 有效字符串需满足: 1,左括号必须用相同类型的右括号闭合. 2,左括号必须以正确的顺序闭合. 注意 ...

  9. 【Leetcode链表】奇偶链表(328)

    题目 给定一个单链表,把所有的奇数节点和偶数节点分别排在一起.请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性. 请尝试使用原地算法完成.你的算法的空间复杂度应为 O(1 ...

  10. Android开发-API指南-<activity-alias>[原创译文]

    http://blog.sina.com.cn/s/blog_48d491300100zmg5.html