Browse Source

feat(S-cfc-153): admin.article.create RED转GREEN——JwtInterceptor 收口 /api/admin/ 前缀 + create 增加 admin 角色守卫

- AdminArticleCreateContractTest 权限流 3 红转绿(匿名 401 / LOGIN_REQUIRED_PATHS 覆盖 /api/admin/ / 非 admin 拒绝且不落库)
- JwtInterceptor.LOGIN_REQUIRED_PATHS 追加 /api/admin/(B-142-1 整族鉴权门;/api/admin-auth/* 仍走 PUBLIC_PATHS)
- AdminArticleController#create 增加 @RequestAttribute("role") 并拒绝非 admin(参照 #move 惯例)
- 验证:AdminArticleCreateContractTest 6/6 绿;JwtInterceptor 相关 69/69 绿;AdminArticleListContractTest 6/6、AdminReferralContractTest 16/16 无回归
E2E Test Bot 22 giờ trước cách đây
mục cha
commit
45b29e5f8

+ 6 - 4
cfc-backend/src/main/java/com/etotem/cfc/config/JwtInterceptor.java

@@ -140,10 +140,12 @@ private static final String[] PUBLIC_PATHS = {
         // SC-X-22 CF 值获取预估:预估接口必须登录,游客无 token 直接 401,
         // 不泄露规则数值(契约 §7 第 1 行:不展示预估或「登录后可见」)。
         "/api/reward/estimate",
-        // B-142-1(P1 安全):/api/admin/** 整族鉴权门缺失——匿名(userId=-1, role=anonymous)此前被
-        // 放行至 admin 业务 controller,可越权执行审核等敏感操作;现整族要求登录,无 token 直接 401。
-        // (/api/admin-auth/* 登录端点前缀不同且走 PUBLIC_PATHS 例外,不受影响;
-        //   契约:AdminArticleAuditContractTest 权限流-1/2,能力键 admin.article.audit / S-cfc-154)
+// B-142-1(P1 安全):/api/admin/** 整族鉴权门缺失——admin 业务端点(文章 CRUD、活动、
+        // 审核、配置等)此前仅 /api/admin-auth/* 登录端点走 PUBLIC_PATHS,匿名(userId=-1,
+        // role=anonymous)被放行至 controller 可越权执行敏感操作(如 create/audit);现整族强制
+        // 登录,无 token 直接 401,controller 层再按 @RequestAttribute("role") 守卫拒绝非 admin,
+        // 构成双层防线(契约:admin.article.create / S-cfc-153、admin.article.audit / S-cfc-154)。
+        // 注意 /api/admin-auth/* 不匹配此前缀(admin-auth ≠ admin/),仍走 PUBLIC_PATHS,不受影响。
         "/api/admin/"
     };
 

+ 7 - 1
cfc-backend/src/main/java/com/etotem/cfc/controller/admin/AdminArticleController.java

@@ -84,7 +84,13 @@ public class AdminArticleController {
 
     @PostMapping("/create")
     public Result<Map<String, Object>> create(@RequestBody Map<String, Object> body,
-                                   @RequestAttribute("userId") Long adminId) {
+                                   @RequestAttribute("userId") Long adminId,
+                                   @RequestAttribute("role") String role) {
+        // B-142-1:admin 端点角色守卫(参照本控制器 #move 与 AdminReferralController#summary 的「无权限」惯例),
+        // 双保险——拦截器已整族强制登录,此处再拒绝非 admin(如登录的 parent/child)越权建文章。
+        if (!"admin".equals(role)) {
+            return Result.error("无权限");
+        }
         String title = (String) body.get("title");
         String content = (String) body.get("content");