Skip to content

4.2 请求处理链路 ​

概述

一个受保护的写请求依次穿过七层中间件与 Bind 包装层。理解这条链路是排查 401/403/429/演示拦截/日志缺失的钥匙。注册顺序即执行顺序(router.go s.Use)。

中间件链总览 ​

text
CORS → Response → RateLimit → Demo → JWT → Tenant → OperationLog → 处理器
                                                              (Bind 包装层做权限校验)
#中间件短路行为
1CORS预检 204;头设置
2ResponseSVG 加固;元信息入 ctx;panic → 500 信封
3RateLimit超限 code=429
4Demoapp.demo=true 时写操作 code=1015
5JWT无效/黑名单 code=401
6Tenant租户禁用 1006 / 过期 1005
7OperationLog不短路,异步采集
8Bind 包装层权限不足 code=403;参数校验失败 400

请求生命周期(12 步) ​

text
 1  CORS            预检/头设置
 2  Response        元信息入 ctx;defer panic 兜底
 3  RateLimit       固定窗口计数,超限 429
 4  Demo            演示模式拦截写操作
 5  JWT             解析校验 token → 注入身份;黑名单 401
 6  Tenant          租户状态/过期校验(60s 缓存)
 7  OperationLog    记 start,Next()
 8  Bind 包装层     元数据入 ctx → HasPerm 权限校验(403 短路)→ Parse 参数 → 调 controller
 9  controller      转发 service 接口
10  logic           ModuleSpec 流水线 / 手写业务;dao.Ctx 查询(钩子已注入条件)
11  response.Write  信封写出(code/body 记入 ctx)
12  OperationLog    组装记录 → 异步通道 → 批量落库

时序图(受保护写请求 + 日志异步落库) ​

text
Client        中间件链              Bind包装/业务           缓存      DB/日志
  │  POST /api/user/update             │                   │           │
  │───▶│ CORS 头/预检                    │                   │           │
  │    │ Response: 元信息入ctx, defer recover               │           │
  │    │ RateLimit ──────────────────────────────────────▶ SET/GET 计数│
  │    │ Demo(app.demo=false 放行)      │                   │           │
  │    │ JWT: Parse+黑名单 ───────────────────────────────▶ Exists jti │
  │    │      注入身份; Roles/Perms/DataScope/DeptId ──────▶ 命中缓存   │
  │    │          未命中 → IdentityLoader 回源 ───────────────────────▶ user/role/menu
  │    │ Tenant: status/expire ───────────────────────────▶ tenant:info │
  │    │ OperationLog: start=now        │                   │           │
  │    │        │                       │                   │           │
  │    │        ▼                       ▼ HasPerm(403?短路) │           │
  │    │                    Parse(req) → logic.Update       │           │
  │    │                    spec 流水线: BeforeUpdate →      │           │
  │    │                    saveTransform → 唯一预检 → ────────────────▶ UPDATE
  │    │                    (hooks 盖章 update_time/user)   │           │
  │    │        ▼                       │                   │           │
  │◀───│ WriteJson{code:0,...}(code/body 记 ctx)           │           │
  │    │ OperationLog: 组装 entry(脱敏 param/result)        │           │
  │    │      select{ logChan<-entry } 非阻塞                 │           │
  │    │                       worker: 攒批 100条/1s ──────────────────▶ INSERT operation_log

异常分支:

text
* 任一中间件短路(429 / 演示拦截 / 401 / 403)→ 直接 WriteJson,后续不执行
* panic → Response recover → 500 信封(已写出响应则仅记录日志)
* logChan 满 → 丢弃并计数告警(每丢 100 条输出一次 Warning),绝不阻塞业务

各层关键实现速查 ​

层实现位置要点
CORSmiddleware/cors.gocors.allowOrigin 空或 * 走框架默认;具体来源显式回显凭证
Responsemiddleware/response.go.svg 强制 attachment+nosniff(防存储型 XSS);realClientIP/UserAgent 入 ctx
RateLimitmiddleware/ratelimit.go固定窗口;键 ratelimit:{ip}:{path}:{窗口起点};计数经 utility/cache 多实例共享;存储异常放行
Demomiddleware/demo.goGET/HEAD/OPTIONS 与公开白名单放行;其余写操作拒绝
JWTmiddleware/jwt.goParse(HS256/过期/签发者)→ 黑名单 Exists → 注入身份(缓存 300s,回源 LoadIdentity)
Tenantmiddleware/tenant.go缓存 60s;租户不存在按禁用处理;数据库基础错误放行(防存储抖动全站不可用)
OperationLogmiddleware/operation_log.go有界通道 2048 + 单 worker;批量 100 条或 1s flush;参数脱敏 password/salt/token → ***
Bindrouter/bind.go权限校验 + r.Parse(gvalid)+ 信封写出;BodyUnmarshaler 逃生舱

真实 IP 与受信代理 ​

text
util.go realClientIP:
  直连来源不在 api.proxyIps(逗号分隔,精确 IP 与 CIDR)内 → 直接用 GetRemoteIp()
  在列表内 → 才信任 X-Forwarded-For 首段 / X-Real-IP

防止伪造 XFF 绕过限流或污染日志。Nginx 反代部署务必把反代地址加入 api.proxyIps。

排障速查 ​

症状检查点
401token 过期/黑名单(登出、轮换、强退都会拉黑 jti)
403gmeta permission ↔ 菜单节点 ↔ 角色勾选 ↔ 身份缓存(300s)
429限流阈值;认证入口独立 authLimit=10
1015app.demo=true 拦截写操作
1005/1006租户过期/禁用(Tenant 中间件,缓存 60s)
日志缺失operationLog.enable / log 标签空 / logQuery 关 / 通道满告警
panic 无堆栈server.errorStack=false;看服务端日志

小蚂蚁云团队 · 提供技术支持