第一层 传输加密(HTTPS/TLS):保护数据在传输过程中不被窃听。这是底线,没有这层的API就是裸奔。用Let's Encrypt免费证书+nginx配置,零成本。
第二层 签名验证(HMAC-SHA256):防止请求被篡改和伪造。客户端把请求参数+时间戳+nonce随机数+密钥拼起来算一个签名,服务端用同样的方式算一遍,对不上就拒绝。防的是"中间人改了你的参数"。
第三层 内容加密(AES/RSA/SM4):请求体和响应体本身就是密文。即便HTTPS被破解(比如企业内网做了SSL卸载),抓包看到的也是一堆乱码。防的是"即使看到了数据也看不懂"。
⚠️ 关键认知:HTTPS只保护传输过程,数据到了客户端和服务器之后是明文。如果你的API响应里包含用户手机号、身份证号等敏感信息,HTTPS挡不住客户端被逆向。三层都上才是真正的端到端加密。
| 网关 | 类型 | 价格 | 加密能力 | 核心优势 | 适用场景 |
|---|---|---|---|---|---|
| Apache APISIX | 开源 | 免费 | AES/SM4加解密插件,支持请求体和响应体分别加密 | 性能极高(单核2万QPS),插件丰富,热更新不停机。支持自定义Lua插件做复杂加密逻辑 | 高并发场景、需要国产化支持(SM4国密)、技术团队有能力自建 |
| Kong | 开源 | 免费 | 通过插件实现AES/RSA加密,社区插件丰富 | 生态最成熟,文档最全,社区插件数量多。企业版有商业支持 | 大中型团队、需要商业支持、已有Kong基础设施的 |
| Nginx + Lua脚本 | 自建 | 免费 | 在nginx.conf里用Lua写加解密逻辑,对指定路由自动加密 | 最轻量,不需要额外部署网关服务。适合已经在用Nginx的团队 | 小团队、不想引入新组件、Nginx已有运维经验 |
| 腾讯云API网关 | 云服务 | 按量付费 | 内置加解密插件,可视化配置加密规则,支持多种算法 | 零运维,控制台点几下就配好。适合不想自己维护网关的团队 | 云上部署、运维能力有限、需要快速上线的 |
# APISIX配置示例:对 /api/* 路由自动加解密(AES)# 在APISIX的路由配置中添加以下插件:curl http://127.0.0.1:9180/apisix/admin/routes/1 -X PUT -d '{"uri": "/api/*","plugins": {"cors": {}, # 跨域"body-transformer": { # APISIX的body转换插件"request": {"template": "" # 解密逻辑在自定义插件中实现}}}}'# 实际上需要通过自定义Lua插件实现完整的AES加解密# APISIX社区有现成的 crypto 插件支持AES/SM4# 安装后在路由配置中启用即可,无需改后端代码Java / Spring Boot
写一个RequestBodyAdvice和ResponseBodyAdvice,配合@ControllerAdvice注解,拦截所有API请求。请求进来先解密body再传给Controller,Controller返回的body自动加密后返回。加一个注解@Encrypt标记需要加密的接口,没标的不处理。200个接口10分钟配完。
Python / Flask / FastAPI
用装饰器或中间件统一处理。FastAPI写一个Middleware,在dispatch方法里对request body解密、对response body加密。Flask用before_request和after_request钩子。所有接口自动生效,不需要逐个改。
Node.js / Express
写一个Express中间件app.use(),在req.on('data')里收集加密的请求体、解密后替换req.body,在res.send上包装一层加密。npm上有现成的express-encrypt-middleware可以用。
PHP / Laravel

写一个全局中间件Middleware,在handle方法里解密request body,在terminate里加密response。Laravel的中间件天然支持分组,把需要加密的路由放到一个middleware group里即可。
# Python FastAPI中间件示例:AES自动加解密from fastapi import FastAPI, Requestfrom Crypto.Cipher import AESimport base64, jsonAES_KEY = b"your-32-byte-key-here-xxxxxx" # 32字节密钥app = FastAPI()@app.middleware("http")async def encrypt_middleware(request: Request, call_next):# 解密请求体body = await request.body()if body:cipher = AES.new(AES_KEY, AES.MODE_ECB)decrypted = cipher.decrypt(base64.b64decode(body))# 将解密后的数据注入request(需要自定义实现)response = await call_next(request)# 加密响应体if response.body:cipher = AES.new(AES_KEY, AES.MODE_ECB)encrypted = base64.b64encode(cipher.encrypt(response.body))response.body = encryptedreturn response# 业务接口完全不用改,自动加密@app.get("/api/user/info")async def get_user():return {"name": "张三", "phone": "138****1234"} # 返回时自动加密标准签名流程(HMAC-SHA256)
① 客户端生成时间戳(timestamp)和随机数(nonce)
② 把所有请求参数按字母排序,拼成字符串:param1=value1¶m2=value2×tamp=xxx&nonce=yyy
③ 用密钥(secret_key)对上述字符串做HMAC-SHA256,得到签名sign
④ 请求时在Header里带上 timestamp、nonce、sign
⑤ 服务端收到后,用同样的方式算一遍签名,对比是否一致
⑥ 检查时间戳是否在允许范围内(如±5分钟),防止重放
⑦ 检查nonce是否已使用过(Redis记录),防止同一个请求重复发送
# Python服务端签名验证中间件(FastAPI)import hmac, hashlib, time, jsonfrom fastapi import Request, HTTPExceptionSECRET_KEY = b"your-secret-key"TIME_WINDOW = 300 # 允许5分钟时间差used_nonces = set() # 生产环境用Redisasync def verify_signature(request: Request):timestamp = request.headers.get("X-Timestamp")nonce = request.headers.get("X-Nonce")sign = request.headers.get("X-Sign")# 1. 防重放:检查时间窗口if abs(time.time() - int(timestamp)) > TIME_WINDOW:raise HTTPException(403, "请求已过期")# 2. 防重放:检查nonce是否已使用if nonce in used_nonces:raise HTTPException(403, "重复请求")used_nonces.add(nonce)# 3. 验签:用同样方式重新计算签名body = await request.body()params = json.loads(body) if body else {}params["timestamp"] = timestampparams["nonce"] = noncesorted_str = "&".join(f"{k}={v}" for k, v in sorted(params.items()))expected_sign = hmac.new(SECRET_KEY, sorted_str.encode(), hashlib.sha256).hexdigest()if sign != expected_sign:raise HTTPException(403, "签名验证失败")| 国密算法 | 对标国际算法 | 类型 | 用途 | 推荐库 |
|---|---|---|---|---|
| SM2 | RSA / ECC | 非对称 | 加密AES/SM4的密钥,建立安全通道 | gmssl(Python)、sm-crypto(JS)、Hutool(Java)、GM-JS(JS) |
| SM3 | SHA-256 | 摘要 | 签名生成、数据完整性校验 | 同上各库都支持 |
| SM4 | AES | 对称 | 加密请求体和响应体,高性能加解密 | 同上各库都支持 |
国密混合加密方案(对标AES+RSA)
① 前端用SM2公钥加密随机生成的SM4密钥
② 用SM4密钥加密业务数据(请求体)
③ 服务端用SM2私钥解密得到SM4密钥
④ 用SM4密钥解密业务数据
⑤ 响应数据同样用SM4加密返回
工具选型:前端用sm-crypto(npm),后端Java用Hutool的SmUtil,Python用gmssl。前后端国密方案在APISIX网关层也可以通过自定义插件实现,业务代码完全不用改。
场景一:已有项目,200个接口,快速批量加密
推荐:APISIX网关 + AES加密插件。在APISIX里配置一条路由规则,指定/api/*路径自动加解密,后端代码零改动。配置好之后200个接口全部自动加密,客户端对接加密SDK即可。
备选:框架拦截器/中间件方案。Spring Boot写两个Advice,Flask/FastAPI写一个Middleware,同样不改业务代码。
时间:APISIX方案半天搭好,框架中间件方案1-2天。
场景二:新项目,从零搭建安全API
推荐:HTTPS + 签名验证(HMAC-SHA256) + 内容加密(AES-GCM)三层全上。前端用CryptoJS + axios拦截器,后端用Hutool + 中间件。签名验证用Redis存储nonce防重放。
国密版:把AES换成SM4,RSA换成SM2,SHA换成SM3。前端sm-crypto,后端Hutool SmUtil。
时间:前后端各2-3天,含测试。

场景三:云上部署,不想自建网关
推荐:腾讯云/阿里云API网关 + 内置加密插件。控制台配置加密规则,支持AES/SM4,可视化操作。接入方式是在你的域名DNS里加一条CNAME指向网关地址。
优势:零运维,自带监控和限流,按量付费。中小项目一个月几十块钱。
时间:2小时内配完。
坑一:只加密不签名,重放攻击一打一个准
加密后的密文被截获,攻击者不需要解密,直接把整个请求原样重发100遍。你的接口如果没做防重放(时间戳窗口+nonce去重),每次都会正常处理。签名验证里的timestamp和nonce就是防这个的,不能省。
坑二:AES用ECB模式,加密后能看到数据模式
ECB模式是最简单的AES模式,但同一段明文加密后的密文完全一样。如果请求体里有{"password":"123456"},每次加密结果都相同,攻击者虽然解不开但知道"这个请求和上次一样"。用CBC或GCM模式,每次加密结果不同。
坑三:密钥硬编码在前端代码里
前端打包后的JS里能直接搜到AES密钥。对称加密的密钥绝不能写死在前端代码里。正确做法是用非对称加密(RSA/SM2)——服务端发公钥给前端,前端用公钥加密一个随机生成的AES密钥发给服务端,之后用这个动态协商的AES密钥通信。
坑四:加密后忘了改接口测试和文档
接口加密之后,Postman直接调不通了,Swagger文档也废了。需要同步更新:①接口文档里注明加密方式和密钥协商流程;②测试工具配置加解密脚本(Apifox支持前置后置加解密脚本);③给内部调用方发SDK。加密上线前先做一次完整的端到端测试。
