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);
|
}
|
}
|