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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM