SpringBoot 集成token实践详解

该博客介绍了如何在SpringBoot应用中集成JWT,包括设置拦截器验证token、实现token自动刷新机制(区分新生和老年token)、定义统一的RESTful响应格式以及全局异常处理。在自动刷新机制中,如果token为老年token,则会返回新的token,过期则抛出异常。代码示例展示了配置、控制器、拦截器、异常处理和工具类的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  token是服务端生成的一串字符串,以作客户端进行请求的令牌,当第一次登陆后,服务器生成一个token便将此token返回给客户端,以后客户端只要带上这个token前来请求数据即可,无需再次带上用户名和密码

一、需求

  1. SpringBoot 集成 JWT(token)

  2. 拦截器自动验证验证 token 是否过期

  3. token 自动刷新(单个 token 刷新机制,保证活跃用户不会掉线)

  4. 标准统一的 RESTFul 返回体数据格式

  5. 异常统一拦截处理

单个 token 刷新机制(介绍):

token 距离发布token 2 个小时内的token为新生token,2-3 个小时的token为老年token

每次请求,前端带上 token,

(1)如果 token 为新 token ,服务器返回原来的 token

(2)如果 token 为老年 token,服务器返回 刷新后的新生token ,

(3)如果 token 为过期 token,服务器返回token过期 状态码 401,,请求失败, 前端重新登录

二、代码
1. 导入依赖
jwt 依赖

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.10.3</version>
</dependency>

2. 配置文件

server:
    port: 8081
spring:
    application:
        name: tokendemo
# token
token:
    privateKey: 'fdasfgdsagaxgsregdfdjyghjfhebfdgwe45ygrfbsdfshfdsag'
    yangToken: 1000000
    oldToken: 3000000000

3. 代码

代码结构如下

  1. AuthWebMvcConfigurer

@Configuration
public class AuthWebMvcConfigurer implements WebMvcConfigurer {
    @Autowired
    AuthHandlerInterceptor authHandlerInterceptor;

    /**
     * 给除了 /login 的接口都配置拦截器,拦截转向到 authHandlerInterceptor
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authHandlerInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/login");
    }
}
  1. TokenTestController
@RestController
public class TokenTestController {
    @Autowired
    TokenUtil tokenUtil;
    /**
     * 使用 /login 请求获得 token, /login 不经过拦截器
     */
    @RequestMapping("/login")
    public String login(){
        return tokenUtil.getToken("靓仔","admin");
    }
    /**
     * 使用 /test-token 测试 token,进过拦截器
     */
    @RequestMapping("/test-token")
    public Map testToken(HttpServletRequest request){
        String token = request.getHeader("token");
        return tokenUtil.parseToken(token);
    }

}
  1. TokenAuthExpiredException
public class TokenAuthExpiredException extends RuntimeException{
}
  1. AuthHandlerInterceptor
@Slf4j
@Component
public class AuthHandlerInterceptor implements HandlerInterceptor {
    @Autowired
    TokenUtil tokenUtil;
    @Value("${token.privateKey}")
    private String privateKey;
    @Value("${token.yangToken}")
    private Long yangToken;
    @Value("${token.oldToken}")
    private Long oldToken;
    /**
     * 权限认证的拦截操作.
     */
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
        log.info("=======进入拦截器========");
        // 如果不是映射到方法直接通过,可以访问资源.
        if (!(object instanceof HandlerMethod)) {
            return true;
        }
        //为空就返回错误
        String token = httpServletRequest.getHeader("token");
        if (null == token || "".equals(token.trim())) {
            return false;
        }
        log.info("==============token:" + token);
        Map<String, String> map = tokenUtil.parseToken(token);
        String userId = map.get("userId");
        String userRole = map.get("userRole");
        long timeOfUse = System.currentTimeMillis() - Long.parseLong(map.get("timeStamp"));
        //1.判断 token 是否过期
        //年轻 token
        if (timeOfUse < yangToken) {
            log.info("年轻 token");
        }
        //老年 token 就刷新 token
        else if (timeOfUse >= yangToken && timeOfUse < oldToken) {
            httpServletResponse.setHeader("token",tokenUtil.getToken(userId,userRole));
        }
        //过期 token 就返回 token 无效.
        else {
            throw new TokenAuthExpiredException();
        }
        //2.角色匹配.
        if ("user".equals(userRole)) {
            log.info("========user账户============");
            return true;
        }
        if ("admin".equals(userRole)) {
            log.info("========admin账户============");
            return true;
        }
        return false;
    }

}
  1. GlobalExceptionHandler
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
    /**
     * 用户 token 过期
     * @return
     */
    @ExceptionHandler(value = TokenAuthExpiredException.class)
    @ResponseBody
    public String tokenExpiredExceptionHandler(){
        log.warn("用户 token 过期");
        return "用户 token 过期";
    }
}
  1. TokenUtil
@Component
public class TokenUtil {
    @Value("${token.privateKey}")
    private String privateKey;

    /**
     * 加密token.
     */
    public String getToken(String userId, String userRole) {
        //这个是放到负载payLoad 里面,魔法值可以使用常量类进行封装.
        String token = JWT
                .create()
                .withClaim("userId" ,userId)
                .withClaim("userRole", userRole)
                .withClaim("timeStamp", System.currentTimeMillis())
                .sign(Algorithm.HMAC256(privateKey));
        return token;
    }

    /**
     * 解析token.
     * (优化可以用常量固定魔法值+使用DTO在 mvc 之前传输数据,而不是 map,这里因为篇幅原因就不做了)
     * {
     * "userId": "3412435312",
     * "userRole": "ROLE_USER",
     * "timeStamp": "134143214"
     * }
     */
    public Map<String, String> parseToken(String token) {
        HashMap<String, String> map = new HashMap<>();
        DecodedJWT decodedjwt = JWT.require(Algorithm.HMAC256(privateKey))
                .build().verify(token);
        Claim userId = decodedjwt.getClaim("userId");
        Claim userRole = decodedjwt.getClaim("userRole");
        Claim timeStamp = decodedjwt.getClaim("timeStamp");
        map.put("userId", userId.asString());
        map.put("userRole", userRole.asString());
        map.put("timeStamp", timeStamp.asLong().toString());
        return map;
    }
}

三、测试



1. 获得 token

访问

localhost:8081/login

效果:

image-20210211102129000

2. 测试 token 是否可用

将 1 测试得到的 token 放到 header 里面测试 token是否可用

访问

localhost:8081/test-token

image-20210211102448340

3. 测试 token 过期

测试全局异常拦截类拦截到 TokenAuthExpiredException 异常,然后返回提示。

将过期时间调小,修改 application.yaml 文件,3 秒钟就过期

server:
    port: 8081
spring:
    application:
        name: tokendemo
# token
token:
    privateKey: 'fdasfgdsagaxgsregdfdjyghjfhebfdgwe45ygrfbsdfshfdsag'
    yangToken: 1000
    oldToken: 3000

重启应用测试:image-20210211102718111

Spring Boot Token 是一个用于身份验证和授权的令牌。根据引用,根据发布时间,可以将 token 分为新生 token 和老年 token。新生 token 是距离发布不超过2个小时的 token,而老年 token 是距离发布2-3个小时的 token。 要修改 token 的过期时间,可以根据引用中的说明,在 application.yaml 文件中将过期时间调小。在该文件中找到 token 配置部分,可以看到 privateKey、yangToken 和 oldToken 字段。可以修改 yangToken 和 oldToken 的值来设置不同类型 token 的过期时间。 在 Spring Boot 中使用 token,需要定义 Controller 类来处理请求。根据引用提供的示例代码,需要创建一个 TestController 类,并在其中定义相应的请求处理方法。对于认证登录功能,可以在 login 方法中生成 JWT 字符串作为 token,并将其返回给客户端。 总结起来,Spring Boot Token 是用于身份验证和授权的令牌,可以根据发布时间分为新生 token 和老年 token。要修改 token 的过期时间,可以在 application.yaml 文件中相应字段进行设置。使用 Spring Boot Token 需要定义 Controller 类处理请求,并在相应方法中生成和返回 token。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [SpringBoot 集成token实践详解](https://blog.youkuaiyun.com/jarvan5/article/details/113789133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [springboot中使用token](https://blog.youkuaiyun.com/Cidaren/article/details/118759256)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值