• 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状态。

    唯一登录经过我的验证后满足需求,暂时没有出现问题,也希望大家能看看有没有问题,有的话给我点好的建议!

  • 相关阅读:
    【HDOJ】2774 Shuffle
    【POJ】2170 Lattice Animals
    【POJ】1084 Square Destroyer
    【POJ】3523 The Morning after Halloween
    【POJ】3134 Power Calculus
    【Latex】如何在Latex中插入伪代码 —— clrscode3e
    【HDOJ】4801 Pocket Cube 的几种解法和优化
    【HDOJ】4080 Stammering Aliens
    【HDOJ】1800 Flying to the Mars
    SQL语法
  • 原文地址:https://www.cnblogs.com/duende99/p/13413265.html
Copyright © 2020-2023  润新知