SpringBoot+SpringSession+Redis实现session共享及唯一登录示例

作者:保尔-科查筋 时间:2023-10-07 07:56:17 

最近在学习springboot,session这个点一直困扰了我好久,今天把这些天踩的坑分享出来吧,希望能帮助更多的人。

一、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上登录的账号就失效了。

浏览器:

SpringBoot+SpringSession+Redis实现session共享及唯一登录示例

SpringBoot+SpringSession+Redis实现session共享及唯一登录示例

postman:

SpringBoot+SpringSession+Redis实现session共享及唯一登录示例

七、核心原理详解

分布式session需要解决两个难点:1、正确配置redis让springboot把session托管到redis服务器。2、唯一登录。

1、redis:

redis需要能正确启动到出现如下效果才证明redis正常配置并启动

SpringBoot+SpringSession+Redis实现session共享及唯一登录示例

同时还要保证配置正确


@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配置成功!

SpringBoot+SpringSession+Redis实现session共享及唯一登录示例

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

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

来源:https://blog.csdn.net/xjj1040249553/article/details/82658889

标签:SpringBoot,session共享,唯一登录
0
投稿

猜你喜欢

  • MyBatis全局映射文件实现原理解析

    2021-06-21 17:01:29
  • 如何使用java修改文件所有者及其权限

    2023-11-16 09:35:53
  • SpringBoot整合Redis将对象写入redis的实现

    2023-07-30 14:39:07
  • Android中使用Service实现后台发送邮件功能实例

    2022-01-26 16:56:05
  • Android手动检查并申请权限方法

    2023-08-04 23:14:17
  • Java锁的升级策略 偏向锁 轻量级锁 重量级锁

    2022-11-07 00:50:17
  • Android网络开发中GET与POST请求详解

    2022-05-28 23:23:53
  • Android framework ATMS启动流程

    2022-11-21 10:08:07
  • 详解SpringBoot项目整合Vue做一个完整的用户注册功能

    2022-02-13 21:46:35
  • C# 根据字符串生成二维码的实例代码

    2023-09-16 09:06:50
  • ViewPager实现带引导小圆点与自动跳转的引导界面

    2021-07-11 12:25:18
  • spring-boot-maven-plugin:打包时排除provided依赖问题

    2023-07-18 02:12:11
  • Cocos2d-x入门教程(详细的实例和讲解)

    2023-07-31 17:18:00
  • CountDownLatch和Atomic原子操作类源码解析

    2023-06-07 06:31:52
  • C#加密知识整合 (AES,MD5,RSA,SHA256)

    2023-07-19 09:09:13
  • java实现dijkstra最短路径寻路算法

    2021-10-23 06:29:58
  • Flutter输入框TextField属性及监听事件介绍

    2023-07-09 10:28:51
  • Spring Cloud Gateway入门解读

    2023-03-14 12:01:00
  • Android游戏开发之黑白棋

    2023-05-24 13:39:03
  • Lombok使用@Tolerate实现冲突兼容问题

    2021-06-27 06:50:55
  • asp之家 软件编程 m.aspxhome.com