zoukankan      html  css  js  c++  java
  • springboot中使用spring-session实现共享会话到redis(二)

    上篇文章介绍了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 2.x 使用 Druid 数据源
    @ConditionalOnMissingBean、@Import 组合使用
    Springboot 2.x 整合 JDBC
    IDEA org.apache.maven.plugins:maven-jar-plugin 报红
  • 原文地址:https://www.cnblogs.com/jpfss/p/11016419.html
Copyright © 2011-2022 走看看