前言
系列写到现在,我们搭建了网关集群、拆解了热更新原理、手写了限流插件。但很多同学问了一个更实际的问题:
“我们后端人手少,能不能把认证、安全、日志这些通用逻辑全丢网关层,后端只写业务?”
答案是:能,而且应该这样做。
这篇文章,我梳理了10 个高频场景对应的网关插件方案。每个方案都是一套可落地的配置,放网关层处理,后端零改动。
本文用"网关 G"代称,所有插件名为 demo 名称。
总览:一张图看懂
┌──────────────────────────────────────────────────┐ │ 客户端请求 │ └─────────────────────┬────────────────────────────┘ ↓ ┌──────────────────────────────────────────────────┐ │ API 网关层 │ │ │ │ ① 安全防护层:IP 黑白名单 → CORS → WAF │ │ ② 认证鉴权层:Token/JWT/Key-Auth │ │ ③ 流量控制层:限流 → 熔断 → 降级 │ │ ④ 协议转换层:HTTP ↔ gRPC ↔ WebSocket │ │ ⑤ 内容改写层:URL重写 → 请求/响应改写 │ │ ⑥ 可观测层:日志 → 指标 → 链路追踪 │ │ │ ├──────────────────────────────────────────────────┤ │ 后端服务(只写业务逻辑) │ └──────────────────────────────────────────────────┘六个层次,十个核心插件,全部配置即生效。
场景一:IP 黑白名单——把攻击挡在门外
后端传统做法
# 每个服务都要写一遍ALLOWED_IPS={"10.0.0.0/8","172.16.0.0/12"}ifrequest.remote_addrnotinALLOWED_IPS:return403,"Forbidden"问题:每个服务都要写、IP 变更要改代码重启、无法统一管理。
网关方案
curlhttp://127.0.0.1:9080/apisix/admin/routes/demo-admin\-H"X-API-KEY: demo-admin-key"\-XPATCH-d' { "plugins": { "ip-restriction": { "whitelist": [ "10.0.0.0/8", "172.16.0.0/12", "203.0.113.50" ] } } }'效果:不在白名单内的请求直接返回 403,后端感知不到这些请求。
白名单外请求:客户端 → 网关 → 403 → 到不了后端 白名单内请求:客户端 → 网关 → 放行 → 后端正常处理场景二:CORS 跨域——前端不再报红
后端传统做法
# 每个响应都要手动加 CORS 头response.headers["Access-Control-Allow-Origin"]="*"response.headers["Access-Control-Allow-Methods"]="GET,POST,PUT,DELETE"response.headers["Access-Control-Allow-Headers"]="Content-Type,Authorization"问题:一旦漏了一个接口,前端就 CORS 报错排查半天。
网关方案
curlhttp://127.0.0.1:9080/apisix/admin/routes/demo-api\-H"X-API-KEY: demo-admin-key"\-XPATCH-d' { "plugins": { "cors": { "allow_origins": "https://demo-app.example.com", "allow_methods": "GET,POST,PUT,DELETE,OPTIONS", "allow_headers": "Authorization,Content-Type,X-Request-Id", "expose_headers": "X-Request-Id,X-Response-Time", "max_age": 3600, "allow_credential": true } } }'省掉的后端代码量:每个接口 4-6 行 × N 个接口 = 几百行代码 + 无尽的调试时间。
场景三:JWT 认证——统一鉴权
后端传统做法
# 每个接口都要校验 Tokentoken=request.headers.get("Authorization","").replace("Bearer ","")try:payload=jwt.decode(token,SECRET_KEY,algorithms=["HS256"])request.user_id=payload["sub"]exceptjwt.ExpiredSignatureError:return401,"Token expired"exceptjwt.InvalidTokenError:return401,"Invalid token"网关方案
curlhttp://127.0.0.1:9080/apisix/admin/routes/demo-api\-H"X-API-KEY: demo-admin-key"\-XPATCH-d' { "plugins": { "jwt-auth": { "key": "demo-jwt-key", "secret": "demo-secret-key-2026-xxxxxxxxxxxxxxxxxx" } } }'效果:
无 Token 或 Token 无效 → 网关直接返回 401 Token 有效 → 网关把 user_id 注入请求头 → 后端直接读 header后端代码简化为:
@app.route("/api/user/profile")defprofile():user_id=request.headers.get("X-User-Id")# 网关已经帮你验过了returnget_user_profile(user_id)省掉的后端代码量:每个需要鉴权的接口 8-15 行 × 几十个接口 + JWT 工具类维护。
场景四:Key-Auth——开放 API 的钥匙
对外暴露给合作伙伴的 API,用 JWT 太重了,一个 API Key 就够。
# 先创建消费者curlhttp://127.0.0.1:9080/apisix/admin/consumers\-H"X-API-KEY: demo-admin-key"\-XPUT-d' { "username": "partner-demo", "plugins": { "key-auth": { "key": "demo-api-key-xxxxxxxxxxxxxx" } } }'# 路由上启用 key-authcurlhttp://127.0.0.1:9080/apisix/admin/routes/demo-open-api\-H"X-API-KEY: demo-admin-key"\-XPATCH-d' { "plugins": { "key-auth": {} } }'客户端只需:
curlhttp://demo-gateway.example.com/open-api/query\-H"apikey: demo-api-key-xxxxxxxxxxxxxx"比在后端写 Key 验证省了多少:一套 Key 管理后台 + SQL 表 + CRUD 接口 → 网关三行配置搞定。
场景五:统一限流——防止接口被刷
这个我们在第四篇详细讲过,这里直接给生产配置:
curlhttp://127.0.0.1:9080/apisix/admin/routes/demo-public-api\-H"X-API-KEY: demo-admin-key"\-XPATCH-d' { "plugins": { "limit-count": { "count": 100, "time_window": 60, "key_type": "var", "key": "remote_addr", "rejected_code": 429, "rejected_msg": "请求太频繁,请 60 秒后再试" }, "limit-conn": { "conn": 10, "burst": 5, "default_conn_delay": 0.5, "key_type": "var", "key": "remote_addr" } } }'两层限流:
limit-count:请求频率限流(100 次/分钟)limit-conn:并发连接数限流(同时最多 10 个连接)
后端零代码,接口安全防护一步到位。
场景六:熔断降级——保护后端不被压垮
后端传统做法
# 需要引入熔断器库,比如 PyBreaker@circuit_breaker(fail_max=3,timeout=30)defcall_downstream_service():returnrequests.get("http://other-service/api/data")网关方案
curlhttp://127.0.0.1:9080/apisix/admin/upstreams/demo-upstream\-H"X-API-KEY: demo-admin-key"\-XPATCH-d' { "checks": { "active": { "type": "http", "http_path": "/health", "timeout": 3, "concurrency": 10, "healthy": { "interval": 5, "successes": 2 }, "unhealthy": { "interval": 5, "http_failures": 3, "timeouts": 3 } } }, "retries": 2, "retry_timeout": 5 }'效果:
- 后端节点连续失败 3 次 → 网关自动标记为不可用
- 不可用期间请求自动路由到健康的节点
- 节点恢复后自动上线
后端一行不用改,高可用由网关保障。
场景七:URL 重写——平滑迁移旧接口
后端要重构接口,但前端没法同步改?网关帮你无缝切换:
curlhttp://127.0.0.1:9080/apisix/admin/routes/demo-legacy-api\-H"X-API-KEY: demo-admin-key"\-XPUT-d' { "uri": "/v1/users/*", "plugins": { "proxy-rewrite": { "uri": "/api/v2/users/$1", "regex_uri": ["/v1/users/(.*)", "/api/v2/users/$1"] } }, "upstream": { "type": "roundrobin", "nodes": { "demo-backend-v2:8080": 1 } } }'映射关系:
| 客户端请求 | 网关转发给后端 |
|---|---|
/v1/users/123 | /api/v2/users/123 |
/v1/users/456/profile | /api/v2/users/456/profile |
前端不用改、客户端不用升级、后端自由重构。
场景八:响应改写——数据脱敏 & 统一格式
后端返回的数据需要脱敏?不需要改业务代码:
curlhttp://127.0.0.1:9080/apisix/admin/routes/demo-user-api\-H"X-API-KEY: demo-admin-key"\-XPATCH-d' { "plugins": { "response-rewrite": { "body": [ { "action": "regex_replace", "regex": "\\"phone\\":\\"([0-9]{3})([0-9]{4})([0-9]{4})\\"", "replace": "\\"phone\\":\\"$1****$3\\"" } ], "headers": { "add": { "X-Response-Time": "$upstream_response_time", "X-Served-By": "demo-gateway-node-1", "X-Content-Type-Options": "nosniff" }, "remove": [ "X-Powered-By", "Server", "X-Internal-Debug" ] } } } }'效果:
后端返回:{"phone": "13812345678", ...} ↓ 网关脱敏 客户端收到:{"phone": "138****5678", ...}同时自动注入的响应头:请求耗时、网关节点、安全头——后端一行代码都没写。
场景九:统一日志——请求审计不留死角
后端每个接口写日志太麻烦,网关一次搞定:
curlhttp://127.0.0.1:9080/apisix/admin/routes/demo-audit-api\-H"X-API-KEY: demo-admin-key"\-XPATCH-d' { "plugins": { "http-logger": { "uri": "http://demo-log-service:8000/ingest", "batch_max_size": 100, "max_retry_count": 3, "retry_delay": 1, "include_req_body": true, "include_resp_body": false }, "file-logger": { "path": "/var/log/demo-gateway/access.log", "log_format": { "time": "$time_iso8601", "remote_addr": "$remote_addr", "method": "$request_method", "uri": "$uri", "status": "$status", "upstream_addr": "$upstream_addr", "request_time": "$request_time", "upstream_response_time": "$upstream_response_time", "user_agent": "$http_user_agent", "request_id": "$http_x_request_id" } } } }'每条请求自动记录:
{"time":"2026-06-05T10:30:12+08:00","remote_addr":"203.0.113.100","method":"POST","uri":"/api/users","status":201,"upstream_addr":"demo-backend-1:8080","request_time":0.045,"upstream_response_time":0.038,"user_agent":"Demo-Client/2.0","request_id":"a1b2c3d4e5f6"}后端需要写什么?零。网关把审计日志投递到日志服务,后端甚至不知道日志的存在。
场景十:简易灰度发布——新版本先跑 10% 流量
不需要 Service Mesh,不用改 K8s 配置,网关直接搞定:
curlhttp://127.0.0.1:9080/apisix/admin/routes/demo-canary\-H"X-API-KEY: demo-admin-key"\-XPUT-d' { "uri": "/demo/product/*", "upstream": { "type": "chash", "hash_on": "header", "key": "X-Canary-User", "nodes": { "demo-backend-stable:8080": 9, "demo-backend-canary:8080": 1 } } }'流量分配:90% 走稳定版,10% 走金丝雀版本。
配合条件路由,还能做得更精细:
# 规则 1:优先匹配——白名单用户走金丝雀curlhttp://127.0.0.1:9080/apisix/admin/routes/demo-canary-beta\-H"X-API-KEY: demo-admin-key"\-XPUT-d' { "uri": "/demo/product/*", "vars": [["http_x_canary_user", "==", "true"]], "priority": 10, "upstream": { "nodes": {"demo-backend-canary:8080": 1} } }'# 规则 2:兜底匹配——普通用户走稳定版curlhttp://127.0.0.1:9080/apisix/admin/routes/demo-canary-default\-H"X-API-KEY: demo-admin-key"\-XPUT-d' { "uri": "/demo/product/*", "priority": 0, "upstream": { "nodes": {"demo-backend-stable:8080": 1} } }'进阶:按用户 ID 哈希的金丝雀:
curlhttp://127.0.0.1:9080/apisix/admin/routes/demo-canary-uid\-H"X-API-KEY: demo-admin-key"\-XPATCH-d' { "upstream": { "type": "chash", "hash_on": "header", "key": "X-User-Id", "nodes": { "demo-backend-stable:8080": 90, "demo-backend-canary:8080": 10 } } }'同一用户始终落在同一版本,不会出现同一用户一会儿看到新版、一会儿看到旧版的问题。
一张表总结:十个场景 vs 后端代码量
| 场景 | 网关插件 | 配置行数 | 省掉的后端代码 |
|---|---|---|---|
| IP 黑白名单 | ip-restriction | 8 行 | 每个服务一个中间件(~30行) × N 个服务 |
| CORS | cors | 7 行 | 每个接口手动加头(~6行) × 几十个接口 |
| JWT 认证 | jwt-auth | 5 行 | 装饰器/中间件 + JWT 工具类(~100行) |
| API Key | key-auth | 3 行 | Key 管理后台 + 校验逻辑(~200行) |
| 限流 | limit-count | 8 行 | 每个接口的限流逻辑(~20行) × N 个接口 |
| 熔断降级 | active health check | 15 行 | 熔断器库引入 + 集成代码(~80行) |
| URL 重写 | proxy-rewrite | 6 行 | Nginx 配置变更 + 后端兼容代码 |
| 响应改写 | response-rewrite | 10 行 | 每个接口的脱敏/格式化代码 |
| 统一日志 | http-logger | 12 行 | 每个接口的日志打点(~5行) × N 个接口 |
| 灰度发布 | chash upstream | 8 行 | K8s Istio 配置 / 自研流量调度系统 |
| 合计 | 10 个插件 | ~80 行配置 | 数百到数千行后端代码 |
组合实战:一条路由挂 5 个插件
把上面多个场景拼在一起,一条路由搞定安全 + 鉴权 + 限流 + 日志 + 版本分流:
curlhttp://127.0.0.1:9080/apisix/admin/routes/demo-full-stack\-H"X-API-KEY: demo-admin-key"\-XPUT-d' { "name": "demo-full-stack-route", "uri": "/api/business/*", "methods": ["GET", "POST", "PUT", "DELETE"], "plugins": { "ip-restriction": { "whitelist": ["10.0.0.0/8"] }, "cors": { "allow_origins": "https://demo-app.example.com", "allow_methods": "GET,POST,PUT,DELETE", "allow_headers": "Authorization,Content-Type" }, "jwt-auth": { "key": "demo-jwt-key", "secret": "demo-jwt-secret" }, "limit-count": { "count": 200, "time_window": 60, "key_type": "var", "key": "http_x_user_id" }, "response-rewrite": { "headers": { "add": { "X-Request-Id": "$request_id", "X-Response-Time": "$upstream_response_time" }, "remove": ["X-Powered-By", "Server"] } } }, "upstream": { "type": "roundrobin", "nodes": { "demo-backend-stable:8080": 9, "demo-backend-canary:8080": 1 } } }'请求到达网关后的处理顺序:
1. IP 校验 → 不在白名单 → 403,后端无感知 2. CORS 处理 → 跨域请求自动响应 OPTIONS 3. JWT 鉴权 → Token 无效 → 401 4. 限流检查 → 超阈值 → 429,提示"稍后重试" 5. 金丝雀分流 → 10% 流量走 canary 版本 6. 响应头注入 → X-Request-Id / X-Response-Time后端只收到合法、未超限、已完成分流的请求。它要做的,只有业务逻辑。
运维小贴士
1. 插件执行顺序调试
如果不确定插件的执行顺序,可以在网关日志中开启 debug 模式:
nginx_config:error_log_level:"debug"日志中会看到每个插件的执行记录:
[debug] plugin: ip-restriction, phase: access, result: allow [debug] plugin: cors, phase: access, result: allow [debug] plugin: jwt-auth, phase: rewrite, result: allow [debug] plugin: limit-count, phase: access, result: allow (count=12/200)2. 插件性能关注
并非所有插件都零开销,以下插件对性能影响较大,需要关注:
| 插件类型 | 性能影响 | 原因 |
|---|---|---|
| 复杂正则路由匹配 | 低 | 前缀树已优化 |
| JWT 验证 | 低 | 毫秒级 |
| 限流计数 | 极低 | 纯内存操作 |
| 请求体日志 | 高 | 需要缓冲请求体 |
| 大正则响应改写 | 高 | 需要缓冲并扫描响应体 |
| WAF 规则 | 中 | 正则匹配开销 |
3. 插件全局启用 vs 路由级启用
# 全局规则:所有路由都生效curlhttp://127.0.0.1:9080/apisix/admin/global_rules/1\-H"X-API-KEY: demo-admin-key"\-XPUT-d' { "plugins": { "prometheus": {}, "response-rewrite": { "headers": { "add": { "X-Gateway-Node": "demo-gateway-01" } } } } }'原则:
- 全局:监控指标、通用安全头、统一响应头
- 路由级:认证、限流、灰度发布(不同路由策略不同)
总结
这篇文章的核心观点就一句话:
让网关做基础设施的事,让后端做业务的事。
十个场景的配置我都写好了,直接改参数就能用到你们的项目里。不用感谢,点赞收藏就是最好的鼓励👍
如果这篇文章让你少写了两百行代码,请务必点赞 + 收藏 + 关注三连 🙏