zoukankan      html  css  js  c++  java
  • 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状态。

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

  • 相关阅读:
    Django~1
    Python 正则表达式入门(初级篇)
    pytho占位符
    django之基于cookie和装饰器实现用户认证
    form error
    Python之路【第十七篇】:Django【进阶篇 】
    djangoform详解
    django自定义form验证error
    Django CRM客户关系管理系统
    ulimit
  • 原文地址:https://www.cnblogs.com/duende99/p/13413265.html
Copyright © 2011-2022 走看看