JAVA开发(token过期续期)

本文介绍了一种通过refresh_token自动刷新token的方法,避免了因token过期而导致的频繁重新登录现象。同时提供了Java环境下JWTUtil工具类的实现源码,并展示了如何在后端通过过滤器验证token的有效性。

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

一、需求背景:

        在使用token进行登录的过程中,如果token过期了,需要重新输入用户名和密码登录,这种体验肯定是不好的,因为如果一直在使用系统,系统应该一直能够保持登录状态,而不是用着用着就突然退出系统重新登录。

二、解决方法:

生成token的接口需要提供,token和刷新的refresh_token ,过期时间。当时间快过期时,重新调用后端接口去获取新的token。

token:包含了凭证相关信息(过期时间相对refresh_token短)

refresh_token:包含凭证相关信息(过期时间相对token长)

过期时间:给客户端计算何时需要使用refresh_token来换取新的token

生成token参照,里面在加上 refresh_token和过期时间就可以。

JAVA开发(JWTUtil工具类实现token源码赏析)_茅河野人的博客-CSDN博客

三、后端验证token的示例:

既可以通过过滤器,也可以使用拦截器来拦截。

import javax.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.gateway.config.properties.IgnoreWhiteProperties;
import reactor.core.publisher.Mono;

/**
 * 网关鉴权
 */
@Component
public class AuthFilter implements GlobalFilter, Ordered
{
    private static final Logger log = LoggerFactory.getLogger(AuthFilter.class);
    
    private final static long EXPIRE_TIME = Constants.TOKEN_EXPIRE * 60;

    // 排除过滤的 uri 地址,nacos自行添加
    @Autowired
    private IgnoreWhiteProperties ignoreWhite;

    @Resource(name = "stringRedisTemplate")
    private ValueOperations<String, String> sops;
    
    @Autowired
    private RedisService redisService;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
    {
        String url = exchange.getRequest().getURI().getPath();
        // 跳过不需要验证的路径
        if (StringUtils.matches(url, ignoreWhite.getWhites()))
        {
            return chain.filter(exchange);
        }
        String token = getToken(exchange.getRequest());
        if (StringUtils.isBlank(token))
        {
            return setUnauthorizedResponse(exchange, "令牌不能为空");
        }
        String userStr = sops.get(getTokenKey(token));
        if (StringUtils.isNull(userStr))
        {
            return setUnauthorizedResponse(exchange, "登录状态已过期");
        }
        JSONObject obj = JSONObject.parseObject(userStr);
        String userid = obj.getString("userid");
        String username = obj.getString("username");
        if (StringUtils.isBlank(userid) || StringUtils.isBlank(username))
        {
            return setUnauthorizedResponse(exchange, "令牌验证失败");
        }
        
        // 设置过期时间
        redisService.expire(getTokenKey(token), EXPIRE_TIME);
        // 设置用户信息到请求
        ServerHttpRequest mutableReq = exchange.getRequest().mutate().header(CacheConstants.DETAILS_USER_ID, userid)
                .header(CacheConstants.DETAILS_USERNAME, username).build();
        ServerWebExchange mutableExchange = exchange.mutate().request(mutableReq).build();

        return chain.filter(mutableExchange);
    }

    private Mono<Void> setUnauthorizedResponse(ServerWebExchange exchange, String msg)
    {
        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        response.setStatusCode(HttpStatus.OK);

        log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());

        return response.writeWith(Mono.fromSupplier(() -> {
            DataBufferFactory bufferFactory = response.bufferFactory();
            return bufferFactory.wrap(JSON.toJSONBytes(R.fail(msg)));
        }));
    }

    private String getTokenKey(String token)
    {
        return CacheConstants.LOGIN_TOKEN_KEY + token;
    }

    /**
     * 获取请求token
     */
    private String getToken(ServerHttpRequest request)
    {
        String token = request.getHeaders().getFirst(CacheConstants.HEADER);
        if (StringUtils.isNotEmpty(token) && token.startsWith(CacheConstants.TOKEN_PREFIX))
        {
            token = token.replace(CacheConstants.TOKEN_PREFIX, "");
        }
        return token;
    }

    @Override
    public int getOrder()
    {
        return -200;
    }
}

 

<think>好的,用户现在想了解微信小程序中token过期后如何实现自动续期的解决方案。我需要结合之前的对话内容和提供的引用资料来回答这个问题。 首先,回顾之前的对话,用户之前询问微信小程序是否需要token,我解释了token的必要性以及相关实现。现在用户进一步询问token过期后的自动续期方法,需要给出具体的方案。 接下来,参考用户提供的四个引用,特别是引用[1]和[2]提到了JWT自动续期的方法,比如双token机制和更新token的策略。引用[3]和[4]则涉及微信小程序和uni-app中的具体代码实现,比如在app.js中验证token,以及拦截请求重新获取token后继续原来的请求。 用户的需求是自动续期,也就是在token过期时,无需用户手动登录,系统自动刷新token并继续之前的操作。需要确保这个过程的平滑性和安全性。 首先,我应该考虑常见的自动续期方案,比如双token机制(access token和refresh token)。当access token过期时,用refresh token去获取新的access token。如果refresh token过期,才需要用户重新登录。这种方法在引用[1]和[2]都有提到,是可行的。 然后,在微信小程序中的具体实现步骤可能需要结合小程序的特性。例如,在请求接口时拦截401错误,检测到token过期后,调用刷新token的接口,获取新的token并重新发起之前的请求。引用[4]中的uni-app示例展示了如何通过拦截请求并在token过期后重新登录再请求,这可以借鉴到微信小程序的实现中。 另外,需要考虑存储token的方式,比如使用wx.setStorageSync存储,并在需要时获取。引用[3]中的代码示例提到了在app.js中初始化时验证token,这可能是在应用启动时检查token的有效性,但自动续期更多是在请求过程中处理。 还需要注意安全性的问题,比如refresh token的有效期较长,需要安全存储,避免泄露。同时,在刷新token时,要确保旧的token失效,防止被重复使用,引用[2]中提到更新密码时需要重新生成token,同理刷新时也应使旧token失效。 另外,错误处理机制也很重要。例如,在刷新token失败时,应跳转到登录页面让用户重新登录。同时,在并发请求时,可能会出现多个请求同时检测到token过期的情况,需要防止多次刷新token,可以通过加锁机制处理,确保只刷新一次。 最后,给出一个具体的实现方案,包括双token机制、请求拦截、错误处理、并发控制等步骤,并附上代码示例,参考用户提供的引用中的代码结构,比如使用wx.request的拦截器或者在全局的请求方法中处理。同时,可以提到使用本地存储来管理token,如wx.setStorageSync和wx.getStorageSync。 总结来说,回答需要涵盖方案设计、具体实现步骤、代码示例、注意事项,以及相关的问题扩展,帮助用户全面理解自动续期的实现方法。</think>在微信小程序中实现token自动续期,需要结合**双token机制**和**请求拦截策略**,以下是具体实现方案与技术细节: --- ### 一、核心设计原理 1. **双token机制**: - $access\_token$:短期有效(如2小时),用于接口鉴权 - $refresh\_token$:长期有效(如7天),专门用于刷新$access\_token$ 2. **过期检测逻辑**: - 当接口返回$401$状态码时触发续期流程[^1] - 服务端需在响应头中返回明确的token过期标识(如`Token-Expired: true`) --- ### 二、具体实现步骤 #### 步骤1:全局请求拦截器 ```javascript // utils/http.js let isRefreshing = false; // 防止并发刷新 const requestQueue = []; // 等待队列 const request = (options) => { return new Promise((resolve, reject) => { const _request = () => { options.header = { 'Authorization': `Bearer ${wx.getStorageSync('access_token')}`, ...options.header }; wx.request({ ...options, success: (res) => { if (res.statusCode === 401) { if (!isRefreshing) { isRefreshing = true; refreshToken().then(newToken => { options.header.Authorization = `Bearer ${newToken}`; requestQueue.forEach(cb => cb(newToken)); requestQueue.length = 0; return _request(); // 重试原始请求 }).catch(() => { wx.navigateTo({ url: '/pages/login/login' }); }).finally(() => { isRefreshing = false; }); } else { return new Promise(resolve => { requestQueue.push((newToken) => { options.header.Authorization = `Bearer ${newToken}`; resolve(_request()); }); }); } } else { resolve(res); } }, fail: reject }); }; _request(); }); }; ``` #### 步骤2:刷新token接口 ```javascript // api/auth.js const refreshToken = () => { return new Promise((resolve, reject) => { wx.request({ url: 'https://api.example.com/refresh', method: 'POST', header: { 'Authorization': `Bearer ${wx.getStorageSync('refresh_token')}` }, success: (res) => { if (res.data.code === 0) { wx.setStorageSync('access_token', res.data.access_token); wx.setStorageSync('refresh_token', res.data.refresh_token); resolve(res.data.access_token); } else { reject(); } }, fail: reject }); }); }; ``` --- ### 三、安全增强措施 1. **refresh_token存储**: - 使用`wx.setStorageSync`加密存储(可配合`crypto-js`库加密) 2. **刷新次数限制**: ```java // 服务端示例(JAVA) if(redis.get("refresh_count:"+userId) > 5){ throw new Exception("超过最大刷新次数"); } redis.incr("refresh_count:"+userId); [^2] ``` 3. **新旧token交替策略**: - 刷新成功后使旧$access\_token$立即失效 - 服务端维护`jti`(JWT ID)黑名单[^1] --- ### 四、异常处理流程 ```mermaid graph TD A[发起请求] --> B{返回401?} B -->|是| C[检查refresh_token是否有效] C -->|有效| D[调用refresh接口] C -->|无效| E[跳转登录页面] D --> F{刷新成功?} F -->|是| G[更新本地token并重试请求] F -->|否| E B -->|否| H[正常处理响应] ``` --- ### 五、最佳实践建议 1. **时效设置建议**: - $access\_token$:建议2小时 - $refresh\_token$:建议7-30天(根据安全等级调整)[^3] 2. **主动续期策略**: ```javascript // 定时检查(在app.js中) setInterval(() => { if (Date.now() - getTokenTime() > 1.5*60*60*1000) { refreshToken(); // 提前30分钟刷新 } }, 5*60*1000); [^4] ``` --相关问题-- 1. 如何防止refresh_token被盗用? 2. 微信小程序中如何实现无感登录? 3. 多端登录场景下token管理有哪些注意事项?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奋力向前123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值