Files
MES/yawei-mes/.tasks/2026-02-25_token滑动机制.md
2026-04-02 10:39:03 +08:00

7.6 KiB
Raw Permalink Blame History

Token滑动机制技术文档

一、概述

本系统采用基于Redis的Token滑动窗口机制实现用户会话的自动续期功能。当用户在系统中持续活动时Token会自动刷新避免频繁登录提升用户体验。

二、核心配置

2.1 配置文件application.yml

# token配置
token:
    # 令牌自定义标识
    header: Authorization
    # 令牌密钥
    secret: abcdefghijklmnopqrstuvwxyz
    # 令牌有效期默认30分钟单位分钟
    expireTime: 10080

配置说明:

  • header: HTTP请求头中Token的字段名
  • secret: JWT签名密钥
  • expireTime: Token有效期当前配置为10080分钟7天

三、核心实现

3.1 数据模型LoginUser

public class LoginUser implements UserDetails {
    /**
     * 用户唯一标识
     */
    private String token;

    /**
     * 登录时间
     */
    private Long loginTime;

    /**
     * 过期时间
     */
    private Long expireTime;
    
    // ... 其他字段
}

3.2 Token服务TokenService

3.2.1 关键常量

// 令牌有效期(从配置文件读取)
@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

public String createToken(LoginUser loginUser) {
    String token = IdUtils.fastUUID();
    loginUser.setToken(token);
    setUserAgent(loginUser);
    refreshToken(loginUser);  // 初始化Token过期时间

    Map<String, Object> claims = new HashMap<>();
    claims.put(Constants.LOGIN_USER_KEY, token);
    return createToken(claims);
}

3.2.3 验证Token滑动机制核心

/**
 * 验证令牌有效期相差不足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

/**
 * 刷新令牌有效期
 * 
 * @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 获取登录用户

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. 用户登录

    • 创建TokenUUID
    • 设置初始过期时间 = 当前时间 + 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}
  • ValueLoginUser对象序列化
  • 过期时间与Token过期时间一致
  • 优势:分布式环境下共享会话,自动清理过期数据

5.3 双重过期机制

  1. LoginUser.expireTime:业务层面的过期时间判断
  2. Redis TTL:存储层面的自动清理机制

六、配置建议

6.1 生产环境配置

token:
    expireTime: 120  # 2小时

说明

  • 滑动窗口触发阈值固定为20分钟
  • 如果用户在2小时内有任何操作且距离过期不足20分钟会自动续期2小时
  • 如果用户超过2小时无操作Token过期需要重新登录

6.2 开发环境配置

token:
    expireTime: 10080  # 7天

说明

  • 方便开发调试,减少频繁登录
  • 生产环境不建议设置过长

七、安全考虑

7.1 Token安全

  • 使用JWT签名防止Token篡改
  • Token存储在Redis中支持主动失效
  • 支持单点登录控制

7.2 滑动窗口安全

  • 20分钟的滑动窗口阈值平衡用户体验和安全性
  • 即使Token被盗用最长有效期仍受expireTime限制
  • 可通过删除Redis中的Token实现强制下线

八、扩展功能

8.1 积木报表Token验证

// 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 用户代理信息记录

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. 安全可控:双重过期机制,支持主动失效

核心优势:在保证安全性的前提下,最大化提升用户体验,避免频繁登录带来的困扰。