# Token滑动机制技术文档 ## 一、概述 本系统采用基于Redis的Token滑动窗口机制,实现用户会话的自动续期功能。当用户在系统中持续活动时,Token会自动刷新,避免频繁登录,提升用户体验。 ## 二、核心配置 ### 2.1 配置文件(application.yml) ```yaml # token配置 token: # 令牌自定义标识 header: Authorization # 令牌密钥 secret: abcdefghijklmnopqrstuvwxyz # 令牌有效期(默认30分钟,单位:分钟) expireTime: 10080 ``` **配置说明:** - `header`: HTTP请求头中Token的字段名 - `secret`: JWT签名密钥 - `expireTime`: Token有效期,当前配置为10080分钟(7天) ## 三、核心实现 ### 3.1 数据模型(LoginUser) ```java public class LoginUser implements UserDetails { /** * 用户唯一标识 */ private String token; /** * 登录时间 */ private Long loginTime; /** * 过期时间 */ private Long expireTime; // ... 其他字段 } ``` ### 3.2 Token服务(TokenService) #### 3.2.1 关键常量 ```java // 令牌有效期(从配置文件读取) @Value("${token.expireTime}") private int expireTime; // 毫秒常量 protected static final long MILLIS_SECOND = 1000; protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; // 滑动窗口触发阈值:20分钟 private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L; ``` #### 3.2.2 创建Token ```java public String createToken(LoginUser loginUser) { String token = IdUtils.fastUUID(); loginUser.setToken(token); setUserAgent(loginUser); refreshToken(loginUser); // 初始化Token过期时间 Map claims = new HashMap<>(); claims.put(Constants.LOGIN_USER_KEY, token); return createToken(claims); } ``` #### 3.2.3 验证Token(滑动机制核心) ```java /** * 验证令牌有效期,相差不足20分钟,自动刷新缓存 * * @param loginUser */ public void verifyToken(LoginUser loginUser) { long expireTime = loginUser.getExpireTime(); long currentTime = System.currentTimeMillis(); // 如果距离过期时间不足20分钟,触发刷新 if (expireTime - currentTime <= MILLIS_MINUTE_TEN) { refreshToken(loginUser); } } ``` #### 3.2.4 刷新Token ```java /** * 刷新令牌有效期 * * @param loginUser 登录信息 */ public void refreshToken(LoginUser loginUser) { // 更新登录时间为当前时间 loginUser.setLoginTime(System.currentTimeMillis()); // 重新计算过期时间 = 当前时间 + 配置的有效期 loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); // 将更新后的用户信息存入Redis,并设置过期时间 String userKey = getTokenKey(loginUser.getToken()); redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); } ``` #### 3.2.5 获取登录用户 ```java public LoginUser getLoginUser(HttpServletRequest request) { // 获取请求携带的令牌 String token = getToken(request); if (StringUtils.isNotEmpty(token)) { try { Claims claims = parseToken(token); // 解析对应的权限以及用户信息 String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); String userKey = getTokenKey(uuid); LoginUser user = redisCache.getCacheObject(userKey); return user; } catch (Exception e) { // Token解析失败 } } return null; } ``` ## 四、滑动机制工作流程 ### 4.1 流程图 ``` 用户请求 → 获取Token → 从Redis获取LoginUser → 验证Token有效期 ↓ 距离过期 ≤ 20分钟? ↓ ↓ 是 否 ↓ ↓ 刷新Token 继续使用 ↓ 更新过期时间 ↓ 更新Redis缓存 ``` ### 4.2 详细说明 1. **用户登录** - 创建Token(UUID) - 设置初始过期时间 = 当前时间 + expireTime - 存入Redis,设置过期时间 2. **用户请求** - 从请求头获取Token - 从Redis获取LoginUser对象 - 调用`verifyToken()`验证 3. **滑动窗口判断** - 计算剩余有效时间 = expireTime - currentTime - 如果剩余时间 ≤ 20分钟,触发刷新 - 否则,继续使用当前Token 4. **Token刷新** - 更新loginTime为当前时间 - 重新计算expireTime - 更新Redis中的LoginUser对象 ## 五、关键特性 ### 5.1 滑动窗口策略 - **触发条件**:距离过期时间不足20分钟 - **刷新动作**:重置过期时间为当前时间 + expireTime - **优势**:用户持续活动时,Token自动续期,无需重新登录 ### 5.2 Redis存储 - **Key格式**:`login_tokens:{uuid}` - **Value**:LoginUser对象(序列化) - **过期时间**:与Token过期时间一致 - **优势**:分布式环境下共享会话,自动清理过期数据 ### 5.3 双重过期机制 1. **LoginUser.expireTime**:业务层面的过期时间判断 2. **Redis TTL**:存储层面的自动清理机制 ## 六、配置建议 ### 6.1 生产环境配置 ```yaml token: expireTime: 120 # 2小时 ``` **说明**: - 滑动窗口触发阈值固定为20分钟 - 如果用户在2小时内有任何操作,且距离过期不足20分钟,会自动续期2小时 - 如果用户超过2小时无操作,Token过期,需要重新登录 ### 6.2 开发环境配置 ```yaml token: expireTime: 10080 # 7天 ``` **说明**: - 方便开发调试,减少频繁登录 - 生产环境不建议设置过长 ## 七、安全考虑 ### 7.1 Token安全 - 使用JWT签名,防止Token篡改 - Token存储在Redis中,支持主动失效 - 支持单点登录控制 ### 7.2 滑动窗口安全 - 20分钟的滑动窗口阈值,平衡用户体验和安全性 - 即使Token被盗用,最长有效期仍受expireTime限制 - 可通过删除Redis中的Token实现强制下线 ## 八、扩展功能 ### 8.1 积木报表Token验证 ```java // JimuReportTokenService.java public boolean isTokenValid(String token) { LoginUser loginUser = tokenService.getLoginUser(request); if (loginUser != null) { // 检查token是否过期 long expireTime = loginUser.getExpireTime(); long currentTime = System.currentTimeMillis(); return currentTime < expireTime; } return false; } ``` ### 8.2 用户代理信息记录 ```java public void setUserAgent(LoginUser loginUser) { UserAgent userAgent = UserAgent.parseUserAgentString( ServletUtils.getRequest().getHeader("User-Agent") ); String ip = IpUtils.getIpAddr(); loginUser.setIpaddr(ip); loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); loginUser.setBrowser(userAgent.getBrowser().getName()); loginUser.setOs(userAgent.getOperatingSystem().getName()); } ``` ## 九、总结 本系统的Token滑动机制通过以下方式实现了高效的会话管理: 1. **自动续期**:用户活跃时自动延长会话,提升体验 2. **灵活配置**:通过配置文件调整有效期和滑动窗口 3. **分布式支持**:基于Redis实现,支持集群部署 4. **安全可控**:双重过期机制,支持主动失效 **核心优势**:在保证安全性的前提下,最大化提升用户体验,避免频繁登录带来的困扰。