刘光辉
13 小时以前 34981c30a78e8bbd7791131059a9210f9928b62c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package jnpf.bizcommon.audit.controller;
 
import cn.dev33.satoken.same.SaSameUtil;
import jnpf.audit.model.AuditEventDTO;
import jnpf.bizcommon.audit.service.AuditEventService;
import jnpf.bizcommon.audit.service.DataInterfaceSignAuditService;
import jnpf.base.ActionResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
/**
 * 审计事件内部接收端点:只服务 {@link jnpf.audit.AuditEventApi} 的 Feign 调用,
 * 走平台内部鉴权链,不经网关白名单暴露给外部。path 与 AuditEventApi 的 @FeignClient
 * path 保持字面一致(/Internal/audit + /events)。
 *
 * <p><b>写端点鉴权(权威安全边界)</b>:本方法首行的服务侧
 * {@link SaSameUtil#checkCurrentRequestToken()} 是审计写端点的<b>权威安全边界</b>——它拒绝一切
 * 不携带合法 {@code SA-SAME-TOKEN} 的请求,覆盖网关层的<b>所有绕过变体</b>(Discovery Locator
 * 自动路由 {@code /jnpf-audit/**}、大小写变体、多斜杠、white-ip 白名单跳过 block 等),
 * 使本端点<b>只接受携带有效 {@code SA-SAME-TOKEN} 的服务间 Feign 调用</b>:
 * jnpf-common-feign 的 {@code FeignConfig} 请求拦截器对所有 Feign 出站请求自动注入
 * {@code SA-SAME-TOKEN = UserProvider.getInnerAuthToken() = SaSameUtil.getToken()}(已反编译实证);
 * 而网关转发的外部请求只被注入 {@code SA-SAME-TOKEN_GATEWAY}(MvcSecurityConfig),<b>不带
 * {@code SA-SAME-TOKEN}</b>,故 {@code checkCurrentRequestToken()} 读不到 header 即抛
 * {@link cn.dev33.satoken.exception.SameTokenInvalidException}(sa-token 1.44.0 的 same-token 校验异常,
 * 该版本无 {@code SaSameException})拒收——堵住任意登录用户经网关伪造投递审计事件、破坏 append-only 完整性的漏洞。
 * 该校验与请求路径大小写、前缀形态无关,是伪造防线的最终裁定层。
 *
 * <p><b>网关 block-url 是纵深第一层(非权威边界)</b>:router.yaml 的 {@code gateway.block-url}
 * 已覆盖数据面标准路径 {@code /api/audit/Internal/**}(前端/DB 数据接口用)与
 * {@code /jnpf-audit/Internal/**}(Discovery Locator 自动路由标准路径);
 * <b>2026-08-11 审计搬入 jnpf-biz-common 后又多出两条到达路径,已一并补堵</b>:
 * {@code /api/biz/Internal/**}(biz-common 的对外前缀同样 StripPrefix=2,
 * {@code /api/biz/Internal/audit/events} 去掉两段后正是本端点)与
 * {@code /jnpf-biz-common/Internal/**}(Locator 按新服务名生成;原 {@code /jnpf-audit/**}
 * 那条仍须保留,因为 discovery.yaml 里 jnpf-audit 作为别名依然注册)。它是纵深第一层,
 * 按用户决策<b>只覆盖数据面标准路径、不穷尽所有变体</b>:大写/混合大小写变体(如
 * {@code /JNPF-AUDIT/Internal/audit/events})实测在下游 Spring MVC 因 controller 路径映射
 * 大小写敏感直接 404(不达本方法),多斜杠变体被平台"非法请求"过滤器拦截——均无法落库,
 * 兜底全部由上述服务侧 SAME-TOKEN 承担。网关层完整覆盖(全局 {@code /Internal} 屏蔽 +
 * Discovery Locator 收敛)作为独立平台加固项另议。
 *
 * <p><b style="color:red">⚠ 新增任何 /Internal 写端点,必须在方法首行复制
 * {@code SaSameUtil.checkCurrentRequestToken()} 校验</b>——网关 block-url 不保证覆盖新路径,
 * 服务侧 SAME-TOKEN 才是权威边界,漏写即等于对外开放伪造投递面。
 */
@RestController
@RequestMapping("/Internal/audit")
public class AuditInternalController {
    @Autowired
    private AuditEventService auditEventService;
 
    @Autowired
    private DataInterfaceSignAuditService dataInterfaceSignAuditService;
 
    @PostMapping("/events")
    public ActionResult<Integer> record(@RequestBody AuditEventDTO dto) {
        // 权威安全边界:仅放行携带有效 SA-SAME-TOKEN 的服务间 Feign 调用;网关转发的普通登录请求
        // 无此 header,抛 SameTokenInvalidException(映射 AD106)——覆盖一切网关绕过变体(见类注释)
        SaSameUtil.checkCurrentRequestToken();
        int rows = auditEventService.saveIdempotent(dto);
        dataInterfaceSignAuditService.recordRelatedSign(dto);
        return ActionResult.success(rows);
    }
}