Prechádzať zdrojové kódy

fix(admin): 管理端接口权限加固 - 用户/孩子/任务模板端点校验平台角色

- 新增 requirePlatformRole/requireBackendRole 私有校验方法
- /users /users/create /users/{id} 等 13 个管理端点加 @RequestAttribute role/roles 校验
- 防止匿名或 C 端角色越权调用管理接口
Xiaogang Liao 1 týždeň pred
rodič
commit
d583b0548d

+ 160 - 16
cfc-backend/src/main/java/com/etotem/cfc/controller/admin/AdminController.java

@@ -91,9 +91,63 @@ public class AdminController {
     private static final long LIFETIME_CONSUME_THRESHOLD = 100_000_000L;  // 消费 ≥ 100万元
     private static final long LIFETIME_SALES_THRESHOLD = 200_000_000L;    // 销售 ≥ 200万元
 
+    /**
+     * 校验当前请求是否具备平台角色(admin/operator/finance)。
+     * 用于用户/孩子/任务模板等管理类端点,防止匿名或 C 端角色越权调用。
+     * 返回 null 表示通过,否则返回应直接回传的错误结果。
+     */
+    private Result<?> requirePlatformRole(Object roleObj, Object rolesObj) {
+        String role = roleObj != null ? String.valueOf(roleObj) : null;
+        if ("admin".equals(role)) {
+            return null;
+        }
+        List<String> roles = null;
+        if (rolesObj instanceof List) {
+            roles = (List<String>) rolesObj;
+        } else if (rolesObj instanceof String) {
+            roles = Collections.singletonList((String) rolesObj);
+        } else if (role != null) {
+            roles = Collections.singletonList(role);
+        }
+        if (roles != null && AdminRoles.hasAnyPlatformRole(roles)) {
+            return null;
+        }
+        return Result.error(403, "无权限访问该接口");
+    }
+
+    /**
+     * 校验当前请求是否具备任意后台角色(平台角色或入驻角色,含遗留角色归一化)。
+     * 用于仅登录后台即可调用的查询类端点(如用户下拉搜索),挡住匿名与 C 端角色。
+     * 返回 null 表示通过,否则返回应直接回传的错误结果。
+     */
+    private Result<?> requireBackendRole(Object roleObj, Object rolesObj) {
+        String role = roleObj != null ? String.valueOf(roleObj) : null;
+        if (AdminRoles.isBackendRole(role)) {
+            return null;
+        }
+        List<String> roles = null;
+        if (rolesObj instanceof List) {
+            roles = (List<String>) rolesObj;
+        } else if (rolesObj instanceof String) {
+            roles = Collections.singletonList((String) rolesObj);
+        } else if (role != null) {
+            roles = Collections.singletonList(role);
+        }
+        if (roles != null && AdminRoles.hasAnyBackendRole(roles)) {
+            return null;
+        }
+        return Result.error(403, "无权限访问该接口");
+    }
+
     // 用户管理
     @PostMapping("/users")
-    public Result<Page<User>> getUsers(@RequestBody Map<String, Object> params) {
+    public Result<Page<User>> getUsers(@RequestBody Map<String, Object> params,
+                                       @RequestAttribute(value = "role", required = false) Object roleObj,
+                                       @RequestAttribute(value = "roles", required = false) Object rolesObj) {
+        Result<?> denied = requirePlatformRole(roleObj, rolesObj);
+        if (denied != null) {
+            return (Result<Page<User>>) denied;
+        }
         int page = params.get("page") != null ? ((Number) params.get("page")).intValue() : 1;
         int size = params.get("size") != null ? ((Number) params.get("size")).intValue() : 10;
         Page<User> pageParam = new Page<>(page, size);
@@ -117,7 +171,13 @@ public class AdminController {
     }
 
     @PostMapping("/users/create")
-    public Result<User> createUser(@RequestBody Map<String, Object> params) {
+    public Result<User> createUser(@RequestBody Map<String, Object> params,
+                                   @RequestAttribute(value = "role", required = false) Object roleObj,
+                                   @RequestAttribute(value = "roles", required = false) Object rolesObj) {
+        Result<?> denied = requirePlatformRole(roleObj, rolesObj);
+        if (denied != null) {
+            return (Result<User>) denied;
+        }
         String phone = (String) params.get("phone");
         String nickname = (String) params.get("nickname");
         String role = (String) params.get("role");
@@ -153,19 +213,37 @@ public class AdminController {
     }
 
     @PostMapping("/users/{id}")
-    public Result<User> getUser(@PathVariable Long id) {
+    public Result<User> getUser(@PathVariable Long id,
+                                @RequestAttribute(value = "role", required = false) Object roleObj,
+                                @RequestAttribute(value = "roles", required = false) Object rolesObj) {
+        Result<?> denied = requirePlatformRole(roleObj, rolesObj);
+        if (denied != null) {
+            return (Result<User>) denied;
+        }
         return Result.success(userMapper.selectById(id));
     }
 
     @PostMapping("/users/{id}/update")
-    public Result<Boolean> updateUser(@PathVariable Long id, @RequestBody User user) {
+    public Result<Boolean> updateUser(@PathVariable Long id, @RequestBody User user,
+                                      @RequestAttribute(value = "role", required = false) Object roleObj,
+                                      @RequestAttribute(value = "roles", required = false) Object rolesObj) {
+        Result<?> denied = requirePlatformRole(roleObj, rolesObj);
+        if (denied != null) {
+            return (Result<Boolean>) denied;
+        }
         user.setId(id);
         userMapper.updateById(user);
         return Result.success(true);
     }
 
     @PostMapping("/users/{id}/reset-password")
-    public Result<Boolean> resetUserPassword(@PathVariable Long id, @RequestBody Map<String, String> params) {
+    public Result<Boolean> resetUserPassword(@PathVariable Long id, @RequestBody Map<String, String> params,
+                                             @RequestAttribute(value = "role", required = false) Object roleObj,
+                                             @RequestAttribute(value = "roles", required = false) Object rolesObj) {
+        Result<?> denied = requirePlatformRole(roleObj, rolesObj);
+        if (denied != null) {
+            return (Result<Boolean>) denied;
+        }
         String newPassword = params.get("password");
         if (newPassword == null || newPassword.length() < 6) {
             return Result.error("密码长度不能少于6位");
@@ -182,7 +260,13 @@ public class AdminController {
      * 支持按 ID 精确 / nickname / phone / realName 模糊搜索,返回列表用于下拉选择
      */
     @PostMapping("/user/search")
-    public Result<List<User>> searchUsers(@RequestBody Map<String, Object> params) {
+    public Result<List<User>> searchUsers(@RequestBody Map<String, Object> params,
+                                          @RequestAttribute(value = "role", required = false) Object roleObj,
+                                          @RequestAttribute(value = "roles", required = false) Object rolesObj) {
+        Result<?> denied = requireBackendRole(roleObj, rolesObj);
+        if (denied != null) {
+            return (Result<List<User>>) denied;
+        }
         String keyword = params.get("keyword") != null ? String.valueOf(params.get("keyword")).trim() : "";
         LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
         if (!keyword.isEmpty()) {
@@ -208,14 +292,26 @@ public class AdminController {
     }
 
     @PostMapping("/users/{id}/delete")
-    public Result<Boolean> deleteUser(@PathVariable Long id) {
+    public Result<Boolean> deleteUser(@PathVariable Long id,
+                                      @RequestAttribute(value = "role", required = false) Object roleObj,
+                                      @RequestAttribute(value = "roles", required = false) Object rolesObj) {
+        Result<?> denied = requirePlatformRole(roleObj, rolesObj);
+        if (denied != null) {
+            return (Result<Boolean>) denied;
+        }
         boolean ok = userService.deleteUser(id);
         return ok ? Result.success(true) : Result.error("用户不存在或删除失败");
     }
 
     // 孩子管理
     @PostMapping("/children")
-    public Result<Page<FamilyMember>> getChildren(@RequestBody Map<String, Object> params) {
+    public Result<Page<FamilyMember>> getChildren(@RequestBody Map<String, Object> params,
+                                                  @RequestAttribute(value = "role", required = false) Object roleObj,
+                                                  @RequestAttribute(value = "roles", required = false) Object rolesObj) {
+        Result<?> denied = requirePlatformRole(roleObj, rolesObj);
+        if (denied != null) {
+            return (Result<Page<FamilyMember>>) denied;
+        }
         int page = params.get("page") != null ? ((Number) params.get("page")).intValue() : 1;
         int size = params.get("size") != null ? ((Number) params.get("size")).intValue() : 10;
         Page<FamilyMember> pageParam = new Page<>(page, size);
@@ -224,12 +320,24 @@ public class AdminController {
     }
 
     @PostMapping("/children/{id}")
-    public Result<FamilyMember> getChild(@PathVariable Long id) {
+    public Result<FamilyMember> getChild(@PathVariable Long id,
+                                         @RequestAttribute(value = "role", required = false) Object roleObj,
+                                         @RequestAttribute(value = "roles", required = false) Object rolesObj) {
+        Result<?> denied = requirePlatformRole(roleObj, rolesObj);
+        if (denied != null) {
+            return (Result<FamilyMember>) denied;
+        }
         return Result.success(familyMemberMapper.selectById(id));
     }
 
     @PostMapping("/children/{id}/update")
-    public Result<Boolean> updateChild(@PathVariable Long id, @RequestBody FamilyMember child) {
+    public Result<Boolean> updateChild(@PathVariable Long id, @RequestBody FamilyMember child,
+                                       @RequestAttribute(value = "role", required = false) Object roleObj,
+                                       @RequestAttribute(value = "roles", required = false) Object rolesObj) {
+        Result<?> denied = requirePlatformRole(roleObj, rolesObj);
+        if (denied != null) {
+            return (Result<Boolean>) denied;
+        }
         child.setId(id);
         familyMemberMapper.updateById(child);
         return Result.success(true);
@@ -238,7 +346,13 @@ public class AdminController {
     @PostMapping("/children/{id}/adjust-points")
     public Result<Map<String, Object>> adjustPoints(
             @PathVariable Long id,
-            @RequestBody Map<String, Object> params) {
+            @RequestBody Map<String, Object> params,
+            @RequestAttribute(value = "role", required = false) Object roleObj,
+            @RequestAttribute(value = "roles", required = false) Object rolesObj) {
+        Result<?> denied = requirePlatformRole(roleObj, rolesObj);
+        if (denied != null) {
+            return (Result<Map<String, Object>>) denied;
+        }
         Integer amount = params.get("amount") != null ? ((Number) params.get("amount")).intValue() : 0;
         String reason = (String) params.get("reason");
 
@@ -847,7 +961,13 @@ public class AdminController {
 
     // 任务模板管理
     @PostMapping("/task-templates")
-    public Result<Page<TaskTemplate>> getTaskTemplates(@RequestBody Map<String, Object> params) {
+    public Result<Page<TaskTemplate>> getTaskTemplates(@RequestBody Map<String, Object> params,
+                                                       @RequestAttribute(value = "role", required = false) Object roleObj,
+                                                       @RequestAttribute(value = "roles", required = false) Object rolesObj) {
+        Result<?> denied = requirePlatformRole(roleObj, rolesObj);
+        if (denied != null) {
+            return (Result<Page<TaskTemplate>>) denied;
+        }
         int page = params.get("page") != null ? ((Number) params.get("page")).intValue() : 1;
         int size = params.get("size") != null ? ((Number) params.get("size")).intValue() : 10;
         String category = (String) params.get("category");
@@ -872,12 +992,24 @@ public class AdminController {
     }
 
     @PostMapping("/task-templates/{id}")
-    public Result<TaskTemplate> getTaskTemplate(@PathVariable Long id) {
+    public Result<TaskTemplate> getTaskTemplate(@PathVariable Long id,
+                                                @RequestAttribute(value = "role", required = false) Object roleObj,
+                                                @RequestAttribute(value = "roles", required = false) Object rolesObj) {
+        Result<?> denied = requirePlatformRole(roleObj, rolesObj);
+        if (denied != null) {
+            return (Result<TaskTemplate>) denied;
+        }
         return Result.success(taskTemplateMapper.selectById(id));
     }
 
     @PostMapping("/task-templates/create")
-    public Result<Long> createTaskTemplate(@RequestBody TaskTemplate template) {
+    public Result<Long> createTaskTemplate(@RequestBody TaskTemplate template,
+                                           @RequestAttribute(value = "role", required = false) Object roleObj,
+                                           @RequestAttribute(value = "roles", required = false) Object rolesObj) {
+        Result<?> denied = requirePlatformRole(roleObj, rolesObj);
+        if (denied != null) {
+            return (Result<Long>) denied;
+        }
         template.setCreatedAt(new Date());
         template.setUpdatedAt(new Date());
         if (template.getIsActive() == null) {
@@ -888,7 +1020,13 @@ public class AdminController {
     }
 
     @PostMapping("/task-templates/{id}/update")
-    public Result<Boolean> updateTaskTemplate(@PathVariable Long id, @RequestBody TaskTemplate template) {
+    public Result<Boolean> updateTaskTemplate(@PathVariable Long id, @RequestBody TaskTemplate template,
+                                              @RequestAttribute(value = "role", required = false) Object roleObj,
+                                              @RequestAttribute(value = "roles", required = false) Object rolesObj) {
+        Result<?> denied = requirePlatformRole(roleObj, rolesObj);
+        if (denied != null) {
+            return (Result<Boolean>) denied;
+        }
         template.setId(id);
         template.setUpdatedAt(new Date());
         taskTemplateMapper.updateById(template);
@@ -896,7 +1034,13 @@ public class AdminController {
     }
 
     @PostMapping("/task-templates/{id}/delete")
-    public Result<Boolean> deleteTaskTemplate(@PathVariable Long id) {
+    public Result<Boolean> deleteTaskTemplate(@PathVariable Long id,
+                                              @RequestAttribute(value = "role", required = false) Object roleObj,
+                                              @RequestAttribute(value = "roles", required = false) Object rolesObj) {
+        Result<?> denied = requirePlatformRole(roleObj, rolesObj);
+        if (denied != null) {
+            return (Result<Boolean>) denied;
+        }
         taskTemplateMapper.deleteById(id);
         return Result.success(true);
     }