# 消息通知权限问题修复说明 ## 🚨 严重问题 ### 问题描述 **所有用户(陪伴员、管理师、家长等)都能看到相同的消息列表,而不是只看到自己的消息。** 从截图可以看到: - 显示了8335条未读消息 - 所有消息都是"服务提醒" - 不同用户登录后看到的消息列表完全相同 ### 问题根源 #### 1. JWT拦截器配置错误 **文件:** `peidu/backend/src/main/java/com/peidu/config/WebMvcConfig.java` **问题代码(第47行):** ```java registry.addInterceptor(jwtInterceptor) .addPathPatterns("/api/**") .excludePathPatterns( // ... 其他排除路径 "/api/notification/**", // ⚠️ 消息通知接口被排除在JWT拦截器之外 // ... ); ``` **后果:** 1. 消息通知接口不需要登录就能访问(安全漏洞) 2. JWT拦截器不会解析token 3. request中没有userId属性 4. 所有用户都无法获取到自己的userId #### 2. 默认返回userId=1 **文件:** `peidu/backend/src/main/java/com/peidu/controller/NotificationController.java` **问题代码(第33-40行):** ```java private Long getCurrentUserId(HttpServletRequest request) { Object userId = request.getAttribute("userId"); if (userId != null) { return Long.valueOf(userId.toString()); } // 默认返回测试用户ID return 1L; // ⚠️ 所有用户都返回userId=1 } ``` **后果:** 1. 当无法获取userId时,默认返回1L 2. 所有用户查询的都是userId=1的消息 3. 导致所有用户看到相同的消息列表 ### 数据流程分析 ``` 用户登录 → 获取token → 前端调用 /api/notification/list ↓ ❌ JWT拦截器被跳过(因为路径被排除) ↓ NotificationController.getList() ↓ getCurrentUserId() → request.getAttribute("userId") = null ↓ ❌ 返回默认值 userId = 1L ↓ 查询 notification 表 WHERE user_id = 1 ↓ ❌ 所有用户都看到userId=1的消息 ``` ## ✅ 修复方案 ### 修复1:移除notification接口的JWT拦截器排除 **文件:** `peidu/backend/src/main/java/com/peidu/config/WebMvcConfig.java` **修改前:** ```java .excludePathPatterns( // ... "/api/notification/**", // ⚠️ 错误:排除了消息通知接口 // ... ); ``` **修改后:** ```java .excludePathPatterns( // ... // "/api/notification/**", // ⚠️ 已移除:消息通知需要登录才能访问 // ... ); ``` ### 修复2:getCurrentUserId抛出异常而不是返回默认值 **文件:** `peidu/backend/src/main/java/com/peidu/controller/NotificationController.java` **修改前:** ```java private Long getCurrentUserId(HttpServletRequest request) { Object userId = request.getAttribute("userId"); if (userId != null) { return Long.valueOf(userId.toString()); } // 默认返回测试用户ID return 1L; // ⚠️ 错误:返回默认值 } ``` **修改后:** ```java private Long getCurrentUserId(HttpServletRequest request) { Object userId = request.getAttribute("userId"); if (userId != null) { return Long.valueOf(userId.toString()); } // ⚠️ 不应该返回默认值,应该抛出异常 throw new RuntimeException("无法获取当前用户ID,请先登录"); } ``` ## 🔄 修复后的数据流程 ``` 用户登录 → 获取token → 前端调用 /api/notification/list ↓ ✅ JWT拦截器执行 ↓ 解析token → 获取userId → request.setAttribute("userId", userId) ↓ NotificationController.getList() ↓ getCurrentUserId() → request.getAttribute("userId") = 实际的userId ↓ ✅ 返回当前登录用户的userId ↓ 查询 notification 表 WHERE user_id = 当前用户的userId ↓ ✅ 每个用户只看到自己的消息 ``` ## 📝 测试步骤 ### 步骤1:重启后端服务 修改了拦截器配置,需要重启后端服务才能生效。 ### 步骤2:清除前端缓存 ```bash cd peidu/uniapp npm run dev:mp-weixin ``` ### 步骤3:测试不同用户 #### 测试陪伴员A 1. 使用陪伴员A的账号登录 2. 进入"消息通知"页面 3. 查看消息列表 4. 记录消息数量和内容 #### 测试陪伴员B 1. 使用陪伴员B的账号登录 2. 进入"消息通知"页面 3. 查看消息列表 4. 记录消息数量和内容 #### 测试管理师 1. 使用管理师账号登录 2. 进入"消息通知"页面 3. 查看消息列表 4. 记录消息数量和内容 ### 步骤4:验证结果 **预期结果:** - ✅ 不同用户看到的消息列表不同 - ✅ 每个用户只看到发送给自己的消息 - ✅ 未读数量显示正确 - ✅ 消息内容显示正确 **如果出现"未登录"错误:** - 检查前端是否正确发送了token - 检查token是否过期 - 重新登录获取新token ## 🔍 数据验证SQL 运行以下SQL检查数据库中的消息分布: ```sql -- 1. 查看各用户的消息数量分布 SELECT user_id, COUNT(*) as total_count, SUM(CASE WHEN is_read = 0 THEN 1 ELSE 0 END) as unread_count, MAX(create_time) as last_notification_time FROM notification WHERE deleted = 0 GROUP BY user_id ORDER BY user_id; -- 2. 查看userId=1的消息(之前所有用户都看到的) SELECT id, user_id, title, content, type, is_read, create_time FROM notification WHERE user_id = 1 AND deleted = 0 ORDER BY create_time DESC LIMIT 20; -- 3. 查看最近创建的消息 SELECT id, user_id, title, content, type, is_read, create_time FROM notification WHERE deleted = 0 ORDER BY create_time DESC LIMIT 50; -- 4. 统计各类型消息的数量 SELECT type, COUNT(*) as count, SUM(CASE WHEN is_read = 0 THEN 1 ELSE 0 END) as unread_count FROM notification WHERE deleted = 0 GROUP BY type ORDER BY count DESC; ``` ## ⚠️ 其他需要检查的控制器 以下控制器也使用了类似的`getCurrentUserId()`方法,需要检查是否有相同的问题: 1. `CheckInRecordController` - 签到记录 2. `ManagerApplicationController` - 管理师申请 3. `StudentController` - 学生管理 4. `TeacherController` - 陪伴员管理 5. `WithdrawController` - 提现管理 6. `CalendarController` - 日历管理 **检查要点:** - 这些接口是否被排除在JWT拦截器之外? - getCurrentUserId()是否返回默认值? - 是否存在类似的权限问题? ## 🎯 安全建议 ### 1. 统一的用户身份获取方式 建议创建一个基础控制器类: ```java public abstract class BaseController { protected Long getCurrentUserId(HttpServletRequest request) { Object userId = request.getAttribute("userId"); if (userId == null) { throw new BusinessException(401, "未登录或登录已过期"); } return Long.valueOf(userId.toString()); } protected Long getCurrentUserIdOrNull(HttpServletRequest request) { Object userId = request.getAttribute("userId"); return userId != null ? Long.valueOf(userId.toString()) : null; } } ``` ### 2. 最小权限原则 - 默认所有接口都需要登录 - 只有明确的公开接口才排除JWT拦截器 - 定期审查excludePathPatterns配置 ### 3. 添加日志记录 在关键操作中记录userId: ```java log.info("用户{}查询消息列表,页码:{},类型:{}", userId, page, type); ``` ### 4. 添加单元测试 测试不同用户只能看到自己的消息: ```java @Test public void testUserCanOnlySeeOwnNotifications() { // 测试用户A只能看到自己的消息 // 测试用户B只能看到自己的消息 // 测试用户A看不到用户B的消息 } ``` ## 📊 修改文件清单 | 文件 | 修改内容 | 状态 | |------|---------|------| | `peidu/backend/src/main/java/com/peidu/config/WebMvcConfig.java` | 移除notification接口的JWT拦截器排除 | ✅ 已完成 | | `peidu/backend/src/main/java/com/peidu/controller/NotificationController.java` | getCurrentUserId抛出异常而不是返回默认值 | ✅ 已完成 | | `Archive/[一次性]消息通知权限问题修复说明.md` | 创建修复说明文档 | ✅ 已完成 | --- **修复完成时间:** 2026-01-26 **修复人员:** Kiro AI **严重程度:** 🚨 高危(安全漏洞) **测试状态:** ⚠️ 需要重启后端服务并测试