SpringBoot+SpringSession+Redis實現session共享及唯一登入
阿新 • • 發佈:2020-08-01
轉載:https://blog.csdn.net/xjj1040249553/article/details/82658889
一、pom.xml配置
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.springframework.session</groupId
> - <artifactId>spring-session-data-redis</artifactId>
- </dependency>
二、application.properties的redis配置
- #redis
- spring.redis.host=127.0.0.1
- spring.redis.port=6379
- spring.redis.password=123456
- spring.redis.pool.max-idle=8
- spring.redis.pool.min-idle=0
- spring.redis.pool.max-active=8
- spring.redis.pool.max-wait
=-1 - #超時一定要大於0
- spring.redis.timeout=3000
- spring.session.store-type=redis
在配置redis時需要確保redis安裝正確,並且配置notify-keyspace-events Egx,spring.redis.timeout設定為大於0,我當時這裡配置為0時springboot時啟不起來。
三、編寫登入狀態攔截器RedisSessionInterceptor
- //攔截登入失效的請求
- public class RedisSessionInterceptor implements HandlerInterceptor
- {
- @Autowired
- private
StringRedisTemplate redisTemplate; -
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
- {
- //無論訪問的地址是不是正確的,都進行登入驗證,登入成功後的訪問再進行分發,404的訪問自然會進入到錯誤控制器中
- HttpSession session = request.getSession();
- if (session.getAttribute("loginUserId") != null)
- {
- try
- {
- //驗證當前請求的session是否是已登入的session
- String loginSessionId = redisTemplate.opsForValue().get("loginUser:" + (long) session.getAttribute("loginUserId"));
- if (loginSessionId != null && loginSessionId.equals(session.getId()))
- {
- return true;
- }
- }
- catch (Exception e)
- {
- e.printStackTrace();
- }
- }
-
- response401(response);
- return false;
- }
-
- private void response401(HttpServletResponse response)
- {
- response.setCharacterEncoding("UTF-8");
- response.setContentType("application/json; charset=utf-8");
-
- try
- {
- response.getWriter().print(JSON.toJSONString(new ReturnData(StatusCode.NEED_LOGIN, "", "使用者未登入!")));
- }
- catch (IOException e)
- {
- e.printStackTrace();
- }
- }
-
- @Override
- public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception
- {
-
- }
-
- @Override
- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception
- {
-
- }
- }
四、配置攔截器
- @Configuration
- public class WebSecurityConfig extends WebMvcConfigurerAdapter
- {
- @Bean
- public RedisSessionInterceptor getSessionInterceptor()
- {
- return new RedisSessionInterceptor();
- }
-
- @Override
- public void addInterceptors(InterceptorRegistry registry)
- {
- //所有已api開頭的訪問都要進入RedisSessionInterceptor攔截器進行登入驗證,並排除login介面(全路徑)。必須寫成鏈式,分別設定的話會建立多個攔截器。
- //必須寫成getSessionInterceptor(),否則SessionInterceptor中的@Autowired會無效
- registry.addInterceptor(getSessionInterceptor()).addPathPatterns("/api/**").excludePathPatterns("/api/user/login");
- super.addInterceptors(registry);
- }
- }
五、登入控制器
- @RestController
- @RequestMapping(value = "/api/user")
- public class LoginController
- {
- @Autowired
- private UserService userService;
-
- @Autowired
- private StringRedisTemplate redisTemplate;
-
- @RequestMapping("/login")
- public ReturnData login(HttpServletRequest request, String account, String password)
- {
- User user = userService.findUserByAccountAndPassword(account, password);
- if (user != null)
- {
- HttpSession session = request.getSession();
- session.setAttribute("loginUserId", user.getUserId());
- redisTemplate.opsForValue().set("loginUser:" + user.getUserId(), session.getId());
-
- return new ReturnData(StatusCode.REQUEST_SUCCESS, user, "登入成功!");
- }
- else
- {
- throw new MyException(StatusCode.ACCOUNT_OR_PASSWORD_ERROR, "賬戶名或密碼錯誤!");
- }
- }
-
- @RequestMapping(value = "/getUserInfo")
- public ReturnData get(long userId)
- {
- User user = userService.findUserByUserId(userId);
- if (user != null)
- {
- return new ReturnData(StatusCode.REQUEST_SUCCESS, user, "查詢成功!");
- }
- else
- {
- throw new MyException(StatusCode.USER_NOT_EXIST, "使用者不存在!");
- }
- }
- }
六、效果
我在瀏覽器上登入,然後獲取使用者資訊,再在postman上登入相同的賬號,瀏覽器再獲取使用者資訊,就會提示401錯誤了,瀏覽器需要重新登入才能獲取得到使用者資訊,同樣,postman上登入的賬號就失效了。
瀏覽器:
postman:
七、核心原理詳解
分散式session需要解決兩個難點:1、正確配置redis讓springboot把session託管到redis伺服器。2、唯一登入。
1、redis:
redis需要能正確啟動到出現如下效果才證明redis正常配置並啟動
同時還要保證配置正確
- @EnableCaching
- @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 30)//session過期時間(秒)
- @Configuration
- public class RedisSessionConfig
- {
- @Bean
- public static ConfigureRedisAction configureRedisAction()
- {
- //讓springSession不再執行config命令
- return ConfigureRedisAction.NO_OP;
- }
- }
springboot啟動後能在redis上查到快取的session才能說明整個redis+springboot配置成功!
2、唯一登入:
1、使用者登入時,在redis中記錄該userId對應的sessionId,並將userId儲存到session中。
- HttpSession session = request.getSession();
- session.setAttribute("loginUserId", user.getUserId());
- redisTemplate.opsForValue().set("loginUser:" + user.getUserId(), session.getId());
2、訪問介面時,會在RedisSessionInterceptor攔截器中的preHandle()中捕獲,然後根據該請求發起者的session中儲存的userId去redis查當前已登入的sessionId,若查到的sessionId與訪問者的sessionId相等,那麼說明請求合法,放行。否則丟擲401異常給全域性異常捕獲器去返回給客戶端401狀態。
唯一登入經過我的驗證後滿足需求,暫時沒有出現問題,也希望大家能看看有沒有問題,有的話給我點好的建議!