前言:
随着Web应用需求的增加,如何保障用户数据和信息的安全,成为了开发者关注的重要问题。传统的单Token认证方法虽然简便,但在长时间使用或高频请求下,可能带来一定的安全隐患。双Token身份认证机制提供了一种更加安全且高效的方式,本文将详细介绍如何在Spring Boot和Vue中实现双Token认证。同时在单token进行操作时,也会遇到token到期而需要频繁登录的问题,使用双token就能很好地解决这个问题!
双Token介绍:
双Token认证机制是基于OAuth2.0标准的一种增强型身份认证方式,主要通过两个Token:访问Token(Access Token) 和 刷新Token(Refresh Token),来分别处理认证与授权问题。
单Token VS 双Token
单Token:用户登录后,返回一个Token,用户每次请求都需要携带此Token进行身份认证。缺点是,Token一旦被盗取或泄露,恶意用户可以长时间访问系统,且Token有效期通常较长,若用户频繁使用系统,Token有效期过长也可能造成不便。
双Token:通过使用两个Token来提高安全性和用户体验。访问Token:短期有效,用于每次请求时验证身份,确保用户认证的实时性。刷新Token:长期有效,用于刷新过期的访问Token,避免频繁登录。
具体流程:
- 用户登录后生成token(过期时间短)和refreshToken(过期时间长),token和refreshToken先返回前端并保存到localstroage中,同时再将refreshToken保存到Redis中
- 前端请求头中携带token进行普通路径的方法调用
- 当token失效,过滤器中发现错误并返回前端
- 前端根据错误请求头携带上refreshToken并调用后端中更新token的方法
- 此时再次进入过滤器中,token还是报错,但是此时能检查到refreshToken,调用更新token的方法
- 在更新token的方法中,先检查当前的refreshToken是否存在与Redis中,若不存在,则说明refreshToken也过期了,用户需要重新登录
- 若Redis中存在refreshToken与当前的refreshToken对应,则说明是当前的短token过期了,重新生成token和refreshToken并返回前端,同时将新refreshToken存到Redis中?
详细操作:
首先这是我们暂时的登录与过滤器中的流程。只实现了单Token的生成及校验。
@Override
public ResponseResult login(User user) {
//1.构造用户名密码认证信息
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(user.getPhoneNumber(), user.getPassword());
//2.使用SpringSecurity 中用于封装用户名密码认证信息的UsernamePasswordAuthenticationToken来进行认证
//这里会进行账号密码校验,不成功会报403
Authentication authenticate = authenticationManager.authenticate(authenticationToken);
//3.认证不通过报错
if(Objects.isNull(authenticate)){
throw new RuntimeException("登录失败");
}
//4.认证通过则生成token
LoginUser loginUser= (LoginUser) authenticate.getPrincipal();
String userId = loginUser.getUser().getId().toString();
String role = loginUser.getUser().getRole();
String token = JwtUtil.createJWT(userId,role);//将userId进行token生成
//4.1如果用户被禁用则无法登录
String userStatus = userService.selectUserStatusByUserId(Long.valueOf(userId));
if(userStatus.equals("banned")){
return ResponseResult.error("您已被禁用,无法登录");
}
//5.封装数据到Redis中
redisCache.setCacheObject(LOGIN_USER_KEY+userId,loginUser,LOGIN_USER_TTL, TimeUnit.MINUTES);
//6.最后将token返回前端
HashMap<String, String> map = n