This repository has been archived on 2026-05-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
all-in-kingsoft/hzh/MS/02-服务治理/网关鉴权策略.md
T

178 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [microservice, api-gateway, auth, service-mesh, mTLS, jwt, rbac]
create time: 2026-04-30 15:30
---
# 网关鉴权分层设计
## 概述
探讨"鉴权是否应该全部放在网关统一处理"这个常见架构争议,给出兼顾统一性和灵活性的分层鉴权方案。
## 问题的本质
### 一刀切的陷阱
> [!question] 引出思考
> 有人说:"鉴权放到网关统一做,避免每个服务重复写。"但如果某个内部服务不需要鉴权呢?或者不同团队要求不同的鉴权方式呢?你怎么设计才能兼顾统一性和灵活性?
核心矛盾在于:**网关做鉴权的优势是集中化和性能**,但代价是 **失去对差异化需求的表达能力**。现实中至少存在三种网关无法处理的场景:
| 场景 | 例子 | 为什么网关搞不定 |
|------|------|----------------|
| **内部调用免鉴权** | A服务直调B服务的内部查询接口 | 外部请求到不了这里,网关做无意义开销 |
| **不同团队标准不同** | 财务系统要求 OAuth2+MFA,运营后台只需 API Key | 策略硬编码在网关里会变成巨石中间件 |
| **精细化权限控制** | 订单服务需要判断"用户A是否有权看这笔订单" | 网关不知道业务数据归属关系 |
## 三层鉴权架构
```mermaid
flowchart TB
subgraph L1["第一层:网关层 - 你是谁?"]
G1["JWT / Token 校验"]
G2["IP 白名单"]
G3["基础限流"]
end
subgraph L2["第二层:服务入口层 - 你能做什么?"]
S1["内部服务间 Token 验证"]
S2["mTLS 双向认证"]
S3["角色 / 权限检查"]
end
subgraph L3["第三层:业务逻辑层 - 你能操作这个资源吗?"]
B1["行级权限:userId == order.userId"]
B2["数据范围:仅本部门可见"]
B3["审批状态机流转"]
end
Client["外部请求"] --> GW["API Gateway<br/>L1 快速过滤非法请求"]
GW --> svcA["Order Service"]
GW --> svcB["Finance Service"]
svcA --> L2
svcB --> L2
L2 --> L3
svcA -.->|"内部调用"| svcC["Inventory Service<br/>仅 L2 鉴权,跳过 L1"]
svcC --> L2
```
### 三层职责划分
> [!summary] 鉴权分层原则
>
> | 层级 | 关注点 | 粒度 | 谁来实现 |
> |------|--------|------|---------|
> | **L1 网关层** | 身份认证 — "你是合法用户吗?" | 粗粒度(用户级) | 平台团队统一维护 |
> | **L2 服务层** | 接口授权 — "你有权限调这个接口吗?" | 中粒度(角色/服务级) | 各业务团队自己定义 |
> | **L3 业务层** | 数据权限 — "你能操作这条数据吗?" | 细粒度(数据级) | 业务逻辑自身实现 |
**设计哲学**:越往上过滤越早失败,但信息越少;越往下信息越多但成本越高。每一层只做它最擅长的那件事。
## 内部服务调用的鉴权策略
对于内部服务互不调用网关的问题,答案是:**不是不需要鉴权,而是用更轻量级的鉴权**。
```go
// 服务间调用:用短期签名 Token 代替完整 JWT
func serviceToServiceAuth(serviceID string, target string) string {
// 使用共享密钥 HMAC 签发短时效 Token
payload := map[string]string{
"sub": serviceID,
"target": target,
"exp": time.Now().Add(5 * time.Minute).Format(time.RFC3339),
}
return signWithSharedKey(payload) // 比解析完整 JWT 便宜得多
}
// 接收方校验
func verifyInterServiceToken(token string) error {
claims, err := verifyHMAC(token)
if err != nil {
return fmt.Errorf("无效的内部服务 Token")
}
// 额外检查:该服务是否有权限访问目标服务
if !allowedAccess(claims.ServiceID, claims.Target) {
return fmt.Errorf("服务 %s 无权调用 %s", claims.ServiceID, claims.Target)
}
return nil
}
```
> [!note] 解释
> 这段代码展示了两个关键点:一是用 **HMAC 短期 Token** 替代完整的 JWT 校验链,将延迟控制在 ~1ms 量级;二是在校验身份之外还做了 **服务间的访问控制检查**,避免被入侵的服务冒充其他服务。
内部鉴权方案对比:
| 方案 | 延迟 | 安全性 | 适用场景 |
|------|------|--------|---------|
| 完全跳过鉴权 | ~0ms | ❌ 不安全 | 同 Pod 内 Sidecar 互调 |
| 简单 Token 签名 | ~1ms | ⚠️ 中等 | 同一安全域内的服务互调 |
| mTLS 双向认证 | ~2ms | ✅ 高 | 跨租户、跨团队、混合云环境 |
## 多团队差异化鉴权方案
针对第二个问题——不同团队需要不同的鉴权方式——推荐用 **策略即代码(Policy-as-Code)** + **可插拔鉴权中间件**。
```go
// 鉴权处理器接口 — 每个团队实现自己的策略
type AuthMiddleware interface {
Name() string
CanHandle(ctx *Context) bool // 这个服务 / 路由是否适用我的策略
Authenticate(w http.ResponseWriter, r *http.Request) bool
}
// 注册表模式:运行时动态装配
var registry = make(map[string]AuthMiddleware)
func Register(name string, m AuthMiddleware) {
registry[name] = m
}
// 路由级别的策略声明
// router.go
router.Use(multiAuth(
"jwt-gateway", // L1 网关已经校验过的 JWT
"rbac-finance", // 财务团队的额外 RBAC 校验
))
```
> [!note] 解释
> 通过接口抽象 + 注册表模式,每个团队可以在不修改基础设施代码的前提下,将自己团队的鉴权策略注册到系统中。`multiAuth` 按顺序执行所有匹配的中间件,任一失败则拒绝请求。
这样做的收益:
> [!tip] 核心收益
>
> 1. **统一但不僵化**:网关承担公共部分,各服务按需叠加自定义策略
> 2. **渐进式迁移**:老服务可以逐步从简单鉴权升级到新策略,不用一次性改造
> 3. **职责边界清晰**:平台团队维护基础设施鉴权,业务团队维护业务鉴权
> 4. **审计友好**:每层的鉴权结果独立记录,出问题时能精确到是哪一层拦截的
## 什么时候应该让网关不做鉴权?
> [!warning] 反模式警告
>
> 以下情况考虑 **绕过网关鉴权**,由服务自行处理:
>
> 1. **内部工具/API**:定时任务脚本、后台管理接口,不走对外网关
> 2. **IoT 设备直连**:设备有自己的凭证体系,不适合套用用户 JWT
> 3. **Webhook 回调**:上游厂商的回调签名验证应在消费侧服务完成
> 4. **高性能扫描场景**:高频行情推送,鉴权开销占总延迟 >5% 时可考虑批量校验
## 总结
这套设计的核心理念是 **"默认严格、按需放宽"**:
- 网关做 **防御纵深的第一道**——挡住明显非法的请求,保护后方
- 每个服务保持 **独立的鉴权决策能力**——因为最了解自身需求的只有服务本身
- 服务间通信用 **轻量级信任机制**——不过度设计,但绝不调包
最终达到 **统一的安全基线 + 灵活的差异化策略** 的平衡。
## 关联笔记
- [[hzh/MS/02-服务治理]] — 服务治理总览