Apache License 2.0

项目介绍

BigGG-Guardian/guardian 是一个托管在 GitHub 上的开源项目,主要使用 Java 编写,采用 Apache License 2.0 许可证,目前获得 173 个 Star、45 次 Fork。

仓库全名
BigGG-Guardian/guardian
开源许可证
Apache License 2.0
Star 数
173
Fork 数
45
未关闭 Issue
6

README 摘要

Guardian 轻量级 Spring Boot API 请求层防护框架 防重提交、接口限流、接口幂等、参数自动Trim、慢接口检测、请求链路追踪、IP黑白名单、防重放攻击、接口开关、参数签名、请求加解密 —— 一个 Starter 搞定 API 请求防护。 GitHub · Gitee · Maven Central 功能一览 功能 | Starter | 注解 | YAML | 说明 | 防重复提交 | guardian-repeat-submit-spring-boot-starter | @RepeatSubmit | ✅ | 防止用户重复提交表单/请求 | 接口限流 | guardian-rate-limit-spring-boot-starter | @RateLimit | ✅ | 滑动窗口 + 令牌桶,双算法可选 | 接口幂等 | guardian-idempotent-spring-boot-starter…

完整内容见 GitHub 原始 README 或本页 。

常见问题

guardian 用什么语言开发?

主要使用 Java 编写,采用 Apache License 2.0 许可证,目前有 173 个 Star。

guardian 可以免费商用吗?

仓库采用 Apache License 2.0 许可证,可免费使用与修改,商业使用请遵守许可证条款(例如保留版权声明与许可声明)。

如何获取 guardian 的源码?

可以直接克隆仓库:git clone https://github.com/BigGG-Guardian/guardian.git,默认分支为 master;安装与运行步骤见仓库 README。

guardian 最近还在维护吗?

仓库最近一次更新为 4月16日,当前有 6 个未关闭 Issue;Star 数为 173,可作为社区活跃度的参考。

简要分析

热度分
266
价值分
117
活跃状态
活跃

语言占比

HTML
Java

Release

README

Maven Central Java Spring Boot License Release Stars

Guardian

轻量级 Spring Boot API 请求层防护框架

防重提交、接口限流、接口幂等、参数自动Trim、慢接口检测、请求链路追踪、IP黑白名单、防重放攻击、接口开关、参数签名、请求加解密 —— 一个 Starter 搞定 API 请求防护。

GitHub · Gitee · Maven Central


Guardian 功能全景图


功能一览

功能 Starter 注解 YAML 说明
防重复提交 guardian-repeat-submit-spring-boot-starter @RepeatSubmit ✅ 防止用户重复提交表单/请求
接口限流 guardian-rate-limit-spring-boot-starter @RateLimit ✅ 滑动窗口 + 令牌桶,双算法可选
接口幂等 guardian-idempotent-spring-boot-starter @Idempotent — Token 机制保证接口幂等性,支持结果缓存
参数自动Trim guardian-auto-trim-spring-boot-starter — ✅ 自动去除请求参数首尾空格 + 不可见字符替换
慢接口检测 guardian-slow-api-spring-boot-starter @SlowApiThreshold ✅ 慢接口检测 + Top N 统计 + Actuator 端点
请求链路追踪 guardian-trace-spring-boot-starter — ✅ 自动生成/透传 TraceId,MDC 日志串联,支持跨线程传递、MQ 链路追踪
IP黑白名单 guardian-ip-filter-spring-boot-starter — ✅ 全局黑名单 + URL 绑定白名单,支持精确/通配符/CIDR
防重放攻击 guardian-anti-replay-spring-boot-starter — ✅ Timestamp + Nonce 双重校验,nonce TTL 与 timestamp 窗口解耦
接口开关 guardian-api-switch-spring-boot-starter — ✅ 动态关闭/开启接口
参数签名 guardian-sign-spring-boot-starter @SignVerify ✅ 支持多种签名算法,请求参数签名验证 + 响应结果签名
请求加解密 guardian-encrypt-spring-boot-starter — ✅ 支持 RSA+AES / SM2+SM4 加密,请求解密 + 响应加密

每个功能独立模块、独立 Starter,用哪个引哪个,互不依赖。所有模块的 YAML 配置均支持配置中心动态刷新(Nacos / Apollo 等),无需重启即可生效。


快速开始

所有starter模块引用

<dependency>
    <groupId>io.github.biggg-guardian</groupId>
    <artifactId>guardian-starter-all</artifactId>
    <version>1.10.0</version>
</dependency>

特别说明:请求链路追踪模块若开启RabbitMq/Kafka/RocketMq请求链路追踪,需额外引入对应的依赖(guardian-trace-rabbitmq/guardian-trace-kafka/guardian-trace-rocketmq)

防重复提交

<dependency>
    <groupId>io.github.biggg-guardian</groupId>
    <artifactId>guardian-repeat-submit-spring-boot-starter</artifactId>
    <version>1.10.0</version>
</dependency>
@PostMapping("/submit")
@RepeatSubmit(interval = 10, timeUnit = TimeUnit.SECONDS, message = "订单正在处理,请勿重复提交")
public Result submitOrder(@RequestBody OrderDTO order) {
    return orderService.submit(order);
}

SpEL 动态 Key(v1.9.0+):

  • 通过 spEl 属性使用 SpEL 表达式动态生成防重 Key
  • #参数名 - 从请求参数或请求体中获取
  • #body.参数名 - 显式从请求体中获取
// 仅请求体
@RepeatSubmit(interval = 10, spEl = "#orderId", message = "请勿重复提交")

// 仅请求参数
@GetMapping("/test")
@RepeatSubmit(interval = 10, spEl = "#orderId")

// 组合使用
@RepeatSubmit(interval = 10, spEl = "#userId + ':' + #body.orderId")

YAML 配置:

guardian:
  repeat-submit:
    urls:
      - pattern: /api/order/spel-body
        interval: 10
        key-scope: user
        sp-el: "#orderId"
      - pattern: /api/order/spel-mix
        interval: 10
        key-scope: user
        sp-el: "#userId + ':' + #body.orderId"

接口限流

<dependency>
    <groupId>io.github.biggg-guardian</groupId>
    <artifactId>guardian-rate-limit-spring-boot-starter</artifactId>
    <version>1.10.0</version>
</dependency>

滑动窗口(默认):

@RateLimit(qps = 1, window = 60, windowUnit = TimeUnit.SECONDS, rateLimitScope = RateLimitKeyScope.IP)

令牌桶:

@RateLimit(qps = 5, capacity = 20, algorithm = RateLimitAlgorithm.TOKEN_BUCKET)

SpEL 动态 Key(v1.9.0+):

  • 通过 spEl 属性使用 SpEL 表达式动态生成限流 Key
  • #参数名 - 从请求参数或请求体中获取
  • #body.参数名 - 显式从请求体中获取
// 仅请求体
@RateLimit(qps = 5, spEl = "#orderId", message = "操作过于频繁")

// 仅请求参数
@GetMapping("/search")
@RateLimit(qps = 5, spEl = "#keyword")

// 组合使用
@RateLimit(qps = 5, spEl = "#userId + ':' + #body.productId")

YAML 配置:

guardian:
  rate-limit:
    urls:
      - pattern: /api/product/spel-body
        qps: 5
        rate-limit-scope: global
        sp-el: "#productId"
      - pattern: /api/order/spel-mix
        qps: 5
        rate-limit-scope: global
        sp-el: "#userId + ':' + #body.productId"

或 YAML 批量配置:

guardian:
  rate-limit:
    urls:
      # 滑动窗口
      - pattern: /api/sms/send
        qps: 1
        window: 60
        window-unit: seconds
        rate-limit-scope: ip
      # 令牌桶
      - pattern: /api/seckill/**
        qps: 10
        capacity: 50
        algorithm: token_bucket
        rate-limit-scope: global

接口幂等

<dependency>
    <groupId>io.github.biggg-guardian</groupId>
    <artifactId>guardian-idempotent-spring-boot-starter</artifactId>
    <version>1.10.0</version>
</dependency>

1. 获取 Token:

GET /guardian/idempotent/token?key=order-submit

2. 业务接口携带 Token:

@Idempotent("order-submit")
@PostMapping("/order/submit")
public Result submitOrder(@RequestBody OrderDTO order) {
    return orderService.submit(order);
}

请求头带上 X-Idempotent-Token: {token},首次请求正常处理,重复请求直接拒绝。

参数自动Trim

<dependency>
    <groupId>io.github.biggg-guardian</groupId>
    <artifactId>guardian-auto-trim-spring-boot-starter</artifactId>
    <version>1.10.0</version>
</dependency>

零配置即可使用,所有请求参数(表单 + JSON Body)自动去除首尾空格。可选配置不可见字符替换:

guardian:
  auto-trim:
    exclude-fields:
      - password
      - signature
    character-replacements:
      - from: "\\u200B"
        to: ""

慢接口检测

<dependency>
    <groupId>io.github.biggg-guardian</groupId>
    <artifactId>guardian-slow-api-spring-boot-starter</artifactId>
    <version>1.10.0</version>
</dependency>

零配置即可使用(默认阈值 3000ms),也可通过注解为单个接口自定义阈值:

@SlowApiThreshold(1000)
@GetMapping("/detail")
public Result getDetail(@RequestParam Long id) {
    return detailService.query(id);
}

超过阈值自动打印 WARN 日志,通过 GET /actuator/guardianSlowApi 查看 Top N 慢接口排行。

请求链路追踪

<dependency>
    <groupId>io.github.biggg-guardian</groupId>
    <artifactId>guardian-trace-spring-boot-starter</artifactId>
    <version>1.10.0</version>
</dependency>

零配置即可使用,每个请求自动生成 TraceId 并写入 MDC。只需在 logback pattern 中加入 %X{traceId}:

<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{traceId}] [%thread] %-5level %logger{36} - %msg%n</pattern>

上游服务通过请求头 X-Trace-Id 传递 TraceId,下游自动复用,实现跨服务链路串联。支持跨线程传递(TraceUtils.wrap() / TraceTaskDecorator),@Async、CompletableFuture、手动线程池场景均可保持 traceId 不丢失。支持 MQ 消息链路追踪(RabbitMQ / Kafka / RocketMQ),发送端自动注入 traceId,消费端通过 AOP 自动提取。

IP黑白名单

<dependency>
    <groupId>io.github.biggg-guardian</groupId>
    <artifactId>guardian-ip-filter-spring-boot-starter</artifactId>
    <version>1.10.0</version>
</dependency>
guardian:
  ip-filter:
    enabled: true
    # 全局黑名单(命中直接拒绝所有请求)
    black-list:
      - 192.168.100.100
      - 10.0.0.0/8
    # URL 绑定白名单(仅白名单 IP 可访问指定接口)
    urls:
      - pattern: /admin/**
        white-list:
          - 127.0.0.1
          - 192.168.1.*

匹配优先级:全局黑名单 > URL 绑定白名单 > 放行。IP 规则支持精确匹配、通配符(192.168.1.*)和 CIDR(10.0.0.0/8)。

防重放攻击

<dependency>
    <groupId>io.github.biggg-guardian</groupId>
    <artifactId>guardian-anti-replay-spring-boot-starter</artifactId>
    <version>1.10.0</version>
</dependency>
guardian:
  anti-replay:
    enabled: true
    max-age: 60              # timestamp 有效窗口(秒)
    nonce-ttl: 86400         # nonce 存活时间(秒,默认 24h)
    urls:
      - pattern: /api/payment/**
      - pattern: /api/transfer/**

客户端请求头携带 X-Timestamp(毫秒时间戳)和 X-Nonce(UUID),服务端校验时间戳有效性 + Nonce 唯一性,双重防护拦截重放请求。

关键设计:nonce-ttl 与 max-age 解耦(默认 24h vs 60s),防止攻击者在 Nonce 过期后篡改 Timestamp 重放。搭配 guardian-sign 签名模块时,nonce-ttl 可缩短至与 max-age 相同。

接口开关

<dependency>
    <groupId>io.github.biggg-guardian</groupId>
    <artifactId>guardian-api-switch-spring-boot-starter</artifactId>
    <version>1.10.0</version>
</dependency>
guardian:
  api-switch:
    enabled: true                        # 总开关(默认 true)
    response-mode: json                  # exception / json
    message: "接口暂时关闭,请稍后再试"    # 提示信息(支持 i18n Key)
    log-enabled: true                    # 是否打印拦截日志
    interceptor-order: 500             # 拦截器排序
    disabled-urls:                       # 启动时默认关闭的接口
      - /api-switch/disabled

参数签名

<dependency>
    <groupId>io.github.biggg-guardian</groupId>
    <artifactId>guardian-sign-spring-boot-starter</artifactId>
    <version>1.10.0</version>
</dependency>

使用注解:

// 基本用法
@SignVerify(algorithm = SignAlgorithm.SHA256)
@PostMapping("/submit")
public Result submit(@RequestBody OrderDTO order) {
    return orderService.submit(order);
}

// 自定义错误信息
@SignVerify(algorithm = SignAlgorithm.HMAC_SHA256, signVerifyMessage = "签名验证失败,请检查参数")
@PostMapping("/payment")
public Result payment(@RequestBody PaymentDTO payment) {
    return paymentService.process(payment);
}

配置示例:

guardian:
  sign:
    enabled: true                        # 总开关(默认 true)
    secret-key: your-secret-key          # 签名密钥
    result-sign: true                    # 结果签名开关(默认 false)
    result-advice-order: 100             # 返回值签名 Advice 排序(默认 100)
    response-mode: json                  # exception / json
    log-enabled: true                    # 是否打印拦截日志
    interceptor-order: 4000              # 拦截器排序
    sign-header: X-Sign                  # 签名请求头名称
    timestamp-header: X-Sign-Timestamp   # 时间戳请求头名称
    max-age: 60                          # 时间戳过期时间(秒)
    max-age-unit: seconds                # 时间戳过期时间单位
    missing-timestamp-message: "缺少时间戳"    # 缺少时间戳提示
    missing-sign-message: "缺少参数签名"      # 缺少签名提示
    expired-message: "请求已过期"            # 请求过期提示
    urls:                                # 需要签名验证的 URL 规则
      - pattern: /api/payment/**
        algorithm: hmac_sha256
        sign-verify-message: "签名验证失败"
      - pattern: /api/order/**
        algorithm: md5
        sign-verify-message: "订单接口签名验证失败"

详细使用示例:

  1. 基础签名验证

    • 添加依赖和配置
    • 在需要验证的接口上添加 @SignVerify 注解
    • 客户端需要按照规则生成签名并在请求头中传递
  2. 结果签名

    • 设置 result-sign: true 开启结果签名
    • 响应会包含签名信息,客户端可以验证响应的完整性
    • 可通过 result-advice-order 配置执行顺序(推荐:幂等缓存 200 → 签名 100 → 加密 300)
  3. 多算法支持

    • 支持多种签名算法:base64、md5、sha256、hmac_sha256
    • 可以为不同的接口配置不同的算法
  4. 时间戳验证

    • 自动验证请求时间戳,防止重放攻击
    • 可配置时间戳过期时间
  5. 自定义错误信息

    • 可以为不同的接口配置不同的错误提示信息
    • 支持国际化消息

防重复提交

展开查看完整文档

使用方式

注解(推荐单接口):

@RepeatSubmit(interval = 10, timeUnit = TimeUnit.SECONDS, message = "请勿重复提交")

YAML(推荐批量):

guardian:
  repeat-submit:
    urls:
      - pattern: /api/order/**
        interval: 10
        message: "订单正在处理,请勿重复提交"

全量配置

guardian:
  repeatable-filter-order: -100000       # 请求体缓存过滤器排序(全局共享,仅需配置一次)
  repeat-submit:
    storage: redis                    # redis / local
    key-encrypt: md5                  # none / md5
    response-mode: exception          # exception / json
    log-enabled: false
    interceptor-order: 2000           # 拦截器排序(值越小越先执行)
    exclude-urls:
      - /api/public/**
    urls:
      - pattern: /api/order/submit
        interval: 10
        time-unit: seconds
        key-scope: user
        message: "请勿重复提交"

防重维度

维度 YAML 值 注解值 效果
用户级 user KeyScope.USER 同一用户 + 同一接口 + 同一参数(默认)
IP 级 ip KeyScope.IP 同一 IP + 同一接口 + 同一参数
全局级 global KeyScope.GLOBAL 同一接口 + 同一参数

响应模式

模式 配置值 行为
异常模式 exception(默认) 抛出 RepeatSubmitException,由全局异常处理器捕获
JSON 模式 json 拦截器直接写入 JSON 响应

可观测性

  • 拦截日志:log-enabled: true,前缀 [Guardian-Repeat-Submit]
  • Actuator:GET /actuator/guardianRepeatSubmit
参数 说明 默认值
top 被拦截接口排行返回条数 10

所有参数均为可选,不传参数与原调用方式完全一致。

{
  "totalBlockCount": 128,
  "totalPassCount": 5432,
  "topBlockedApis": {
    "/api/order/submit": 56,
    "/api/sms/send": 42
  }
}

扩展点

Guardian 的核心组件均可替换,注册同类型 Bean 即可覆盖默认实现。

自定义用户上下文(所有模块共享):

@Bean
public UserContext userContext() {
    // 从你的登录体系中获取当前用户 ID
    return () -> SecurityUtils.getCurrentUserId();
}

不实现也能用,框架会自动以 SessionId / IP 作为用户标识。

自定义 Key 生成策略:

public class MyKeyGenerator extends AbstractKeyGenerator {

    public MyKeyGenerator(UserContext userContext, AbstractKeyEncrypt keyEncrypt) {
        super(userContext, keyEncrypt);
    }

    @Override
    protected String buildKey(RepeatSubmitKey key) {
        return key.getServletUri() + ":" + key.getUserId();
    }
}

@Bean
public MyKeyGenerator myKeyGenerator(UserContext userContext, AbstractKeyEncrypt keyEncrypt) {
    return new MyKeyGenerator(userContext, keyEncrypt);
}

自定义 Key 加密策略:

@Bean
public AbstractKeyEncrypt sha256Encrypt() {
    return new AbstractKeyEncrypt() {
        @Override
        public String encrypt(String key) {
            return DigestUtil.sha256Hex(key);
        }
    };
}

自定义存储:

@Bean
public RepeatSubmitStorage myStorage() {
    return new RepeatSubmitStorage() {
        @Override
        public boolean tryAcquire(RepeatSubmitToken token) { /* ... */ }
        @Override
        public void release(RepeatSubmitToken token) { /* ... */ }
    };
}

自定义响应处理器(仅 response-mode: json 时生效):

@Bean
public RepeatSubmitResponseHandler repeatSubmitResponseHandler() {
    return (request, response, code, data, message) -> {
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(JSONUtil.toJsonStr(CommonResult.result(code, data, message)));
    };
}

接口限流

展开查看完整文档

两种算法

滑动窗口(默认) 令牌桶
算法 统计窗口内请求数,超过阈值拒绝 按速率补充令牌,有令牌放行,无令牌拒绝
突发流量 不允许,窗口内严格限制 允许,桶满时可瞬间消耗所有令牌
参数 maxCount = qps × window(秒) 补充速率 = qps / window(秒), capacity = 桶容量
适合场景 精确控速的接口 允许突发的场景(秒杀、验证码)
数据结构 Local: Deque / Redis: ZSET Local: double + synchronized / Redis: HASH

使用方式

注解:

// 滑动窗口:每秒 10 次,全局
@RateLimit(qps = 10)

// 令牌桶:每秒补 5 个,桶容量 20,允许突发 20 次
@RateLimit(qps = 5, capacity = 20, algorithm = RateLimitAlgorithm.TOKEN_BUCKET)

// 令牌桶 + 分钟级:每分钟补 10 个,桶容量 10
@RateLimit(qps = 10, window = 1, windowUnit = TimeUnit.MINUTES, capacity = 10, algorithm = RateLimitAlgorithm.TOKEN_BUCKET)

YAML:

guardian:
  rate-limit:
    urls:
      - pattern: /api/sms/send
        qps: 1
        window: 60
        window-unit: seconds
        rate-limit-scope: ip
      - pattern: /api/seckill/**
        qps: 10
        capacity: 50
        algorithm: token_bucket
        rate-limit-scope: global
        message: "抢购太火爆,请稍后重试"

全量配置

guardian:
  rate-limit:
    enabled: true                     # 总开关
    storage: redis                    # redis / local
    response-mode: exception          # exception / json
    log-enabled: false
    interceptor-order: 1000           # 拦截器排序(值越小越先执行)
    exclude-urls:
      - /api/public/**
    urls:
      - pattern: /api/sms/send
        qps: 1
        window: 60
        window-unit: seconds
        rate-limit-scope: ip
      - pattern: /api/order/**
        qps: 10
        rate-limit-scope: user
      - pattern: /api/seckill/**
        qps: 10
        capacity: 50
        algorithm: token_bucket
        rate-limit-scope: global

注解参数

参数 默认值 说明
qps 10 滑动窗口=QPS,令牌桶=每 window 补充的令牌数
window 1 滑动窗口=窗口跨度,令牌桶=补充周期
windowUnit SECONDS 时间单位
algorithm SLIDING_WINDOW 限流算法:SLIDING_WINDOW / TOKEN_BUCKET
capacity -1 令牌桶容量,≤0 时取 qps 值
rateLimitScope GLOBAL 限流维度
message 请求过于频繁,请稍后再试 提示信息

限流维度

维度 YAML 值 注解值 效果
全局 global GLOBAL 接口维度,不区分用户和 IP(默认)
IP ip IP 同一 IP 独立计数
用户 user USER 同一用户独立计数

可观测性

  • 拦截日志:log-enabled: true,前缀 [Guardian-Rate-Limit]
  • Actuator:GET /actuator/guardianRateLimit
参数 说明 默认值
blockedTop 被拦截接口排行返回条数 10
requestTop 高频请求接口排行返回条数 10
detailsTop 接口维度明细返回条数 10

所有参数均为可选,不传参数与原调用方式完全一致。

{
  "totalRequestCount": 5560,
  "totalPassCount": 5432,
  "totalBlockCount": 128,
  "blockRate": "2.30%",
  "topBlockedApis": { "/api/sms/send": 56 },
  "topRequestApis": { "/api/search": 3200 },
  "apiDetails": {
    "/api/sms/send": { "requests": 200, "passes": 144, "blocks": 56, "blockRate": "28.00%" }
  }
}

扩展点

注册同类型 Bean 即可覆盖默认实现。

自定义用户上下文(与防重模块共享):

@Bean
public UserContext userContext() {
    return () -> SecurityUtils.getCurrentUserId();
}

自定义限流 Key 生成策略:

public class MyRateLimitKeyGenerator extends AbstractRateLimitKeyGenerator {

    public MyRateLimitKeyGenerator(UserContext userContext) {
        super(userContext);
    }

    @Override
    protected String buildKey(RateLimitKey key) {
        return key.getServletUri() + ":" + key.getUserId();
    }
}

@Bean
public MyRateLimitKeyGenerator myRateLimitKeyGenerator(UserContext userContext) {
    return new MyRateLimitKeyGenerator(userContext);
}

自定义存储:

@Bean
public RateLimitStorage myRateLimitStorage() {
    return new RateLimitStorage() {
        @Override
        public boolean tryAcquire(RateLimitToken token) { /* ... */ }
    };
}

自定义响应处理器(仅 response-mode: json 时生效):

@Bean
public RateLimitResponseHandler rateLimitResponseHandler() {
    return (request, response, code, data, message) -> {
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(JSONUtil.toJsonStr(CommonResult.result(code, data, message)));
    };
}

接口幂等

展开查看完整文档

工作流程

  1. 客户端调用 GET /guardian/idempotent/token?key=order-submit 获取一次性 Token
  2. 客户端携带 Token 发起业务请求(Header 或 Param 方式)
  3. 拦截器校验 Token:存在则消费放行,不存在或已消费则拒绝
  4. (可选)开启结果缓存后,重复请求返回首次执行结果而非拒绝

注解参数

参数 默认值 说明
value 必填 接口唯一标识,用于隔离不同接口的 Token
from HEADER Token 来源:HEADER / PARAM(PARAM 模式依次查找 URL 参数、表单字段、JSON Body)
tokenName X-Idempotent-Token Header 名 / URL 参数名 / JSON Body 字段名
message 幂等Token无效或已消费 拒绝时的提示信息

全量配置

guardian:
  repeatable-filter-order: -100000       # 请求体缓存过滤器排序(全局共享,仅需配置一次)
  idempotent:
    enabled: true                     # 总开关
    storage: redis                    # redis / local
    timeout: 300                      # Token 有效期(默认 300)
    time-unit: seconds                # 有效期单位
    response-mode: exception          # exception / json
    log-enabled: false
    interceptor-order: 3000           # 拦截器排序(值越小越先执行)
    result-advice-order: 200          # 返回值缓存 Advice 排序(默认 200)
    token-endpoint: true              # 是否注册内置 Token 获取接口
    result-cache: false               # 是否启用结果缓存
    missing-token-message: "请求缺少幂等Token"  # 缺少 Token 时的提示(支持 i18n Key)

结果缓存

开启 result-cache: true 后,首次请求的返回值会被缓存,重复请求直接返回缓存结果(而非拒绝)。

可观测性

  • 拦截日志:log-enabled: true,前缀 [Guardian-Idempotent]
  • Actuator:GET /actuator/guardianIdempotent
参数 说明 默认值
top 被拦截接口排行返回条数 10

所有参数均为可选,不传参数与原调用方式完全一致。

{
  "totalRequestCount": 1200,
  "totalPassCount": 1100,
  "totalBlockCount": 100,
  "blockRate": "8.33%",
  "topBlockedApis": {
    "/order/submit": 60,
    "/pay/confirm": 40
  }
}

扩展点

自定义 Token 生成器:

@Bean
public IdempotentTokenGenerator idempotentTokenGenerator() {
    return () -> "custom-" + UUID.randomUUID().toString();
}

自定义存储:

@Bean
public IdempotentStorage myIdempotentStorage() {
    return new IdempotentStorage() {
        @Override
        public void save(IdempotentToken token) { /* ... */ }
        @Override
        public boolean tryConsume(String tokenKey) { /* ... */ }
    };
}

自定义响应处理器(仅 response-mode: json 时生效):

@Bean
public IdempotentResponseHandler idempotentResponseHandler() {
    return (request, response, code, data, message) -> {
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(JSONUtil.toJsonStr(CommonResult.result(code, data, message)));
    };
}

参数自动Trim

展开查看完整文档

功能说明

自动去除所有请求参数的首尾空格,同时支持不可见字符替换(如零宽空格、BOM、回车符等从复制粘贴混入的脏字符)。同时作用于表单参数(Query / Form)和 JSON Body。

全量配置

guardian:
  auto-trim:
    enabled: true                      # 总开关(默认 true)
    filter-order: -10000               # Filter 排序(值越小越先执行)
    exclude-fields:                    # 排除字段(表单 + JSON Body 统一生效)
      - password
      - signature
    character-replacements:            # 字符替换规则(先替换后 trim)
      - from: "\\r"                    # 回车符
        to: ""
      - from: "\\u200B"               # 零宽空格
        to: ""
      - from: "\\uFEFF"               # BOM
        to: ""

字符替换规则

character-replacements 支持以下转义格式:

转义写法 实际字符 说明
\\r \r 回车符
\\n \n 换行符
\\t \t 制表符
\\0 \0 空字符
\\uXXXX Unicode 字符 如 \\u200B = 零宽空格

执行顺序:先执行字符替换,再执行 trim。

排除字段

密码、签名等不应被 trim 的字段可加入 exclude-fields,同时作用于表单参数名和 JSON Body 字段名:

guardian:
  auto-trim:
    exclude-fields:
      - password
      - signature

慢接口检测

展开查看完整文档

功能说明

自动检测响应时间超过阈值的接口,打印 WARN 日志并记录统计数据。支持全局阈值 + 注解覆盖,通过 Actuator 端点查看 Top N 排行。

使用方式

零配置:引入 Starter 即可使用,默认阈值 3000ms。

注解自定义阈值:

@SlowApiThreshold(1000)
@GetMapping("/detail")
public Result getDetail(@RequestParam Long id) {
    return detailService.query(id);
}

注解优先级高于全局配置。没有注解的接口使用全局阈值。

全量配置

guardian:
  slow-api:
    enabled: true                      # 总开关(默认 true)
    threshold: 3000                    # 全局阈值(毫秒,默认 3000)
    interceptor-order: -1000           # 拦截器排序(值越小越先执行)
    exclude-urls:                      # 排除规则(白名单,命中直接放行)
      - /api/health
      - /api/public/**

可观测性

日志输出:超过阈值自动打印 WARN 日志,前缀 [Guardian-Slow-Api]:

WARN [Guardian-Slow-Api] @SlowApiThreshold 慢接口检测 | Method=GET | URI=/api/detail | 耗时=3521ms | 阈值=3000ms

Actuator 端点:GET /actuator/guardianSlowApi

参数 说明 默认值
top 慢接口排行返回条数 10
recordTop 每个接口的耗时明细记录条数 5(最大 100)

所有参数均为可选,不传参数与原调用方式完全一致。

{
  "totalSlowCount": 15,
  "topSlowApis": {
    "/api/detail": {
      "count": 8,
      "maxDuration": 5230,
      "recentRecords": [
        { "duration": 5230, "time": "2026-02-09 15:32:10" },
        { "duration": 4100, "time": "2026-02-09 14:20:45" }
      ]
    },
    "/api/export": {
      "count": 7,
      "maxDuration": 12500,
      "recentRecords": [
        { "duration": 12500, "time": "2026-02-09 11:05:22" }
      ]
    }
  }
}

扩展点

自定义慢接口记录器(SPI):

默认使用内存环形缓冲记录慢接口数据,可通过注入自定义 Bean 替换为数据库 / ES 等持久化方案:

@Bean
public SlowApiRecorder slowApiRecorder() {
    return new SlowApiRecorder() {
        @Override
        public void record(SlowApiRecord record) { /* 写入数据库 / ES */ }
        @Override
        public long getTotalSlowCount() { /* ... */ }
        @Override
        public LinkedHashMap<String, Map<String, Object>> getTopSlowApis(int n) { /* ... */ }
        @Override
        public List<SlowApiRecord> getRecords(String uri, int limit) { /* ... */ }
    };
}

请求链路追踪

展开查看完整文档

功能说明

自动为每个请求生成唯一的 TraceId,写入 MDC 和响应头。上游服务通过请求头传递 TraceId,下游自动复用,实现跨服务日志串联。内置跨线程传递工具类,@Async、CompletableFuture、手动线程池等场景均可保持 traceId 不丢失。

使用方式

零配置:引入 Starter 即可使用。

在 Logback 配置中加入 %X{traceId} 即可在日志中打印 TraceId:

<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{traceId}] [%thread] %-5level %logger{36} - %msg%n</pattern>

工作流程

请求进入
  │
  ▼
TraceIdFilter(OncePerRequestFilter)
  ├─ 请求头有 X-Trace-Id → 复用(上游透传)
  └─ 请求头没有 → 自动生成(时分秒 + 10位随机字符串)
  │
  ▼
MDC.put("traceId", traceId)     ← 写入 MDC,日志自动携带
response.setHeader(headerName)  ← 写入响应头,客户端可获取
  │
  ▼
业务执行(同一线程内所有日志都带 traceId)
  │
  ▼
MDC.remove("traceId")           ← 请求结束清理

跨服务链路串联

上游服务调用下游时,把响应头中的 TraceId 传递到下游请求头即可:

服务 A 收到请求 → 生成 traceId=abc123 → 调用服务 B 时带上 X-Trace-Id: abc123
服务 B 收到请求 → 从请求头取出 abc123 → 复用同一个 traceId

同一条链路上所有服务的日志都带 abc123,排查问题时按 TraceId 搜索即可。

跨线程 TraceId 传递

MDC 基于 ThreadLocal,新开子线程时 traceId 会丢失。Guardian 提供以下工具解决:

1. 手动包装 Runnable / Callable

executor.submit(TraceUtils.wrap(() -> {
    // 子线程中 traceId 与父线程一致
    log.info("异步任务执行");
}));

Future<String> future = executor.submit(TraceUtils.wrap(() -> {
    log.info("带返回值的异步任务");
    return "result";
}));

2. 包装 Executor(适用于 CompletableFuture)

Executor tracedExecutor = TraceUtils.wrap(myExecutor);
CompletableFuture.runAsync(() -> {
    log.info("CompletableFuture 中也有 traceId");
}, tracedExecutor);

3. @Async 线程池自动传递

@Bean
public ThreadPoolTaskExecutor asyncExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(10);
    executor.setTaskDecorator(new TraceTaskDecorator());
    executor.initialize();
    return executor;
}

配置 TraceTaskDecorator 后,所有 @Async 任务自动携带父线程的 traceId,无需手动包装。

类名 场景 用法
TraceRunnable 手动提交 Runnable new TraceRunnable(task) 或 TraceUtils.wrap(task)
TraceCallable 手动提交 Callable new TraceCallable<>(task) 或 TraceUtils.wrap(task)
TraceTaskDecorator @Async 线程池 设置到 ThreadPoolTaskExecutor.setTaskDecorator()
TraceUtils 工具类 wrap(Runnable) / wrap(Callable) / wrap(Executor) / getTraceId() / switchTraceId()

MQ 消息链路追踪

Guardian 支持 RabbitMQ、Kafka、RocketMQ 三种消息队列的 TraceId 自动传递,发送端自动注入 traceId,消费端通过 AOP 切面自动提取并写入 MDC。

核心设计:消费端采用 AOP 切面拦截 Listener 方法,不占用框架原生拦截器/Hook 位置,接入方可自由注册自己的拦截器。

注意:AOP 切面从方法参数中提取 traceId,因此 Listener 方法的参数类型必须使用消息原始类型:

  • RabbitMQ:Message(org.springframework.amqp.core.Message),不能用 String
  • Kafka:ConsumerRecord<?, ?>,不能用 String
  • RocketMQ:MessageExt(org.apache.rocketmq.common.message.MessageExt)
RabbitMQ
<dependency>
    <groupId>io.github.biggg-guardian</groupId>
    <artifactId>guardian-trace-rabbitmq</artifactId>
    <version>1.10.0</version>
</dependency>
  • 发送端:通过 MessagePostProcessor 将 traceId 写入消息 Header(additive,不冲突)
  • 消费端:AOP 切面拦截 @RabbitListener / @RabbitHandler 方法,自动从消息 Header 提取 traceId
Kafka
<dependency>
    <groupId>io.github.biggg-guardian</groupId>
    <artifactId>guardian-trace-kafka</artifactId>
    <version>1.10.0</version>
</dependency>

发送端需在 Kafka Producer 配置中注册拦截器:

spring:
  kafka:
    producer:
      properties:
        interceptor.classes: com.sun.guardian.trace.kafka.interceptor.TraceKafkaProducerInterceptor
  • 发送端:通过 ProducerInterceptor 将 traceId 写入消息 Header(不占用 RecordInterceptor)
  • 消费端:AOP 切面拦截 @KafkaListener / @KafkaHandler 方法,自动从消息 Header 提取 traceId
RocketMQ
<dependency>
    <groupId>io.github.biggg-guardian</groupId>
    <artifactId>guardian-trace-rocketmq</artifactId>
    <version>1.10.0</version>
</dependency>
  • 发送端:通过 SendMessageHook 将 traceId 写入消息 UserProperty(支持注册多个 Hook,不冲突)
  • 消费端:AOP 切面拦截 @RocketMQMessageListener 注解的类,自动从消息 UserProperty 提取 traceId
批量消费场景

AOP 切面自动设置第一条消息的 traceId。如需在批量消费中逐条切换 traceId,各模块提供了专用工具类,一行代码即可完成切换:

模块 工具类 用法
RabbitMQ TraceRabbitUtils TraceRabbitUtils.switchTraceId(message)
Kafka TraceKafkaUtils TraceKafkaUtils.switchTraceId(record)
RocketMQ TraceRocketMQUtils TraceRocketMQUtils.switchTraceId(messageExt)

工具类内部自动从消息 Header / UserProperty 中提取 traceId 并切换 MDC,headerName 通过 TraceConfig 动态获取,支持配置中心热更新。

RabbitMQ 批量消费示例:

@RabbitListener(queues = "myQueue", containerFactory = "batchContainerFactory")
public void handleBatch(List<Message> messages) {
    for (Message msg : messages) {
        TraceRabbitUtils.switchTraceId(msg);
        // 业务逻辑...
    }
}

Kafka 批量消费示例:

@KafkaListener(topics = "myTopic", batch = "true")
public void handleBatch(List<ConsumerRecord<String, String>> records) {
    for (ConsumerRecord<String, String> record : records) {
        TraceKafkaUtils.switchTraceId(record);
        // 业务逻辑...
    }
}

RocketMQ 说明:RocketMQ Spring Boot Starter 的 RocketMQListener 本身是逐条回调,AOP 切面会自动为每次调用注入/清理 traceId,通常无需手动切换。如有特殊场景,也可使用 TraceRocketMQUtils.switchTraceId(messageExt)。

全量配置

guardian:
  trace:
    enabled: true                      # 总开关(默认 true)
    filter-order: -30000               # Filter 排序(值越小越先执行,确保最先执行)
    header-name: X-Trace-Id            # 请求头/响应头名称(默认 X-Trace-Id)

IP黑白名单

展开查看完整文档

功能说明

基于 IP 的访问控制,支持两种模式:

  • 全局黑名单:匹配的 IP 拒绝访问所有接口
  • URL 绑定白名单:指定接口仅允许白名单 IP 访问,其余 IP 拒绝

匹配优先级:全局黑名单 > URL 绑定白名单 > 放行

IP 规则支持三种格式:

格式 示例 说明
精确匹配 192.168.1.100 精确匹配单个 IP
通配符 192.168.1.* 匹配整个网段
CIDR 10.0.0.0/8 CIDR 网段匹配

全量配置

guardian:
  ip-filter:
    enabled: true                       # 总开关(默认 false,需显式开启)
    response-mode: json                 # exception / json
    log-enabled: true                   # 是否打印拦截日志(默认 false)
    message: "IP 访问被拒绝"              # 拒绝提示信息(支持 i18n Key)
    filter-order: -20000                # Filter 排序(值越小越先执行)
    black-list:                         # 全局 IP 黑名单
      - 192.168.100.100
      - 10.0.0.0/8
    urls:                               # URL 绑定白名单
      - pattern: /admin/**
        white-list:
          - 127.0.0.1
          - 192.168.1.*
      - pattern: /internal/api/**
        white-list:
          - 10.0.0.0/8

可观测性

  • 拦截日志:log-enabled: true,前缀 [Guardian-Ip-Filter]
  • Actuator:GET /actuator/guardianIpFilter
参数 说明 默认值
blockTop 黑名单拦截 IP 排行返回条数 10
whiteTop 白名单拦截请求排行返回条数 10

所有参数均为可选,不传参数与原调用方式完全一致。

{
  "totalBlackListBlockCount": 56,
  "totalWhiteListBlockCount": 23,
  "topBlackListBlocked": {
    "192.168.100.100": 42,
    "10.1.2.3": 14
  },
  "topWhiteListBlocked": {
    "/admin/dashboard | 172.16.0.5": 15,
    "/internal/api/config | 192.168.2.1": 8
  }
}

扩展点

自定义响应处理器(仅 response-mode: json 时生效):

@Bean
public IpFilterResponseHandler ipFilterResponseHandler() {
    return (request, response, code, data, message) -> {
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(JSONUtil.toJsonStr(CommonResult.result(code, data, message)));
    };
}

防重放攻击

展开查看完整文档

功能说明

防止攻击者截获合法请求后重复发送(Replay Attack)。客户端在请求头中携带时间戳和一次性随机标识(Nonce),服务端进行双重校验:

  1. Timestamp 校验:请求时间戳与服务器时间差超过 max-age 则拒绝(请求过期)
  2. Nonce 校验:Nonce 已存在于存储中则拒绝(重放攻击),不存在则记录并放行

安全设计:Nonce 的存活时间(nonce-ttl,默认 24h)远大于 Timestamp 有效窗口(max-age,默认 60s)。二者解耦可防止攻击者在 Nonce 过期后篡改 Timestamp 重放请求。搭配 guardian-sign 签名模块使用时,签名防止 Timestamp 被篡改,nonce-ttl 可缩短至与 max-age 相同。

校验流程

请求进入 AntiReplayFilter
  │
  ├─ 1. 排除规则匹配(exclude-urls)→ 命中直接放行
  │
  ├─ 2. URL 规则匹配(urls)→ 未命中则放行
  │
  ├─ 3. 取 X-Timestamp,判断与服务器时间差
  │      ↓ 超过 maxAge → 拒绝(请求过期)
  │
  ├─ 4. 取 X-Nonce,判断是否已使用
  │      ↓ Storage 中已存在 → 拒绝(重放攻击)
  │
  ├─ 5. 校验通过
  │      将 Nonce 存入 Storage(TTL = nonceTtl)
  │      放行
  ▼

客户端请求示例

POST /api/payment/submit HTTP/1.1
X-Timestamp: 1708000000000
X-Nonce: a1b2c3d4e5f6g7h8
Content-Type: application/json

{"orderId": "ORD001", "amount": 99.99}

全量配置

guardian:
  anti-replay:
    enabled: true                        # 总开关(默认 true)
    storage: redis                       # redis / local
    max-age: 60                          # timestamp 有效窗口(默认 60)
    max-age-unit: seconds                # timestamp 有效窗口单位(默认秒)
    nonce-ttl: 86400                     # nonce 存活时间(默认 86400 = 24h,必须 >= max-age)
    nonce-ttl-unit: seconds              # nonce 存活时间单位(默认秒)
    timestamp-header: X-Timestamp        # 时间戳请求头名称
    nonce-header: X-Nonce                # Nonce 请求头名称
    response-mode: json                  # exception / json
    log-enabled: true                    # 是否打印拦截日志
    filter-order: -14000                 # Filter 排序
    missing-timestamp-message: "缺少时间戳"   # 提示信息(支持 i18n Key)
    missing-nonce-message: "缺少请求标识"
    expired-message: "请求已过期"
    replay-message: "重复请求"
    urls:                                # 需要防重放的 URL(AntPath)
      - pattern: /api/payment/**
      - pattern: /api/transfer/**
    exclude-urls:                        # 排除规则(白名单,优先级最高)
      - /api/public/**

可观测性

  • 拦截日志:log-enabled: true,前缀 [Guardian-Anti-Replay]
  • Actuator:GET /actuator/guardianAntiReplay
参数 说明 默认值
top 被拦截接口排行返回条数 10

所有参数均为可选,不传参数与原调用方式完全一致。

{
  "totalRequestCount": 5000,
  "totalPassCount": 4980,
  "totalBlockCount": 20,
  "blockRate": "0.40%",
  "topBlockedApis": {
    "/api/payment/submit": 12,
    "/api/transfer/confirm": 8
  }
}

扩展点

自定义 Nonce 存储:

@Bean
public NonceStorage nonceStorage() {
    return new NonceStorage() {
        @Override
        public boolean tryAcquire(String nonce, long nonceTtl, TimeUnit nonceTtlUnit) {
            // 自定义存储逻辑(如 Memcached、数据库等)
        }
    };
}

自定义响应处理器(仅 response-mode: json 时生效):

@Bean
public AntiReplayResponseHandler antiReplayResponseHandler() {
    return (request, response, code, data, message) -> {
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(JSONUtil.toJsonStr(CommonResult.result(code, data, message)));
    };
}

接口开关

展开查看完整文档

功能说明

基于yml/actuator实现接口的关闭/开启控制,接口路径支持AntPath

全量配置

guardian:
  api-switch:
    enabled: true                        # 总开关(默认 true)
    response-mode: json                  # exception / json
    message: "接口暂时关闭,请稍后再试"    # 提示信息(支持 i18n Key)
    log-enabled: true                    # 是否打印拦截日志
    interceptor-order: 500               # 拦截器排序
    disabled-urls:                       # 启动时默认关闭的接口
      - /api-switch/disabled

可观测性

  • 拦截日志:log-enabled: true,前缀 [Guardian-Api-Switch]
  • Actuator:GET /actuator/guardianApiSwitch
{
  "disabledUrls": [
    "/api-switch/disabled"
  ]
}

关闭接口

  • Actuator:POST /actuator/guardianApiSwitch/{urlPattern}

关闭接口需将接口内{urlPattern}替换为实际接口路径,例如:/actuator/guardianApiSwitch/api-switch/disabled

{
  "action": "disabled",
  "urlPattern": "/api-switch/disabled",
  "disabledUrls": [
    "/123",
    "/api-switch/disabled"
  ]
}

开启接口

  • Actuator:DELETE /actuator/guardianApiSwitch/{urlPattern}

开启接口需将接口内{urlPattern}替换为实际接口路径,例如:/actuator/guardianApiSwitch/api-switch/disabled

{
  "action": "enabled",
  "urlPattern": "/api-switch/disabled",
  "disabledUrls": [
    "/123"
  ]
}

扩展点

自定义响应处理器(仅 response-mode: json 时生效):

@Bean
public ApiSwitchResponseHandler apiSwitchResponseHandler() {
    return (request, response, code, data, message) -> {
        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(JSONUtil.toJsonStr(CommonResult.result(code, data, message)));
    };
}

参数签名

展开查看完整文档

功能说明

对接口请求参数进行签名验证,防止参数被篡改。支持多种签名算法,包括 BASE64、MD5、SHA256 和 HMAC-SHA256。同时支持响应结果签名,确保返回数据的完整性。

使用方式

注解:

// 使用默认算法(BASE64)
@SignVerify

// 指定算法
@SignVerify(algorithm = SignAlgorithm.SHA256)

// 自定义错误信息
@SignVerify(algorithm = SignAlgorithm.HMAC_SHA256, signVerifyMessage = "签名验证失败")

YAML 批量配置:

guardian:
  sign:
    urls:
      - pattern: /api/payment/**
        algorithm: hmac_sha256
        sign-verify-message: "支付接口签名验证失败"
      - pattern: /api/order/**
        algorithm: sha256

全量配置

guardian:
  sign:
    enabled: true                        # 总开关(默认 true)
    secret-key: your-secret-key          # 签名密钥
    result-sign: false                   # 结果签名开关(默认 false)
    response-mode: exception             # exception / json
    log-enabled: false                   # 是否打印拦截日志
    interceptor-order: 4000             # 拦截器排序(值越小越先执行)
    sign-header: X-Sign                  # 签名请求头名称
    timestamp-header: X-Sign-Timestamp   # 时间戳请求头名称
    max-age: 60                          # 时间戳过期时间(秒)
    max-age-unit: seconds                # 时间戳过期时间单位
    missing-timestamp-message: "缺少时间戳"    # 缺少时间戳提示(支持 i18n Key)
    missing-sign-message: "缺少参数签名"      # 缺少签名提示(支持 i18n Key)
    expired-message: "请求已过期"            # 请求过期提示(支持 i18n Key)
    urls:                                # 需要签名验证的 URL 规则
      - pattern: /api/payment/**
        algorithm: hmac_sha256
        sign-verify-message: "签名验证失败"

支持的签名算法

算法 YAML 值 注解值 说明
BASE64 base64 SignAlgorithm.BASE64 基于 BASE64 的简单编码
MD5 md5 SignAlgorithm.MD5 MD5 哈希算法
SHA256 sha256 SignAlgorithm.SHA256 SHA256 哈希算法
HMAC-SHA256 hmac_sha256 SignAlgorithm.HMAC_SHA256 基于密钥的 HMAC-SHA256 算法
SM3 sm3 SignAlgorithm.SM3 国密 SM3 哈希算法

签名计算规则

  1. 参数排序:对请求参数(包括 Query 参数和 JSON Body)按字典序排序
  2. 拼接字符串:param1=value1&param2=value2&timestamp={timestamp}&key={secretKey}
  3. 计算签名:使用指定的算法对拼接后的字符串进行计算
  4. 验证签名:将计算结果与请求头中的 X-Sign 进行比较

前端实现参考

前端实现请参考示例项目中的测试页面:

  • 文件位置:guardian-example/src/main/resources/static/sign-test.html
  • 关键依赖:CryptoJS(MD5/SHA256/HMAC-SHA256)、sm-crypto(SM3)

核心实现要点:

  1. BASE64 算法(GET 请求):只使用 Query 参数,不使用 Body
  2. 其他算法(POST 请求):只使用 Body,不使用 Query 参数
  3. 响应验签:后端对整个响应体进行签名,前端需使用完整响应数据验签

响应结果签名

开启 result-sign: true 后,响应结果会自动添加签名头:

  • X-Sign:响应结果的签名
  • X-Sign-Timestamp:签名时的时间戳

特别说明:结果参数签名时会将controller返回的所有信息都进行签名,包括code、message、data等完整的响应结构。例如:

{
  "code": 200,
  "message": "success",
  "data": {
    "request": {
      "name": "guardian",
      "value": "test123"
    },
    "message": "This response is signed"
  },
  "timestamp": 1772534163534
}

签名会基于上述完整的JSON结构进行计算,确保整个响应结果的完整性。

可观测性

  • 拦截日志:log-enabled: true,前缀 [Guardian-Sign]
  • Actuator:GET /actuator/guardianSign
{
  "totalRequestCount": 1000,
  "totalPassCount": 990,
  "totalBlockCount": 10,
  "blockRate": "1.00%",
  "topBlockedApis": {
    "/api/payment/submit": 5,
    "/api/order/create": 3
  }
}

扩展点

自定义签名服务:

@Bean
public SignService customSignService() {
    return new SignService() {
        @Override
        public String sign(SortedMap<String, String> params, String timestamp, String secretKey, SignAlgorithm algorithm) {
            // 自定义签名逻辑
        }
    };
}

自定义响应处理器(仅 response-mode: json 时生效):

@Bean
public SignResponseHandler signResponseHandler() {
    return (request, response, code, data, message) -> {
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(JSONUtil.toJsonStr(CommonResult.result(code, data, message)));
    };
}

请求加解密

展开查看完整文档

功能说明

对接口请求参数进行解密,对响应结果进行加密,确保数据传输的安全性。支持两种加密方案:

  • RSA + AES:RSA 加密 AES 密钥,AES 加密数据
  • SM2 + SM4:国密算法,SM2 加密 SM4 密钥,SM4 加密数据

使用方式

YAML 配置:

guardian:
  # 请求解密配置
  decrypt:
    enabled: true
    key:
      private-key: "your-private-key-base64"    # 解密密钥时使用的私钥
    data:
      data-key-header: "X-Encrypt-Key"          # 数据密钥请求头名称
    urls:
      - pattern: /api/sensitive/**
        key-algorithm: rsa                      # 密钥加密算法:rsa / sm2
        data-algorithm: aes                     # 数据加密算法:aes / sm4

  # 响应加密配置
  encrypt:
    enabled: true
    key:
      public-key: "your-public-key-base64"      # 加密密钥时使用的公钥
    data:
      data-key-header: "X-Encrypt-Key"          # 数据密钥响应头名称
    urls:
      - pattern: /api/sensitive/**
        key-algorithm: rsa
        data-algorithm: aes

全量配置

guardian:
  decrypt:
    enabled: true                               # 总开关(默认 true)
    filter-order: -40000                        # Filter 排序(默认 -40000)
    log-enabled: false                          # 是否打印拦截日志(默认 false)
    response-mode: exception                    # 响应模式:exception / json(默认 exception)
    key:
      private-key: ""                           # 私钥(Base64 格式)
    data:
      param-alias: "encryptParam"               # param 数据加密后别名(默认 encryptParam)
      body-alias: "encryptBody"                 # body 数据加密后别名(默认 encryptBody)
      data-key-header: "X-Encrypt-Key"          # 数据密钥请求头名称(默认 X-Encrypt-Key)
    missing-data-key-header-message: "缺少数据密钥请求头"  # 缺少密钥头提示
    urls:                                       # 需要解密的 URL 规则
      - pattern: /api/sensitive/**
        key-algorithm: rsa
        data-algorithm: aes
    exclude-urls:                               # 排除规则(白名单)
      - /api/public/**

  encrypt:
    enabled: true                               # 总开关(默认 true)
    result-advice-order: 300                    # 返回值加密 Advice 排序(默认 300)
    key:
      public-key: ""                            # 公钥(Base64 格式)
    data:
      param-alias: "encryptParam"               # param 数据加密后别名(默认 encryptParam)
      body-alias: "encryptBody"                 # body 数据加密后别名(默认 encryptBody)
      data-key-header: "X-Encrypt-Key"          # 数据密钥响应头名称(默认 X-Encrypt-Key)
      key-mode: dynamic                         # 密钥模式:dynamic / static(默认 dynamic)
      key: ""                                   # 静态密钥(key-mode=static 时使用)
    urls:                                       # 需要加密的 URL 规则
      - pattern: /api/sensitive/**
        key-algorithm: rsa
        data-algorithm: aes
    exclude-urls:                               # 排除规则(白名单)
      - /api/public/**

支持的加密算法

密钥算法 YAML 值 说明
RSA rsa RSA 加密 AES 密钥
SM2 sm2 国密 SM2 加密 SM4 密钥
数据算法 YAML 值 说明
AES aes AES-GCM 模式加密数据
SM4 sm4 国密 SM4-CBC 模式加密数据

工作流程

请求解密流程:

客户端请求
  │
  ├─ 1. 生成随机数据密钥(AES/SM4)
  ├─ 2. 使用数据密钥加密请求参数(param 或 body)
  ├─ 3. 使用服务端公钥加密数据密钥
  ├─ 4. 发送请求:
  │      Header: X-Encrypt-Key: {加密后的密钥}
  │      Param: encryptParam={加密后的参数} 或
  │      Body: {"encryptBody": "{加密后的数据}"}
  │
  ▼
服务端 DecryptFilter
  │
  ├─ 1. 从 Header 获取加密的密钥
  ├─ 2. 使用私钥解密得到数据密钥
  ├─ 3. 从 param 或 body 中获取加密数据
  ├─ 4. 使用数据密钥解密数据
  ├─ 5. 将解密后的数据传递给业务层
  │
  ▼
业务处理

响应加密流程:

业务层返回数据
  │
  ▼
EncryptResponseAdvice
  │
  ├─ 1. 生成随机数据密钥(或使用静态密钥)
  ├─ 2. 使用数据密钥加密响应数据
  ├─ 3. 使用客户端公钥加密数据密钥
  ├─ 4. 返回响应:
  │      Header: X-Encrypt-Key: {加密后的密钥}
  │      Body: {"encryptBody": "{加密后的数据}"}
  │
  ▼
客户端
  │
  ├─ 1. 从 Header 获取加密的密钥
  ├─ 2. 使用私钥解密得到数据密钥
  ├─ 3. 使用数据密钥解密响应数据
  │
  ▼
业务处理

密钥模式

支持两种密钥模式:

模式 YAML 值 说明
动态密钥 dynamic 每次请求动态生成密钥(默认)
静态密钥 static 使用配置的固定密钥

前端实现参考

前端实现请参考示例项目中的测试页面:

  • 文件位置:guardian-example/src/main/resources/static/encrypt-test.html
  • 关键依赖:CryptoJS(AES)、sm-crypto(SM2/SM4)、jsencrypt(RSA)

核心实现要点:

  1. RSA + AES 方案

    • 生成 16 字节 AES 密钥
    • 使用 AES-GCM 模式加密数据
    • 使用 RSA 公钥加密 AES 密钥
  2. SM2 + SM4 方案

    • 生成 16 字节 SM4 密钥
    • 使用 SM4-CBC 模式加密数据
    • 使用 SM2 公钥加密 SM4 密钥(C1C3C2 格式)

扩展点

自定义密钥解密服务:

@Bean
public KeyDecryptService customKeyDecryptService() {
    return new KeyDecryptService() {
        @Override
        public String decrypt(String encryptedKey, String privateKey) {
            // 自定义密钥解密逻辑
        }
        @Override
        public KeyEncryptAlgorithm getAlgorithm() {
            return KeyEncryptAlgorithm.RSA;
        }
    };
}

自定义数据解密服务:

@Bean
public DataDecryptService customDataDecryptService() {
    return new DataDecryptService() {
        @Override
        public String decrypt(String encryptedData, String key) {
            // 自定义数据解密逻辑
        }
        @Override
        public DataEncryptAlgorithm getAlgorithm() {
            return DataEncryptAlgorithm.AES;
        }
    };
}

规则优先级

Guardian 各模块(防重复提交、接口限流、慢接口检测)的规则匹配遵循以下优先级:

exclude-urls(白名单)> YAML 规则 > 注解 > 放行
场景 行为
URL 命中 exclude-urls 直接放行,跳过所有检测(包括注解)
URL 命中 YAML urls 规则 YAML 规则生效
方法有注解 @RateLimit / @RepeatSubmit / @SlowApiThreshold 注解规则生效
以上均未命中 放行

设计理念:exclude-urls 作为白名单拥有最高优先级,可在紧急情况下通过配置中心动态添加 URL 实现"一键放行",无需改代码重启。注解适合"长期固定"的保护策略,YAML 规则适合"动态可调"的批量策略。


动态配置

Guardian 所有模块的 YAML 配置均支持通过配置中心(Nacos、Apollo 等)动态刷新,无需重启应用即可生效。

支持动态刷新的配置项

以下列出所有模块可在配置中心动态修改的完整参数(enabled、storage 等启动期参数修改后需重启生效)。

防重复提交(prefix: guardian.repeat-submit)

YAML Key 类型 默认值 说明
response-mode exception / json exception 响应模式
log-enabled boolean false 是否打印拦截日志
exclude-urls List<String> [] 排除规则(白名单,优先级最高,AntPath)
urls List [] 防重规则列表,每项参数如下
urls[].pattern String — 接口路径(AntPath)
urls[].interval int 5 防重间隔
urls[].time-unit TimeUnit seconds 间隔时间单位
urls[].key-scope user / ip / global user 防重维度
urls[].sp-el String — SpEL 表达式,动态生成 Key(v1.9.0+,如 #orderId)
urls[].message String 您的请求过于频繁,请稍后再试 拦截提示信息
urls[].client-type pc / app pc 客户端类型

接口限流(prefix: guardian.rate-limit)

YAML Key 类型 默认值 说明
response-mode exception / json exception 响应模式
log-enabled boolean false 是否打印拦截日志
exclude-urls List<String> [] 排除规则(白名单,优先级最高,AntPath)
urls List [] 限流规则列表,每项参数如下
urls[].pattern String — 接口路径(AntPath)
urls[].qps int 10 滑动窗口=QPS,令牌桶=每 window 补充令牌数
urls[].window int 1 滑动窗口=窗口跨度,令牌桶=补充周期
urls[].window-unit TimeUnit seconds 时间单位
urls[].algorithm sliding_window / token_bucket sliding_window 限流算法
urls[].capacity int -1 令牌桶容量(≤0 时取 qps 值)
urls[].rate-limit-scope global / ip / user global 限流维度
urls[].sp-el String — SpEL 表达式,动态生成 Key(v1.9.0+,如 #orderId)
urls[].message String 请求过于频繁,请稍后再试 拦截提示信息

接口幂等(prefix: guardian.idempotent)

YAML Key 类型 默认值 说明
timeout long 300 Token 有效期
time-unit TimeUnit seconds 有效期单位
response-mode exception / json exception 响应模式
log-enabled boolean false 是否打印拦截日志

参数自动Trim(prefix: guardian.auto-trim)

YAML Key 类型 默认值 说明
exclude-fields Set<String> [] 排除字段(不做 trim 的字段名)
character-replacements List [] 字符替换规则列表,每项参数如下
character-replacements[].from String — 待替换字符(支持 \\r \\n \\t \\uXXXX 转义)
character-replacements[].to String "" 替换为

慢接口检测(prefix: guardian.slow-api)

YAML Key 类型 默认值 说明
threshold long 3000 全局慢接口阈值(毫秒)
exclude-urls List<String> [] 排除规则(白名单,优先级最高,AntPath)

请求链路追踪(prefix: guardian.trace)

YAML Key 类型 默认值 说明
header-name String X-Trace-Id 请求头/响应头名称(同时影响 MQ 链路追踪的 Header Key)

防重放攻击(prefix: guardian.anti-replay)

YAML Key 类型 默认值 说明
max-age long 60 timestamp 有效窗口
max-age-unit TimeUnit seconds timestamp 有效窗口单位
nonce-ttl long 86400 nonce 存活时间(必须 >= max-age)
nonce-ttl-unit TimeUnit seconds nonce 存活时间单位
response-mode exception / json exception 响应模式
log-enabled boolean false 是否打印拦截日志
missing-timestamp-message String 缺少时间戳 缺少时间戳提示(支持 i18n Key)
missing-nonce-message String 缺少请求标识 缺少 Nonce 提示(支持 i18n Key)
expired-message String 请求已过期 请求过期提示(支持 i18n Key

README 内容过长,已截取部分内容。完整内容请前往 GitHub 仓库查看。

评论

暂无评论