初始代码

This commit is contained in:
hhh
2026-04-02 10:38:23 +08:00
parent d8b4140f50
commit aed67ce1fd
1937 changed files with 447678 additions and 1 deletions

View File

@@ -0,0 +1,287 @@
# 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<String, Object> 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. **用户登录**
- 创建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}`
- **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. **安全可控**:双重过期机制,支持主动失效
**核心优势**:在保证安全性的前提下,最大化提升用户体验,避免频繁登录带来的困扰。