参考资料

SpringBoot教程(十四) | SpringBoot集成Redis(全网最全)

JWT面试常见问题


代码地址

https://github.com/laolang2016/jwt-study/tree/master/jwt-02

登录与注销

JWT 生成去掉过期时间

首先是 jwt 生成的时候不需要过期时间了

/**
 * 生成 token
 */
public String buildToken(AuthUser authUser) {
    authUser.setUuid(IdUtil.fastSimpleUUID());
    Map<String, Object> claims = Maps.newHashMap();
//        Date iat = new Date();
//        Date exp = new Date(iat.getTime() + tokenProperties.getExpireTime() * 60 * 1000);
    claims.put(GlobalConst.LOGIN_USER_KEY, authUser.getUuid());
    claims.put("username", authUser.getUsername());
    return Jwts.builder()
//                .setIssuedAt(iat) // 签发时间
//                .setExpiration(exp) // 过期时间
            .setClaims(claims) // 自定义数据
            .signWith(SignatureAlgorithm.HS512, tokenProperties.getSecret()).compact();
}

登录时把 Token 存入 Redis

/**
 * 刷新 token
 *
 * @param authUser 用户信息, 将来可以携带一些其他信息, 比如角色信息
 */
private void refreshToken(AuthUser authUser) {
    redisUtil.setCacheObject(getRedisKey(authUser.getUsername()), authUser, tokenProperties.getExpireTime(), 
            TimeUnit.MINUTES);
}

/**
 * 获取 token redis key
 *
 * @param username 用户名
 * @return 前缀
 */
private String getRedisKey(String username) {
    return RedisPrefix.AUTH_TOKEN_PREFIX + username;
}


校验 Token 时判断 Redis 中 Token 是否存在

/**
 * 校验 token
 */
public AuthUser verify(String token) {
    try {
        Claims claims = Jwts.parser().setSigningKey(tokenProperties.getSecret()).parseClaimsJws(token).getBody();
        String username = claims.get("username", String.class);
        AuthUser authUser = redisUtil.getCacheObject(getRedisKey(username));
        if (Objects.isNull(authUser)) {
            throw new AuthBusinessException(AuthBizCode.token_not_exist);
        }
        return authUser;
    } catch (ExpiredJwtException e) {
        throw new AuthBusinessException(AuthBizCode.login_expired);
    } catch (Exception e) {
        if (e instanceof AuthBusinessException) {
            throw e;
        }
        throw new AuthBusinessException(AuthBizCode.error_token);
    }
}

加一个退出接口

controller

/**
 * 退出接口
 */
@GetMapping("logout")
public R<Void> logout() {
    authLogic.logout();
    return R.ok();
}

logic

public void logout() {
    try {
        String token = authUtil.getHeaderToken();
        if (StrUtil.isBlank(token)) {
            log.warn("token 不存在");
            ServletKit.writeJson(response, JSONUtil.toJsonStr(R.doOverdue()));
            return;
        }
        tokenService.removeToken(token);
    } catch (Exception e) {
        log.error("退出接口异常:{}", ExceptionUtils.getMessage(e));
        throw new BusinessException(StatusCodeConst.ERROR);
    }
}

tokenService

/**
 * 删除 token
 *
 * @param token 请求头中的 token
 */
public void removeToken(String token) {
    AuthUser authUser = verify(token);
    redisUtil.del(getRedisKey(authUser.getUsername()));
}

在拦截器中已经调用了刷新 token 的方法

Spring Boot 中 JWT 的一些常见问题_用户名