1. 程式人生 > 實用技巧 >SpringBoot+SpringSession+Redis實現session共享及唯一登入

SpringBoot+SpringSession+Redis實現session共享及唯一登入

轉載:https://blog.csdn.net/xjj1040249553/article/details/82658889

一、pom.xml配置

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-redis</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.springframework.session</groupId
    >
  7. <artifactId>spring-session-data-redis</artifactId>
  8. </dependency>

二、application.properties的redis配置

  1. #redis
  2. spring.redis.host=127.0.0.1
  3. spring.redis.port=6379
  4. spring.redis.password=123456
  5. spring.redis.pool.max-idle=8
  6. spring.redis.pool.min-idle=0
  7. spring.redis.pool.max-active=8
  8. spring.redis.pool.max-wait
    =-1
  9. #超時一定要大於0
  10. spring.redis.timeout=3000
  11. spring.session.store-type=redis

在配置redis時需要確保redis安裝正確,並且配置notify-keyspace-events Egx,spring.redis.timeout設定為大於0,我當時這裡配置為0時springboot時啟不起來。

三、編寫登入狀態攔截器RedisSessionInterceptor

  1. //攔截登入失效的請求
  2. public class RedisSessionInterceptor implements HandlerInterceptor
  3. {
  4. @Autowired
  5. private
    StringRedisTemplate redisTemplate;
  6. @Override
  7. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
  8. {
  9. //無論訪問的地址是不是正確的,都進行登入驗證,登入成功後的訪問再進行分發,404的訪問自然會進入到錯誤控制器中
  10. HttpSession session = request.getSession();
  11. if (session.getAttribute("loginUserId") != null)
  12. {
  13. try
  14. {
  15. //驗證當前請求的session是否是已登入的session
  16. String loginSessionId = redisTemplate.opsForValue().get("loginUser:" + (long) session.getAttribute("loginUserId"));
  17. if (loginSessionId != null && loginSessionId.equals(session.getId()))
  18. {
  19. return true;
  20. }
  21. }
  22. catch (Exception e)
  23. {
  24. e.printStackTrace();
  25. }
  26. }
  27. response401(response);
  28. return false;
  29. }
  30. private void response401(HttpServletResponse response)
  31. {
  32. response.setCharacterEncoding("UTF-8");
  33. response.setContentType("application/json; charset=utf-8");
  34. try
  35. {
  36. response.getWriter().print(JSON.toJSONString(new ReturnData(StatusCode.NEED_LOGIN, "", "使用者未登入!")));
  37. }
  38. catch (IOException e)
  39. {
  40. e.printStackTrace();
  41. }
  42. }
  43. @Override
  44. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception
  45. {
  46. }
  47. @Override
  48. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception
  49. {
  50. }
  51. }

四、配置攔截器

  1. @Configuration
  2. public class WebSecurityConfig extends WebMvcConfigurerAdapter
  3. {
  4. @Bean
  5. public RedisSessionInterceptor getSessionInterceptor()
  6. {
  7. return new RedisSessionInterceptor();
  8. }
  9. @Override
  10. public void addInterceptors(InterceptorRegistry registry)
  11. {
  12. //所有已api開頭的訪問都要進入RedisSessionInterceptor攔截器進行登入驗證,並排除login介面(全路徑)。必須寫成鏈式,分別設定的話會建立多個攔截器。
  13. //必須寫成getSessionInterceptor(),否則SessionInterceptor中的@Autowired會無效
  14. registry.addInterceptor(getSessionInterceptor()).addPathPatterns("/api/**").excludePathPatterns("/api/user/login");
  15. super.addInterceptors(registry);
  16. }
  17. }

五、登入控制器

  1. @RestController
  2. @RequestMapping(value = "/api/user")
  3. public class LoginController
  4. {
  5. @Autowired
  6. private UserService userService;
  7. @Autowired
  8. private StringRedisTemplate redisTemplate;
  9. @RequestMapping("/login")
  10. public ReturnData login(HttpServletRequest request, String account, String password)
  11. {
  12. User user = userService.findUserByAccountAndPassword(account, password);
  13. if (user != null)
  14. {
  15. HttpSession session = request.getSession();
  16. session.setAttribute("loginUserId", user.getUserId());
  17. redisTemplate.opsForValue().set("loginUser:" + user.getUserId(), session.getId());
  18. return new ReturnData(StatusCode.REQUEST_SUCCESS, user, "登入成功!");
  19. }
  20. else
  21. {
  22. throw new MyException(StatusCode.ACCOUNT_OR_PASSWORD_ERROR, "賬戶名或密碼錯誤!");
  23. }
  24. }
  25. @RequestMapping(value = "/getUserInfo")
  26. public ReturnData get(long userId)
  27. {
  28. User user = userService.findUserByUserId(userId);
  29. if (user != null)
  30. {
  31. return new ReturnData(StatusCode.REQUEST_SUCCESS, user, "查詢成功!");
  32. }
  33. else
  34. {
  35. throw new MyException(StatusCode.USER_NOT_EXIST, "使用者不存在!");
  36. }
  37. }
  38. }

六、效果

我在瀏覽器上登入,然後獲取使用者資訊,再在postman上登入相同的賬號,瀏覽器再獲取使用者資訊,就會提示401錯誤了,瀏覽器需要重新登入才能獲取得到使用者資訊,同樣,postman上登入的賬號就失效了。

瀏覽器:

postman:

七、核心原理詳解

分散式session需要解決兩個難點:1、正確配置redis讓springboot把session託管到redis伺服器。2、唯一登入。

1、redis:

redis需要能正確啟動到出現如下效果才證明redis正常配置並啟動

同時還要保證配置正確

  1. @EnableCaching
  2. @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 30)//session過期時間(秒)
  3. @Configuration
  4. public class RedisSessionConfig
  5. {
  6. @Bean
  7. public static ConfigureRedisAction configureRedisAction()
  8. {
  9. //讓springSession不再執行config命令
  10. return ConfigureRedisAction.NO_OP;
  11. }
  12. }

springboot啟動後能在redis上查到快取的session才能說明整個redis+springboot配置成功!

2、唯一登入:

1、使用者登入時,在redis中記錄該userId對應的sessionId,並將userId儲存到session中。

  1. HttpSession session = request.getSession();
  2. session.setAttribute("loginUserId", user.getUserId());
  3. redisTemplate.opsForValue().set("loginUser:" + user.getUserId(), session.getId());

2、訪問介面時,會在RedisSessionInterceptor攔截器中的preHandle()中捕獲,然後根據該請求發起者的session中儲存的userId去redis查當前已登入的sessionId,若查到的sessionId與訪問者的sessionId相等,那麼說明請求合法,放行。否則丟擲401異常給全域性異常捕獲器去返回給客戶端401狀態。

唯一登入經過我的驗證後滿足需求,暫時沒有出現問題,也希望大家能看看有沒有問題,有的話給我點好的建議!