# 开发日志 - 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 验证流程 - 账户状态检查 ### 兼容性 - 此修改不影响现有功能 - 登出、黑名单等功能仍然正常工作 - 前端无需修改