Skip to content

BigGG-Guardian/guardian

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

52 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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),@AsyncCompletableFuture、手动线程池场景均可保持 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-ttlmax-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]
  • ActuatorGET /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]
  • ActuatorGET /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]
  • ActuatorGET /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,下游自动复用,实现跨服务日志串联。内置跨线程传递工具类,@AsyncCompletableFuture、手动线程池等场景均可保持 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:Messageorg.springframework.amqp.core.Message),不能用 String
  • Kafka:ConsumerRecord<?, ?>,不能用 String
  • RocketMQ:MessageExtorg.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]
  • ActuatorGET /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]
  • ActuatorGET /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]
  • ActuatorGET /actuator/guardianApiSwitch
{
  "disabledUrls": [
    "/api-switch/disabled"
  ]
}

关闭接口

  • ActuatorPOST /actuator/guardianApiSwitch/{urlPattern}

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

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

开启接口

  • ActuatorDELETE /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]
  • ActuatorGET /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 等)动态刷新,无需重启应用即可生效。

支持动态刷新的配置项

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

防重复提交(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)
replay-message String 重复请求 重放攻击提示(支持 i18n Key)
urls List [] 防重放 URL 规则列表(AntPath)
exclude-urls List<String> [] 排除规则(白名单,优先级最高)

IP黑白名单(prefix: guardian.ip-filter

YAML Key 类型 默认值 说明
response-mode exception / json exception 响应模式
log-enabled boolean false 是否打印拦截日志
message String IP 访问被拒绝 拒绝提示信息(支持 i18n Key)
black-list List<String> [] 全局 IP 黑名单(精确 / 通配符 / CIDR)
urls List [] URL 绑定白名单规则列表,每项参数如下
urls[].pattern String 接口路径(AntPath)
urls[].white-list List<String> [] 允许访问的 IP 列表(精确 / 通配符 / CIDR)

接口开关(prefix: guardian.api-switch

YAML Key 类型 默认值 说明
response-mode exception / json exception 响应模式
log-enabled boolean false 是否打印拦截日志
message String 接口暂时关闭,请稍后再试 提示信息(支持 i18n Key)
disabled-urls List<String> [] 默认关闭的接口路径(AntPath)

参数签名(prefix: guardian.sign

YAML Key 类型 默认值 说明
secret-key String "" 签名密钥
response-mode exception / json exception 响应模式
log-enabled boolean false 是否打印拦截日志
sign-header String X-Sign 签名请求头名称
timestamp-header String X-Sign-Timestamp 时间戳请求头名称
max-age long 60 时间戳过期时间
max-age-unit TimeUnit seconds 时间戳过期时间单位
missing-timestamp-message String 缺少时间戳 缺少时间戳提示(支持 i18n Key)
missing-sign-message String 缺少参数签名 缺少签名提示(支持 i18n Key)
expired-message String 请求已过期 请求过期提示(支持 i18n Key)
urls List [] 签名验证规则列表,每项参数如下
urls[].pattern String 接口路径(AntPath)
urls[].algorithm base64 / md5 / sha256 / hmac_sha256 / sm3 base64 签名算法
urls[].sign-verify-message String 签名校验失败 签名验证失败提示(支持 i18n Key)

请求解密(prefix: guardian.decrypt

YAML Key 类型 默认值 说明
enabled boolean true 总开关
filter-order int -40000 Filter 排序(值越小越先执行)
log-enabled boolean false 是否打印拦截日志
response-mode exception / json exception 响应模式
key.private-key String "" 私钥(Base64 格式,用于解密密钥)
data.param-alias String encryptParam param 数据加密后别名
data.body-alias String encryptBody body 数据加密后别名
data.data-key-header String X-Encrypt-Key 数据密钥请求头名称
missing-data-key-header-message String 缺少数据密钥请求头 缺少密钥头提示(支持 i18n Key)
urls List [] 解密规则列表,每项参数如下
urls[].pattern String 接口路径(AntPath)
urls[].key-algorithm rsa / sm2 rsa 密钥加密算法
urls[].data-algorithm aes / sm4 aes 数据加密算法
exclude-urls List<String> [] 排除规则(白名单,优先级最高,AntPath)

响应加密(prefix: guardian.encrypt

YAML Key 类型 默认值 说明
enabled boolean true 总开关
result-advice-order int 300 返回值加密 Advice 排序
key.public-key String "" 公钥(Base64 格式,用于加密密钥)
data.param-alias String encryptParam param 数据加密后别名
data.body-alias String encryptBody body 数据加密后别名
data.data-key-header String X-Encrypt-Key 数据密钥响应头名称
data.key-mode dynamic / static dynamic 密钥模式:动态生成 / 静态配置
data.key String "" 静态密钥(key-mode=static 时使用)
urls List [] 加密规则列表,每项参数如下
urls[].pattern String 接口路径(AntPath)
urls[].key-algorithm rsa / sm2 rsa 密钥加密算法
urls[].data-algorithm aes / sm4 aes 数据加密算法
exclude-urls List<String> [] 排除规则(白名单,优先级最高,AntPath)

使用方式

以 Nacos 为例,引入 Spring Cloud Alibaba Nacos Config 依赖后,在 Nacos 控制台修改 Guardian 相关配置并发布,应用会自动感知变更并即时生效。

1. 添加依赖(以 Spring Boot 2.7.x 为例):

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

2. 配置 bootstrap.yml

spring:
  application:
    name: your-app
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yml

3. 在 Nacos 控制台修改配置:

在对应的 Data ID(如 your-app.yml)中修改 Guardian 配置并发布,发布后即时生效,无需重启。以下是覆盖全模块的 Nacos 配置示例:

guardian:
  repeat-submit:
    response-mode: exception
    log-enabled: false
    exclude-urls:
      - /api/public/**
    urls:
      - pattern: /api/order/**
        interval: 10
        time-unit: seconds
        key-scope: user
        message: "请勿重复提交"
      # SpEL 动态 Key 示例(v1.9.0+)
      - 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"

  rate-limit:
    response-mode: exception
    log-enabled: false
    exclude-urls:
      - /api/public/**
    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: "抢购太火爆,请稍后重试"
      # SpEL 动态 Key 示例(v1.9.0+)
      - 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"

  idempotent:
    timeout: 300
    time-unit: seconds
    response-mode: exception
    log-enabled: false
    missing-token-message: guardian.idempotent.missing-token

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

  slow-api:
    threshold: 3000
    exclude-urls:
      - /api/health

  trace:
    header-name: X-Trace-Id

  ip-filter:
    enabled: true
    response-mode: json
    log-enabled: true
    message: "IP 访问被拒绝"
    black-list:
      - 192.168.100.100
    urls:
      - pattern: /admin/**
        white-list:
          - 127.0.0.1
          - 192.168.1.*

  anti-replay:
    max-age: 60
    nonce-ttl: 86400
    response-mode: json
    log-enabled: true
    missing-timestamp-message: "缺少时间戳"
    missing-nonce-message: "缺少请求标识"
    expired-message: "请求已过期"
    replay-message: "重复请求"
    urls:
      - pattern: /api/payment/**
      - pattern: /api/transfer/**
    exclude-urls:
      - /api/public/**

  api-switch:
    response-mode: json
    message: "接口暂时关闭,请稍后再试"
    log-enabled: true
    disabled-urls:
      - /api-switch/disabled

  sign:
    secret-key: your-secret-key
    response-mode: exception
    log-enabled: false
    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:
      - pattern: /api/payment/**
        algorithm: hmac_sha256
        sign-verify-message: "签名校验失败"
      - pattern: /api/order/**
        algorithm: md5
        sign-verify-message: "签名校验失败"

  decrypt:
    enabled: true
    filter-order: -40000
    log-enabled: false
    response-mode: exception
    key:
      private-key: "your-private-key-base64"
    data:
      param-alias: "encryptParam"
      body-alias: "encryptBody"
      data-key-header: "X-Encrypt-Key"
    missing-data-key-header-message: "缺少数据密钥请求头"
    urls:
      - pattern: /api/sensitive/**
        key-algorithm: rsa
        data-algorithm: aes
      - pattern: /api/government/**
        key-algorithm: sm2
        data-algorithm: sm4
    exclude-urls:
      - /api/public/**

  encrypt:
    enabled: true
    result-advice-order: 300
    key:
      public-key: "your-public-key-base64"
    data:
      param-alias: "encryptParam"
      body-alias: "encryptBody"
      data-key-header: "X-Encrypt-Key"
      key-mode: dynamic
      key: ""
    urls:
      - pattern: /api/sensitive/**
        key-algorithm: rsa
        data-algorithm: aes
      - pattern: /api/government/**
        key-algorithm: sm2
        data-algorithm: sm4
    exclude-urls:
      - /api/public/**

只需配置你用到的模块,没用到的模块无需配置。修改任意参数后点击发布,下一次请求即可读取到最新值。

实现原理

Guardian 的 @ConfigurationProperties 属性类实现了模块配置接口(如 RepeatSubmitConfigRateLimitConfig 等),拦截器/过滤器通过接口引用动态读取配置值。当配置中心推送变更时,Spring Cloud 的 ConfigurationPropertiesRebinder 自动重新绑定属性,所有引用该配置的组件在下次请求时即可读取到最新值。


消息国际化

Guardian 的拒绝响应消息(防重、限流、幂等)支持 Spring 标准的 MessageSource 国际化机制。不使用国际化的用户无需任何改动,message 配置中文纯文本即可,行为与之前完全一致。

工作原理

message 字段同时支持纯文本i18n Key 两种写法:

# 纯文本(默认,不走国际化)
message: "请求过于频繁,请稍后再试"

# i18n Key(自动走 MessageSource 解析)
message: guardian.rate-limit.rejected

Guardian 通过 GuardianMessageResolver 统一解析:尝试从 MessageSource 查找,找到则返回对应语言的翻译,找不到则原样返回。语言由请求头 Accept-Language 自动决定。

使用方式

第一步,把 message 改成 i18n Key。

YAML 规则示例:

guardian:
  rate-limit:
    urls:
      - pattern: /api/sms/send
        qps: 1
        message: guardian.rate-limit.rejected
  repeat-submit:
    urls:
      - pattern: /api/order/submit
        interval: 10
        message: guardian.repeat-submit.rejected
  idempotent:
    missing-token-message: guardian.idempotent.missing-token

注解示例:

@RateLimit(qps = 5, message = "guardian.rate-limit.rejected")
@RepeatSubmit(interval = 10, message = "guardian.repeat-submit.rejected")
@Idempotent(value = "createOrder", message = "guardian.idempotent.rejected")

第二步,在项目中添加多语言消息文件。

重要:必须创建基础文件 messages.properties,Spring Boot 的 MessageSourceAutoConfiguration 需要检测到该文件才会激活 ResourceBundleMessageSource,否则国际化不生效。

# src/main/resources/messages.properties(必须,作为默认回退)
guardian.rate-limit.rejected=请求过于频繁,请稍后再试
guardian.repeat-submit.rejected=您的请求过于频繁,请稍后再试
guardian.idempotent.rejected=幂等Token无效或已消费
guardian.idempotent.missing-token=请求缺少幂等Token
# src/main/resources/messages_zh_CN.properties
guardian.rate-limit.rejected=请求过于频繁,请稍后再试
guardian.repeat-submit.rejected=您的请求过于频繁,请稍后再试
guardian.idempotent.rejected=幂等Token无效或已消费
guardian.idempotent.missing-token=请求缺少幂等Token
# src/main/resources/messages_en.properties
guardian.rate-limit.rejected=Rate limit exceeded, please try again later
guardian.repeat-submit.rejected=Too many requests, please try again later
guardian.idempotent.rejected=Idempotent token is invalid or already consumed
guardian.idempotent.missing-token=Missing idempotent token

第三步,通过请求头 Accept-Language 切换语言:

请求头 匹配文件 效果
Accept-Language: zh-CN messages_zh_CN.properties 中文
Accept-Language: en messages_en.properties 英文
不传 messages.properties 默认回退

Spring Boot 自动根据 Accept-Language 匹配语言,无需额外配置。

如果项目使用自定义消息文件路径(如 i18n/messages),只需配置 spring.messages.basename=i18n/messages,Guardian 自动适配。同样需要确保基础文件 i18n/messages.properties 存在。


架构

模块结构

guardian-parent
├── guardian-core                          # 公共基础(共享类)
├── guardian-repeat-submit/                # 防重复提交
│   ├── guardian-repeat-submit-core/
│   └── guardian-repeat-submit-spring-boot-starter/
├── guardian-rate-limit/                   # 接口限流
│   ├── guardian-rate-limit-core/
│   └── guardian-rate-limit-spring-boot-starter/
├── guardian-idempotent/                   # 接口幂等
│   ├── guardian-idempotent-core/
│   └── guardian-idempotent-spring-boot-starter/
├── guardian-auto-trim/                    # 参数自动Trim
│   ├── guardian-auto-trim-core/
│   └── guardian-auto-trim-spring-boot-starter/
├── guardian-slow-api/                     # 慢接口检测
│   ├── guardian-slow-api-core/
│   └── guardian-slow-api-spring-boot-starter/
├── guardian-trace/                        # 请求链路追踪
│   ├── guardian-trace-core/
│   ├── guardian-trace-spring-boot-starter/
│   ├── guardian-trace-rabbitmq/           # RabbitMQ 链路追踪
│   ├── guardian-trace-kafka/              # Kafka 链路追踪
│   └── guardian-trace-rocketmq/           # RocketMQ 链路追踪
├── guardian-ip-filter/                    # IP黑白名单
│   ├── guardian-ip-filter-core/
│   └── guardian-ip-filter-spring-boot-starter/
├── guardian-anti-replay/                  # 防重放攻击
│   ├── guardian-anti-replay-core/
│   └── guardian-anti-replay-spring-boot-starter/
├── guardian-api-switch/                  # 接口开关
│   ├── guardian-api-switch-core/
│   └── guardian-api-switch-spring-boot-starter/
├── guardian-sign/                         # 参数签名
│   ├── guardian-sign-core/
│   └── guardian-sign-spring-boot-starter/
├── guardian-encrypt/                      # 请求加解密
│   ├── guardian-encrypt-core/
│   └── guardian-encrypt-spring-boot-starter/
├── guardian-starter-all/                  # 整合引用所有Starter模块
├── guardian-storage-redis/                # Redis 存储(多模块共享)
└── guardian-example/                      # 示例工程

执行顺序

Guardian 的 Filter 和 Interceptor 通过 order 值控制执行优先级,值越小越先执行

Filter 执行顺序

Filter 在 Servlet 层执行,先于所有 Interceptor:

顺序 模块 配置项 默认 order 说明
1 请求体缓存 guardian.repeatable-filter-order -100000 最先执行,缓存请求体供后续模块重复读取
2 请求解密 guardian.decrypt.filter-order -40000 解密请求数据
3 请求链路追踪 guardian.trace.filter-order -30000 为后续所有操作提供 TraceId
4 IP 黑白名单 guardian.ip-filter.filter-order -20000 拦截恶意 IP,尽早阻断
5 防重放攻击 guardian.anti-replay.filter-order -14000 校验 Timestamp + Nonce,拦截重放请求
6 参数自动 Trim guardian.auto-trim.filter-order -10000 参数预处理,在业务逻辑前清洗数据

Interceptor 执行顺序

Interceptor 在 Spring MVC 层执行,Filter 之后:

顺序 模块 配置项 默认 order 说明
1 慢接口检测 guardian.slow-api.interceptor-order -1000 最先进入最后退出,精确计算整体耗时
2 接口开关 guardian.api-switch.interceptor-order 500 接口关闭直接拒绝,避免后续无意义计算
3 接口限流 guardian.rate-limit.interceptor-order 1000 流量超限直接拒绝,避免后续无意义计算
4 防重复提交 guardian.repeat-submit.interceptor-order 2000 通过限流后,判断是否短时间重复请求
5 接口幂等 guardian.idempotent.interceptor-order 3000 消费 Token 不可逆,确保前面校验都通过
6 参数签名 guardian.sign.interceptor-order 4000 签名验证,确保请求参数未被篡改

每个模块的 order 均可通过 YAML 自定义,方便与项目中其他拦截器协调:

guardian:
  # Filter 排序
  repeatable-filter-order: -100000       # 请求体缓存(最先执行,全局共享)
  decrypt:
    filter-order: -40000                 # 请求解密
  trace:
    filter-order: -30000                 # 链路追踪
  ip-filter:
    filter-order: -20000                 # IP 黑白名单
  anti-replay:
    filter-order: -14000                 # 防重放攻击
  auto-trim:
    filter-order: -10000                 # 参数自动 Trim

  # Interceptor 排序
  slow-api:
    interceptor-order: -1000             # 慢接口检测
  api-switch:
    interceptor-order: 500               # 接口开关
  rate-limit:
    interceptor-order: 1000              # 接口限流
  repeat-submit:
    interceptor-order: 2000              # 防重复提交
  idempotent:
    interceptor-order: 3000              # 接口幂等
  sign:
    interceptor-order: 4000              # 参数签名

  # ResponseBodyAdvice 排序
  idempotent:
    result-advice-order: 200             # 幂等结果缓存(需开启 result-cache)
  sign:
    result-advice-order: 100             # 响应结果签名(需开启 result-sign)
  encrypt:
    result-advice-order: 300             # 响应结果加密

存储对比

Redis Local
分布式 支持 仅单机
持久性 Redis 持久化 重启丢失
推荐场景 生产环境 开发/单体应用
额外依赖 需要 Redis

更新日志

v1.10.0

  • 新增:请求加解密模块(guardian-encrypt),支持 RSA+AES / SM2+SM4 加密方案
  • 新增:请求解密过滤器(DecryptFilter),自动解密请求参数
  • 新增:响应加密 Advice(EncryptResponseAdvice),自动加密响应结果
  • 新增:密钥加密服务(KeyEncryptService),支持 RSA 和 SM2 算法
  • 新增:数据加密服务(DataEncryptService),支持 AES-GCM 和 SM4-CBC 算法
  • 新增:加密模块 Actuator 端点(GET /actuator/guardianEncrypt
  • 新增:前端示例页面(sign-test.htmlencrypt-test.html),提供完整的前端实现参考
  • 新增:签名模块支持 SM3 算法
  • 优化:签名模块前端实现文档简化,指向示例页面
  • 优化:加密模块支持动态密钥和静态密钥两种模式

v1.9.0

  • 新增:防重复提交模块支持 SpEL 表达式动态生成 Key(spEl 属性)
  • 新增:接口限流模块支持 SpEL 表达式动态生成 Key(spEl 属性)
  • 新增SpElUtils 工具类,支持 #参数名#body.参数名 访问请求参数

v1.8.1

  • 新增:ResponseBodyAdvice 执行顺序可配置,通过 YAML 动态控制
  • 新增:签名模块 result-advice-order 配置项(默认100)
  • 新增:幂等模块 result-advice-order 配置项(默认200)
  • 修复:修复 IdempotentResultCacheAdvice 未添加 @ControllerAdvice 注解导致 Advice 不生效的问题
  • 修复:修复 SignResultSignAdvice Advice 执行顺序不可控的问题
  • 优化:完善拦截器与 Advice 执行顺序说明

v1.8.0

  • 新增:参数签名模块(guardian-sign),支持多种签名算法,请求参数签名验证 + 响应结果签名
  • 新增@SignVerify 注解,支持指定签名算法和自定义错误信息
  • 新增:签名验证拦截器(SignVerifyInterceptor),自动校验请求签名
  • 新增:响应结果签名(SignResultSignAdvice),确保返回数据的完整性
  • 新增:签名服务(SignService),支持 BASE64、MD5、SHA256 和 HMAC-SHA256 四种算法
  • 新增:签名统计和 Actuator 端点(GET /actuator/guardianSign
  • 修复:HMAC-SHA256 算法实现错误,添加了密钥参数
  • 修复:配置验证,添加了对 secretKey 的非空检查
  • 修复:响应签名空值检查缺失问题

v1.7.2

  • 新增:接口开关模块(guardian-api-switch),动态关闭/开启接口

v1.7.1

  • 新增:整合所有starter模块(guardian-starter-all),以便于引用所有功能时只需引用此模块
  • 优化:共享类重新分类分包

v1.7.0

  • 新增:防重放攻击模块(guardian-anti-replay-spring-boot-starter),通过 Timestamp + Nonce 双重校验拦截重放请求
  • 新增NonceStorage 存储接口,支持 Redis(分布式)和 Local(单机)两种实现
  • 新增nonce-ttlmax-age 解耦设计,Nonce 存活时间(默认 24h)远大于 Timestamp 有效窗口(默认 60s),防止攻击者在 Nonce 过期后篡改 Timestamp 重放
  • 新增nonce-ttl-unit / max-age-unit 支持自定义时间单位,validate() 方法统一换算后比较
  • 新增:防重放攻击 Actuator 监控端点(GET /actuator/guardianAntiReplay),展示拦截统计和 Top N 被拦截接口
  • 新增:防重放攻击拦截日志(log-enabled: true,前缀 [Guardian-Anti-Replay]
  • 新增:防重放攻击 URL 规则 + 排除规则(urls + exclude-urls),支持 AntPath 匹配
  • 新增AntiReplayResponseHandler SPI 扩展点,支持自定义拒绝响应格式
  • 新增:全部拒绝提示信息支持 i18n Key(missing-timestamp-message / missing-nonce-message / expired-message / replay-message

v1.6.2

  • 新增:MQ 消息链路追踪,支持 RabbitMQ、Kafka、RocketMQ 三种消息队列的 TraceId 自动传递
  • 新增guardian-trace-rabbitmq 模块,发送端通过 MessagePostProcessor 注入 traceId,消费端通过 AOP 切面拦截 @RabbitListener 自动提取
  • 新增guardian-trace-kafka 模块,发送端通过 ProducerInterceptor 注入 traceId,消费端通过 AOP 切面拦截 @KafkaListener 自动提取
  • 新增guardian-trace-rocketmq 模块,发送端通过 SendMessageHook 注入 traceId,消费端通过 AOP 切面拦截 @RocketMQMessageListener 自动提取
  • 新增TraceUtils.switchTraceId() 方法,支持 MQ 批量消费场景逐条切换 traceId
  • 新增TraceRabbitUtils / TraceKafkaUtils / TraceRocketMQUtils 批量消费专用工具类,一行代码从消息中提取并切换 traceId
  • 新增:慢接口记录器 SPI 扩展点(SlowApiRecorder),支持自定义持久化方案(数据库 / ES 等)替换默认内存实现
  • 优化:MQ 消费端统一采用 AOP 切面拦截,不占用框架原生拦截器/Hook 位置,与用户自定义拦截器互不冲突
  • 优化:Kafka ProducerInterceptor 通过持有 TraceConfig 引用动态获取 headerName,支持配置中心热更新
  • 修复:MQ 自动配置类添加 @AutoConfigureAfter,确保在 TraceConfig 和 MQ Template Bean 创建之后加载,解决因加载顺序导致的链路追踪不生效问题

v1.6.1

  • 重构:移除 hutool-all 依赖,全面切换 Spring/JDK/Jackson 原生工具,项目零外部工具包依赖
  • 新增GuardianJsonUtils(基于 Jackson)、IpUtilsMd5Utils 三个核心工具类
  • 修复jackson-databind 作用域由 provided 改为 compile,解决下游模块编译失败

v1.6.0

  • 新增:IP 黑白名单模块(guardian-ip-filter-spring-boot-starter),支持全局 IP 黑名单 + URL 绑定白名单
  • 新增:IP 规则匹配支持精确匹配、通配符(192.168.1.*)和 CIDR(10.0.0.0/8)三种格式
  • 新增:IP 黑白名单拦截统计 + Actuator 端点(GET /actuator/guardianIpFilter
  • 新增IpMatcher 工具类,统一处理 IP 规则匹配逻辑
  • 新增:IP 黑白名单拦截日志(log-enabled: true,前缀 [Guardian-Ip-Filter]

v1.5.3

  • 新增:消息国际化支持,拒绝响应消息(防重、限流、幂等)支持 Spring MessageSource 国际化,message 字段可配置 i18n Key,根据 Accept-Language 自动匹配语言
  • 新增GuardianMessageResolver 消息解析工具,MessageSource 能解析则返回翻译,否则原样返回,不使用国际化的用户零感知
  • 新增:幂等模块 missing-token-message 配置项,缺少 Token 时的提示信息支持自定义及国际化

v1.5.2

  • 优化GuardianRateLimitProperties / GuardianRepeatSubmitProperties 删除多余无用参数

v1.5.1

  • 新增:全模块配置动态刷新支持,配合 Nacos / Apollo 等配置中心可在不重启应用的情况下实时更新 Guardian 配置
  • 新增BaseConfig / BaseCharacterReplacement 配置接口,拦截器/过滤器通过接口引用动态读取配置,与 Spring Cloud ConfigurationPropertiesRebinder 无缝集成
  • 优化CharacterSanitizer 引入基于哈希值的缓存机制,配置未变更时零解析开销,配置动态刷新后自动重建
  • 修复DefaultIdempotentTokenService 改为持有 IdempotentConfig 接口引用,修复 Token 过期时间无法动态更新的问题

v1.5.0

  • 新增:参数自动Trim模块(guardian-auto-trim-spring-boot-starter),自动去除请求参数首尾空格,支持表单参数 + JSON Body
  • 新增:不可见字符替换功能(character-replacements),可清除零宽空格、BOM、回车符等从复制粘贴混入的不可见字符
  • 新增:排除字段配置(exclude-fields),密码、签名等敏感字段可跳过 Trim
  • 新增:慢接口检测模块(guardian-slow-api-spring-boot-starter),超过阈值自动记录并打印 WARN 日志
  • 新增@SlowApiThreshold 注解,支持为单个接口自定义慢接口阈值
  • 新增:慢接口 Actuator 端点(GET /actuator/guardianSlowApi),展示触发次数 + Top N 排行 + 最大耗时
  • 新增:请求链路追踪模块(guardian-trace-spring-boot-starter),自动生成/透传 TraceId
  • 新增:TraceId 写入 MDC + 响应头,Logback pattern 加 %X{traceId} 即可串联全链路日志
  • 新增:上游服务通过 X-Trace-Id 请求头透传 TraceId,支持跨服务链路追踪

v1.4.3

  • 新增:接口幂等模块(guardian-idempotent-spring-boot-starter),Token 机制保证接口幂等性
  • 新增:幂等结果缓存,开启后重复请求返回首次执行结果
  • 新增:幂等 Actuator 端点、拦截统计、Token 生成器可插拔
  • 新增:幂等 PARAM 模式支持从 JSON Body 解析 Token(兼容 form-data / x-www-form-urlencoded / JSON 三种 POST 传参)
  • 优化RepeatableRequestFilter 排序提升到全局配置 GuardianCorePropertiesguardian.repeatable-filter-order),仅需配置一次
  • 优化:拦截器执行顺序可配置(interceptor-order),默认:限流 1000 → 防重 2000 → 幂等 3000
  • 优化:三模块 Properties 提取公共基类 BaseGuardianProperties,统一 storage / responseMode / logEnabled / interceptorOrder
  • 优化:移除 Manager 中间层(KeyGeneratorManagerRateLimitKeyGeneratorManagerKeyEncryptManagerIdempotentTokenGeneratorManager),改为构造函数直接注入
  • 优化:删除未使用的异常类(TokenGeneratorNotFoundExceptionKeyGeneratorNotFoundExceptionKeyEncryptNotFoundException
  • 修复:幂等 null 返回值处理,与 Spring 原生行为保持一致
  • 修复BaseResult.error() 状态码错误(200 → 500)
  • 修复:Actuator Endpoint ID 改为驼峰命名,消除 Spring Boot 启动 WARN
  • 修复:三模块参数校验(qpswindowintervaltimeout ≤ 0 时抛出明确异常)
  • 修复:令牌桶算法时间回拨防护(Redis Lua + 本地存储均加 max(0, elapsed)

v1.3.0

  • 新增:接口限流令牌桶算法(TOKEN_BUCKET),支持突发流量
  • 修复:本地滑动窗口并发竞态条件(check-then-act),加 synchronized 保证原子性
  • 修复:本地缓存内存泄漏,增加守护线程定期清理过期 Key
  • 修复:rate-limit 模块 POM parent 版本写死,统一改为 ${revision}

v1.2.0

  • 新增:接口限流模块(滑动窗口算法),支持注解 + YAML 双模式
  • 新增:限流维度(GLOBAL / IP / USER)
  • 新增:限流 Actuator 监控端点

v1.1.0

  • 防重复提交初始版本

环境要求

  • JDK 1.8+
  • Spring Boot 2.7.x
  • Redis 5.0+(使用 Redis 存储时)

仓库地址

平台 地址
GitHub(主仓) https://github.com/BigGG-Guardian/guardian
Gitee(镜像同步) https://gitee.com/BigGG-Guardian/guardian
Maven Central https://central.sonatype.com/artifact/io.github.biggg-guardian/guardian-repeat-submit-spring-boot-starter

Gitee 从 GitHub 自动同步,Issues 和 PR 请提交到 GitHub。

开源协议

Apache License 2.0

About

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

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages