主要变更: - 修改 JwtTokenRedisService.validateToken() 方法,移除 token 一致性检查 - 在 JwtAuthenticationFilter 中新增 isAccountActive() 方法,每次请求验证账户状态 - 所有状态判断改为忽略大小写 (equalsIgnoreCase) - 保留黑名单机制用于主动踢人、登出等场景 功能特性: - 同一账号可以在多个设备/浏览器同时登录 - 各个登录状态的 token 都有效,不会互踢下线 - 支持所有角色:admin, school, teacher, parent - JWT token 过期时间(默认 24 小时)保证安全性 修改文件: - JwtTokenRedisService.java - JwtAuthenticationFilter.java - AuthServiceImpl.java 文档更新: - docs/CHANGELOG.md - docs/dev-logs/2026-03-17.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
146 lines
4.6 KiB
Markdown
146 lines
4.6 KiB
Markdown
# 开发日志 - 2026-03-17
|
||
|
||
## 多地点登录支持实现
|
||
|
||
### 修改内容
|
||
|
||
#### 1. JwtTokenRedisService.java
|
||
|
||
**文件路径**: `reading-platform-java/src/main/java/com/reading/platform/common/security/JwtTokenRedisService.java`
|
||
|
||
**修改内容**:
|
||
- 修改 `validateToken()` 方法,移除了 token 一致性检查
|
||
- 现在只检查 token 是否在黑名单中
|
||
- 允许同一账号有多个有效的 token,支持多地点同时登录
|
||
|
||
**修改前逻辑**:
|
||
```java
|
||
// 检查是否与存储的 token 一致
|
||
String storedToken = getStoredToken(username);
|
||
if (storedToken == null) {
|
||
return true;
|
||
}
|
||
boolean isValid = token.equals(storedToken);
|
||
return isValid; // 如果不一致则返回 false,导致互踢下线
|
||
```
|
||
|
||
**修改后逻辑**:
|
||
```java
|
||
// 仅检查是否在黑名单中
|
||
if (isBlacklisted(token)) {
|
||
return false;
|
||
}
|
||
// 不再检查是否与存储的 token 一致,允许同一账号有多个有效 token
|
||
return true;
|
||
```
|
||
|
||
#### 2. JwtAuthenticationFilter.java
|
||
|
||
**文件路径**: `reading-platform-java/src/main/java/com/reading/platform/common/security/JwtAuthenticationFilter.java`
|
||
|
||
**修改内容**:
|
||
- 新增依赖注入:`AdminUserMapper`, `TenantMapper`, `TeacherMapper`, `ParentMapper`
|
||
- 在 token 验证通过后,增加账户状态检查
|
||
- 新增 `isAccountActive()` 方法,根据用户角色查询对应表验证账户状态
|
||
|
||
**新增代码**:
|
||
```java
|
||
// 检查账户状态是否为 active
|
||
if (!isAccountActive(payload)) {
|
||
log.debug("Account is not active for user: {}", payload.getUsername());
|
||
sendError(response, HttpStatus.UNAUTHORIZED, "账户已被禁用,请联系管理员");
|
||
return;
|
||
}
|
||
```
|
||
|
||
**新增方法**:
|
||
```java
|
||
private boolean isAccountActive(JwtPayload payload) {
|
||
String role = payload.getRole();
|
||
Long userId = payload.getUserId();
|
||
|
||
return switch (role) {
|
||
case "admin" -> {
|
||
AdminUser adminUser = adminUserMapper.selectById(userId);
|
||
yield adminUser != null && "active".equalsIgnoreCase(adminUser.getStatus());
|
||
}
|
||
case "school" -> {
|
||
Tenant tenant = tenantMapper.selectById(userId);
|
||
yield tenant != null && "active".equalsIgnoreCase(tenant.getStatus());
|
||
}
|
||
case "teacher" -> {
|
||
Teacher teacher = teacherMapper.selectById(userId);
|
||
yield teacher != null && "active".equalsIgnoreCase(teacher.getStatus());
|
||
}
|
||
case "parent" -> {
|
||
Parent parent = parentMapper.selectById(userId);
|
||
yield parent != null && "active".equalsIgnoreCase(parent.getStatus());
|
||
}
|
||
default -> false;
|
||
};
|
||
}
|
||
```
|
||
|
||
#### 3. AuthServiceImpl.java
|
||
|
||
**文件路径**: `reading-platform-java/src/main/java/com/reading/platform/service/impl/AuthServiceImpl.java`
|
||
|
||
**修改内容**:
|
||
- 更新 `logout()` 方法的注释,说明当前实现
|
||
|
||
#### 4. 状态判断忽略大小写修改
|
||
|
||
**修改内容**:
|
||
- 将所有 `"active".equals(status)` 修改为 `"active".equalsIgnoreCase(status)`
|
||
- 确保大小写不敏感的状态判断,如 "Active"、"ACTIVE"、"active" 都被识别为激活状态
|
||
|
||
**修改文件**:
|
||
- `JwtAuthenticationFilter.java` - `isAccountActive()` 方法中的 4 处判断
|
||
- `AuthServiceImpl.java` - `login()` 方法中的 8 处判断(admin、teacher、parent、tenant 各 2 处)
|
||
|
||
### 功能说明
|
||
|
||
#### 多地点登录
|
||
- 同一账号可以在多个设备/浏览器同时登录
|
||
- 各个登录状态的 token 都有效,不会互踢下线
|
||
- JWT token 本身的过期时间(默认 24 小时)保证安全性
|
||
|
||
#### 账户状态验证
|
||
- 每次请求都会验证账户状态是否为 "active"
|
||
- 如果管理员在后台禁用某个账号,该账号的所有已登录会话将立即失效
|
||
- 支持所有角色:admin、school、teacher、parent
|
||
|
||
#### 黑名单机制
|
||
- 黑名单机制仍然有效
|
||
- 可以用于主动使特定 token 失效(如踢人下线场景)
|
||
|
||
### 验证步骤
|
||
|
||
1. **多地点登录测试**:
|
||
- 使用同一账号在两个不同的浏览器登录
|
||
- 在两个浏览器都发起 API 请求
|
||
- 预期:两个登录都保持有效
|
||
|
||
2. **账户状态禁用测试**:
|
||
- 使用账号 A 在浏览器 1 登录
|
||
- 在超管后台将账号 A 的状态修改为"非激活"
|
||
- 在浏览器 1 再次发起请求,应返回"账户已被禁用"
|
||
|
||
### 安全性考虑
|
||
|
||
1. JWT token 有过期时间(默认 24 小时),过期后自动失效
|
||
2. 黑名单机制仍然有效,可以主动使特定 token 失效
|
||
3. 每次请求都会验证账户状态,确保禁用账号无法访问
|
||
|
||
### 影响范围
|
||
|
||
- 所有角色的登录认证流程
|
||
- Token 验证流程
|
||
- 账户状态检查
|
||
|
||
### 兼容性
|
||
|
||
- 此修改不影响现有功能
|
||
- 登出、黑名单等功能仍然正常工作
|
||
- 前端无需修改
|