1. APISIX 验签的核心原理

APISIX 的请求处理流程为:请求进入 → 插件执行(顺序可配置) → 转发至上游服务。验签逻辑本质是在插件执行阶段,通过以下步骤实现:

  1. 提取 签名 相关参数:从请求头(如X-SignatureAuthorization)、请求参数或 Body 中,提取签名值、时间戳、随机串、AccessKey 等验签必需信息。
  2. 生成待验签串:按照业务约定的规则(如 “参数按 ASCII 排序 + 拼接时间戳 + 密钥”),重组请求中的关键数据(如请求参数、Body、时间戳),生成 “待验签字符串”。
  3. 执行 签名 验证:使用与客户端约定的算法(如 HMAC-SHA256、RSA),结合预设的密钥(或公钥)对 “待验签串” 进行签名,对比生成的签名与请求中提取的签名是否一致。
  4. 拦截或放行:验证通过则继续转发请求至上游;验证失败(如签名不匹配、时间戳过期)则直接返回 401/403 错误,拦截请求。

2. APISIX 实现验签的 3 种常见方式

根据业务场景(如是否需要自定义验签规则、签名算法类型),APISIX 提供了 “内置插件”“自定义 Lua 插件”“集成外部服务” 三种实现方式,以下详细说明:

方式 1:使用 APISIX 内置验签插件(推荐,无需编码)

APISIX 提供了多个成熟的内置插件,覆盖主流验签场景(如 HMAC 对称加密、JWT 令牌、Basic Auth),无需编写代码,仅需通过配置即可启用。

1.1 HMAC 对称加密验签(hmac-auth插件)

适用于客户端与 网关 共享 密钥的场景(如内部服务间调用),基于 HMAC 算法(如 HMAC-SHA256)验证签名,支持防重放(通过时间戳 + nonce)。

核心配置步骤:

(1)创建 Consumer(消费者,即客户端身份) 为每个客户端配置唯一的access_key(标识客户端)和secret_key(对称密钥,需客户端与网关一致),并绑定hmac-auth插件。示例(通过 APISIX Admin API 配置):


# 创建Consumer并启用hmac-auth插件curl http://apisix-admin:9180/apisix/admin/consumers -X PUT -d ' { "username": "client_001", # Consumer名称(自定义) "plugins": { "hmac-auth": { "access_key": "AK123456", # 客户端唯一标识(客户端需携带) "secret_key": "SKabcdef123456", # 对称密钥(客户端与网关共享,需保密) "sign_header": "X-HMAC-Signature", # 请求头中签名的字段名 "clock_skew": 300, # 允许的时间戳偏差(秒,防重放) "nonce_header": "X-HMAC-Nonce", # 请求头中随机串的字段名 "timestamp_header": "X-HMAC-Timestamp" # 请求头中时间戳的字段名 } } }'

(2)为 Route/ Service 绑定 hmac-auth 插件将插件应用到需要验签的路由(Route)或服务(Service),确保该路由的所有请求都必须通过 HMAC 验签。示例(为 Route 配置):


# 为Route 1绑定hmac-auth插件curl http://apisix-admin:9180/apisix/admin/routes/1 -X PUT -d ' { "uri": "/api/v1/order/*", # 需验签的接口路径 "upstream": { "type": "roundrobin", "nodes": {"192.168.1.100:8080": 1} # 上游服务地址 }, "plugins": { "hmac-auth": {} # 启用插件(无需额外配置,复用Consumer的插件参数) } }'

(3)客户端请求示例

  客户端需按以下规则生成签名并携带请求头:

  • 生成nonce(随机串,如abc123)、timestamp(当前时间戳,如1690000000);
  • 待验签串规则:method + uri + timestamp + nonce + body(具体规则可通过插件参数sign_params自定义);
  • 使用secret_key对 “待验签串” 进行 HMAC-SHA256 加密,得到签名值;
  • 请求头携带:X-HMAC-AccessKey: AK123456X-HMAC-Timestamp: 1690000000X-HMAC-Nonce: abc123X-HMAC-Signature: 签名值
1.2 JWT 令牌验签(jwt-auth插件)

适用于分布式 系统、用户认证场景,客户端先通过登录接口获取 JWT 令牌,后续请求携带令牌,网关通过公钥验证令牌有效性(支持对称 / 非对称加密)。

核心配置步骤:

(1)创建 Consumer 并配置 JWT 参数示例(对称加密,使用secret验证;非对称加密需配置public_key):


curl http://apisix-admin:9180/apisix/admin/consumers -X PUT -d ' { "username": "user_001", "plugins": { "jwt-auth": { "secret": "jwt_secret_123", # 对称密钥(非对称加密用public_key替代) "algorithm": "HS256", # 算法(HS256=对称,RS256=非对称) "exp": 3600, # 令牌过期时间(秒) "token_header": "Authorization", # 携带令牌的请求头(如Bearer Token) "token_prefix": "Bearer " # 令牌前缀(如"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...") } } }'

(2)为 Route 绑定 jwt-auth 插件确保请求需携带有效 JWT 令牌才能访问:


curl http://apisix-admin:9180/apisix/admin/routes/2 -X PUT -d ' { "uri": "/api/v1/user/*", "upstream": {"nodes": {"192.168.1.101:8080": 1}}, "plugins": {"jwt-auth": {}} }'

(3)客户端请求示例

  • 登录获取 JWT 令牌:客户端通过登录接口(如/login)提交账号密码,上游服务生成 JWT 令牌(使用secret签名)返回给客户端;
  • 后续请求携带令牌:请求头添加Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...,APISIX 验证令牌签名及过期时间。

方式 2:自定义 Lua 插件(适合复杂自定义验签规则)

若内置插件无法满足业务需求(如特殊签名算法、复杂验签逻辑),可基于 APISIX 的Lua 插件开发框架编写自定义验签插件,完全控制验签流程。

核心开发步骤:

(1)创建插件目录与文件APISIX 插件默认存放在/usr/local/apisix/plugins/目录,新建自定义插件文件(如custom-sign.lua)。

(2)编写插件核心逻辑插件需实现rewrite(请求转发前执行)或access(权限校验阶段)方法,在方法中编写验签逻辑。示例(自定义 HMAC 验签插件核心代码):


local core = require("apisix.core")local hmac = require("resty.hmac")-- 插件Schema(定义配置参数,如secret_key、sign_header)local schema = { type = "object", properties = { secret_key = {type = "string"}, -- 对称密钥 sign_header = {type = "string", default = "X-Custom-Sign"}, -- 签名请求头 timestamp_header = {type = "string", default = "X-Custom-Timestamp"} -- 时间戳请求头}, required = {"secret_key"}}-- 插件主逻辑(access阶段执行)local function access(conf, ctx)-- 1. 提取请求中的验签参数local req_sign = core.request.header(ctx, conf.sign_header)local req_timestamp = core.request.header(ctx, conf.timestamp_header)local req_method = core.request.get_method(ctx)local req_uri = ctx.var.uri local req_body = core.request.get_body(ctx) -- 需开启body读取(在APISIX配置中设置)-- 2. 校验必要参数是否存在if not req_sign or not req_timestamp thenreturn 401, {message = "missing sign or timestamp"}end-- 3. 校验时间戳(防重放,允许5分钟偏差)local current_ts = ngx.time()if math.abs(current_ts - tonumber(req_timestamp)) > 300 thenreturn 401, {message = "timestamp expired"}end-- 4. 生成待验签串(按业务规则拼接)local sign_str = string.format("%s%s%s%s", req_method, req_uri, req_timestamp, req_body or "")-- 5. 计算HMAC签名并对比local hmac_obj = hmac:new(conf.secret_key, hmac.ALGOS.SHA256)local computed_sign = hmac_obj:final(sign_str, true) -- true表示返回hex格式if computed_sign ~= req_sign thenreturn 403, {message = "invalid signature"}end-- 6. 验签通过,继续转发请求returnend-- 注册插件return { version = 0.1, priority = 2000, -- 插件执行优先级(数值越大越先执行) name = "custom-sign", -- 插件名称 schema = schema, access = access -- 绑定access阶段执行}

(3)启用自定义插件

  • 在 APISIX 配置文件(config.yaml)中添加插件到plugins列表,确保 APISIX 加载插件:

plugins: - custom-sign # 添加自定义插件名称 - hmac-auth - jwt-auth

  • 重启 APISIX:apisix restart
  • 为 Consumer 和 Route 绑定custom-sign插件(同内置插件配置方式)。

方式 3:集成外部验签服务(适合验签逻辑独立部署)

若验签逻辑复杂(如依赖数据库查询密钥、多系统共享验签服务),可通过 APISIX 的ext-plugin(外部插件)或http-logger+request-validator,将验签请求转发到外部服务(如 Java/Python 编写的验签服务),由外部服务返回验签结果,APISIX 根据结果决定是否放行。

核心实现思路:

(1)部署外部验签服务

开发一个验签 API(如http://sign-service:8080/verify),接收 APISIX 转发的请求参数(如请求头、Body、路径),执行验签逻辑,返回{"success": true/false, "message": "..."}

(2)APISIX 配置转发与结果判断使用ext-pluginpre-req(请求前调用外部服务)功能,或通过proxy-rewrite将请求转发到验签服务,再通过response-rewrite判断结果:


# 示例:为Route配置外部验签服务curl http://apisix-admin:9180/apisix/admin/routes/3 -X PUT -d ' { "uri": "/api/v1/pay/*", "plugins": { "ext-plugin-pre-req": { "conf": [ { "name": "ext-plugin-http", # 调用外部HTTP服务的插件 "value": '{"uri":"http://sign-service:8080/verify","method":"POST","timeout":500}' } ] } }, "upstream": {"nodes": {"192.168.1.102:8080": 1}} }'

外部验签服务返回success: false时,APISIX 直接拦截请求并返回错误。

3. 验签配置的最佳实践

  1. 密钥管理

    1. 对称密钥(如hmac-authsecret_key)需通过 APISIX 的配置中心(如 etcd、Nacos)管理,避免硬编码;
    2. 非对称加密(如 JWT RS256)需定期轮换公钥 / 私钥,确保密钥安全。
  2. 防重放攻击

    1. 必须加入 “时间戳 + nonce” 机制(如hmac-authclock_skewnonce_header),防止攻击者复用旧签名;
    2. 可通过 APISIX 的redis插件缓存已使用的nonce,避免重复使用。
  3. 性能优化

    1. 自定义插件中避免频繁 IO 操作(如数据库查询),可将密钥缓存到 APISIX 本地(如core.lrucache);
    2. 对于大 Body 请求,验签时可仅对 Body 的哈希值(如 MD5)进行签名,减少数据处理量。
  4. 日志与监控

    1. 启用 APISIX 的error-log-loggeraccess-logger,记录验签失败日志(如签名不匹配、时间戳过期);
    2. 通过 APISIX 的监控插件(如prometheus)统计验签成功率,及时发现异常请求。

4. 常见问题排查

  1. 验签失败但客户端确认 签名 正确

    1. 检查 “待验签串” 的拼接规则是否与客户端一致(如参数排序、是否包含 Body、大小写敏感);
    2. 确认 APISIX 是否正确读取请求 Body(需在config.yaml中设置proxy_body_temp_path,并确保插件启用 Body 读取)。
  2. JWT 令牌验证失败

    1. 检查algorithm是否匹配(如客户端用 HS256,网关配置 RS256);
    2. 确认令牌未过期(exp字段),且secret/public_key与客户端签名时使用的密钥一致。
  3. 自定义插件不生效

    1. 检查插件是否添加到 APISIX 的plugins列表中;
    2. 通过 APISIX 日志(/usr/local/apisix/logs/error.log)查看插件执行报错信息。
Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐