微服务的认证鉴权为什么要下沉到网关?网关统一鉴权怎么做?
简化版
**微服务架构下,如果每个服务都自己做「认证 + 鉴权」,会有大量重复代码、逻辑不一致、维护困难——所以主流做法是把「认证鉴权下沉到 API 网关」:网关作为统一入口,先做认证鉴权,通过了才转发给后端服务。**核心流程:① 用户登录,认证服务颁发一个 Token(通常是 JWT);② 后续请求带着 Token 到网关;③ 网关统一校验 Token(验签、查是否过期、解析出用户身份),校验通过才放行、转发给后端微服务,不通过直接拦截(返回 401);④ 网关把解析出的用户信息(用户 id、角色等)通过请求头传给后端服务,后端服务信任网关传来的身份、不用再校验 Token(内网信任)。好处:认证鉴权逻辑集中在网关一处(不用每个服务重复写)、后端服务专注业务、安全策略统一。关键点:认证(你是谁)适合完全在网关做;鉴权(你能不能做这件事)分两层——网关做粗粒度(URL 级别、是否登录),细粒度的业务权限(如「只能改自己的订单」)还得在服务内做。
详细版
网关鉴权 vs 每个服务各自鉴权:
各自鉴权(问题): 网关统一鉴权(推荐):
每个服务自己校验 Token 网关统一校验 Token
→ 重复代码、逻辑可能不一致 → 一处校验,逻辑统一
→ 每个服务都要引安全依赖 → 后端服务专注业务
→ 改鉴权逻辑要改所有服务 → 改一处(网关)
网关统一鉴权流程:
1. 登录:POST /login → 认证服务验证账密 → 颁发 JWT
2. 请求:GET /orders Header: Authorization: Bearer <JWT>
↓
3. 网关:校验 JWT(验签 + 过期 + 解析用户)
- 校验失败 → 直接返回 401(拦截,不转发)
- 校验成功 → 解析出 userId、roles
↓
4. 网关转发给订单服务,把用户信息放请求头:
X-User-Id: 123 X-User-Roles: USER,VIP
↓
5. 订单服务:信任网关传来的用户信息(内网信任),直接用
不用再校验 JWT(网关已校验)
做细粒度业务鉴权(如"只能查自己的订单")
// Spring Cloud Gateway 全局过滤器统一鉴权(示意)
@Component
public class AuthGatewayFilter implements GlobalFilter {
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
if (!validateJwt(token)) { // 校验 JWT
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete(); // 拦截,返回 401
}
// 校验通过,解析用户信息放请求头,转发
Claims claims = parseJwt(token);
ServerHttpRequest request = exchange.getRequest().mutate()
.header("X-User-Id", claims.getSubject())
.header("X-User-Roles", claims.get("roles", String.class))
.build();
return chain.filter(exchange.mutate().request(request).build());
}
}
⚠️ 网关统一鉴权有一个必须堵住的安全漏洞:后端服务信任网关传来的用户信息(
X-User-Id等请求头),但如果攻击者能「绕过网关直接访问后端服务」,就能伪造这些请求头冒充任意用户。所以必须保证「后端服务只能通过网关访问、不能被外部直接访问」——通过网络隔离(后端服务在内网、只有网关能访问)、或服务间调用也校验(如内部 Token、mTLS 双向认证)。另外,JWT 的「无状态」是双刃剑:网关校验 JWT 不用查数据库(快、无状态),但也意味着「JWT 签发后无法主动失效」——用户登出、被封禁时,已签发的 JWT 在过期前仍然有效。要支持主动失效,得引入「黑名单」(把要失效的 JWT 存 Redis、网关校验时查黑名单),但这就又变成有状态了(要查 Redis),是无状态和可失效之间的权衡。
完整版教学
一、问题:每个服务各自鉴权的痛点
先理解「为什么要下沉到网关」:
如果每个微服务自己做认证鉴权:
订单服务、用户服务、商品服务...每个都:
- 校验 Token(验签、查过期、解析用户)
- 判断权限
→ 问题:
① 重复代码——每个服务都写一遍认证鉴权逻辑
② 逻辑可能不一致——不同服务的校验实现有差异、有 bug
③ 每个服务都要引安全依赖、配密钥
④ 改鉴权逻辑(如换 Token 格式、加校验)要改所有服务
⑤ 安全风险分散——一个服务的鉴权漏洞就是入口
微服务的特点让这更糟:
服务很多——重复 N 次
独立部署——改一处逻辑要协调所有服务升级
想要的:
认证鉴权逻辑集中在一处,服务不用重复做
→ 统一入口做鉴权 → API 网关
每个服务各自鉴权的痛点:① 重复代码(每个服务写一遍认证鉴权)、② 逻辑可能不一致(实现有差异有 bug)、③ 每个服务要引安全依赖配密钥、④ 改鉴权逻辑要改所有服务、⑤ 安全风险分散。微服务特点让这更糟(服务多重复 N 次、独立部署改一处要协调所有)。想要的是「认证鉴权集中一处、服务不用重复做」——统一入口做鉴权,即 API 网关。理解「各自鉴权痛点:重复代码/逻辑不一致/每个服务引依赖/改逻辑要改所有服务/风险分散、微服务多且独立部署加剧、需要集中到统一入口网关」,就理解了下沉到网关的动机。
二、认证与鉴权的区别
先分清「认证」和「鉴权」——它们下沉的程度不同:
认证(Authentication,你是谁):
验证用户身份——账密登录、Token 校验
→ "确认这个请求是谁发的"
例:校验 JWT 有效、解析出 userId
鉴权(Authorization,你能不能做):
验证用户有没有权限做某事
→ "确认这个用户能不能访问这个资源/执行这个操作"
分两种粒度:
① 粗粒度:URL/接口级别(这个角色能不能访问 /admin/*)
② 细粒度:业务/数据级别(这个用户能不能改这条具体的订单)
下沉到网关的程度:
认证:完全适合在网关做(统一校验 Token、解析身份)
鉴权(粗粒度):适合在网关做(URL 级别、角色级别)
网关配路由规则:"/admin/** 需要 ADMIN 角色"
鉴权(细粒度):得在服务内做(网关不懂业务)
"只能改自己的订单"——要查订单归属,是业务逻辑,网关不知道
所以:
认证 → 网关统一做
粗粒度鉴权 → 网关做
细粒度业务鉴权 → 服务内做(网关做不了)
先分清认证(Authentication,你是谁,验证身份/校验 Token)和鉴权(Authorization,你能不能做,验证权限)。鉴权分粗粒度(URL/接口级别,角色能否访问 /admin/*)和细粒度(业务/数据级别,能否改这条具体订单)。下沉程度:认证完全适合网关做(统一校验 Token 解析身份)、粗粒度鉴权适合网关做(URL/角色级别)、细粒度业务鉴权得在服务内做(网关不懂业务、不知道订单归属)。理解「认证(你是谁/校验 Token)vs 鉴权(你能不能做/验证权限);鉴权分粗粒度(URL/角色)和细粒度(业务/数据);下沉:认证网关做、粗粒度鉴权网关做、细粒度业务鉴权服务内做」,就理解了认证鉴权的区别和下沉程度。
三、网关统一鉴权的流程
理解网关统一鉴权的完整流程:
完整流程:
1. 用户登录:
POST /login(账号密码)→ 认证服务验证 → 颁发 JWT
JWT 里含:userId、角色、过期时间等(签名保证不可篡改)
2. 后续请求带 Token:
GET /orders Header: Authorization: Bearer <JWT>
3. 网关拦截、校验 JWT:
- 验签(用密钥验证签名,防篡改)
- 查是否过期
- 解析出用户身份(userId、roles)
校验失败 → 直接返回 401(拦截,不转发给后端)
校验成功 → 继续
4. 网关转发给后端服务,传递用户信息:
把解析出的 userId、roles 放到请求头
X-User-Id: 123 X-User-Roles: USER,VIP
→ 转发给订单服务
5. 后端服务:
信任网关传来的用户信息(内网信任,网关已校验过)
直接用 X-User-Id 做业务(不用再校验 JWT)
做细粒度业务鉴权(如"查自己的订单"用 X-User-Id 过滤)
关键设计:
- 网关做认证 + 粗粒度鉴权(Token 校验 + URL 级别)
- 后端服务信任网关的身份传递,专注业务 + 细粒度鉴权
- 一次校验(网关),后端不重复校验(内网信任)
网关统一鉴权流程:① 登录(认证服务验证颁发 JWT,含 userId/角色/过期时间/签名)→ ② 请求带 Token(Authorization: Bearer <JWT>)→ ③ 网关校验 JWT(验签+查过期+解析身份,失败返回 401 拦截、成功继续)→ ④ 网关把用户信息放请求头转发(X-User-Id/X-User-Roles)→ ⑤ 后端服务信任网关传来的身份(内网信任、不再校验 JWT)、做细粒度业务鉴权。关键:网关做认证+粗粒度鉴权、后端信任网关身份传递专注业务+细粒度鉴权、一次校验后端不重复。理解「流程:登录颁发 JWT→请求带 Token→网关校验(验签/过期/解析,失败 401)→放用户信息请求头转发→后端信任网关身份做业务;网关认证+粗粒度、后端信任+细粒度」,就掌握了统一鉴权流程。
四、安全漏洞:绕过网关
网关鉴权有个必须堵住的漏洞——「绕过网关直接访问后端」:
漏洞:后端服务信任网关传来的请求头(X-User-Id)
如果攻击者能"绕过网关、直接访问后端服务":
直接请求订单服务,自己伪造 X-User-Id: 999
→ 后端服务信任这个头 → 冒充用户 999 → 越权!
根本原因:
后端服务"信任"网关传来的身份(内网信任模型)
但如果后端能被外部直接访问,这个"信任"就被利用了
解决(必须做):
① 网络隔离(最基础):
后端服务放内网,只有网关能访问、外部访问不到
→ 攻击者到不了后端,只能走网关(网关会校验)
② 服务间调用也校验(纵深防御):
内部服务间也要求 Token 或内部凭证(不裸信任请求头)
或 mTLS(双向 TLS,服务间互相验证身份)
→ 即使进了内网,也要有合法身份
③ 网关到后端加内部标识:
网关转发时加一个"来自网关"的签名/内部 Token
后端验证这个标识,确认请求确实来自网关
原则:
"信任"必须建立在"不可绕过"之上
网关鉴权 + 后端不可被外部直接访问 = 安全
只做网关鉴权、后端裸奔 = 有越权漏洞
所以网关统一鉴权,必须配合"后端服务网络隔离"
网关鉴权的漏洞是「绕过网关直接访问后端」——后端信任网关传的 X-User-Id,若攻击者能绕过网关直接访问后端、伪造 X-User-Id: 999,就能冒充用户越权。根本原因是「后端信任网关身份(内网信任),但后端能被外部直接访问时信任被利用」。解决(必须做):① 网络隔离(后端放内网、只有网关能访问、外部访问不到,最基础)、② 服务间调用也校验(内部 Token/mTLS 双向认证、纵深防御)、③ 网关加内部标识(转发加「来自网关」的签名/内部 Token,后端验证)。原则「信任必须建立在不可绕过之上」——网关鉴权 + 后端不可被外部直接访问 = 安全。理解「漏洞:绕过网关直接访问后端伪造 X-User-Id 越权、根本原因后端信任网关身份但能被外部直接访问;解决:网络隔离(后端内网只有网关能访问)+服务间校验(内部 Token/mTLS)+网关加内部标识;信任必须不可绕过」,就掌握了这个关键安全点。
五、JWT 的无状态与失效难题
网关鉴权常用 JWT,理解 JWT 的「无状态」双刃剑:
JWT 的无状态优势:
JWT 自包含(用户信息 + 签名都在 token 里)
网关校验 JWT 只需"验签 + 查过期"——不用查数据库/Redis
→ 无状态、快、易水平扩展(网关不用共享 session)
JWT 的失效难题(无状态的代价):
JWT 一旦签发,在过期前"无法主动失效":
用户登出 → 但已签发的 JWT 还在过期前有效
用户被封禁/改密码 → 旧 JWT 仍有效
→ 因为网关只验签+查过期,不查"这个 token 是否被吊销"
→ 无法主动让某个 JWT 失效
解决主动失效(引入状态):
① 黑名单:
要失效的 JWT(如登出的)存 Redis 黑名单
网关校验时:验签 + 查过期 + 查黑名单(不在黑名单才有效)
→ 但这就变"有状态"了(要查 Redis)
② 短过期 + Refresh Token:
Access Token 过期时间短(如 15 分钟)→ 失效窗口小
用 Refresh Token 续期(Refresh 可吊销)
→ 平衡无状态和可控性
③ 版本号/时间戳:
用户信息里带版本号,改密码时版本号+1
JWT 里也存版本号,校验时比对(不匹配则失效)
→ 也要查用户当前版本(有状态)
权衡:
纯 JWT(无状态):快、易扩展,但不能主动失效
JWT + 黑名单/短过期:能失效,但引入了状态
→ 根据"是否需要主动失效"权衡
网关常用 JWT,其「无状态」是双刃剑:优势——JWT 自包含(用户信息+签名在 token 里),网关校验只需验签+查过期、不用查数据库(无状态、快、易水平扩展)。失效难题(代价)——JWT 签发后过期前无法主动失效(登出/封禁/改密码后旧 JWT 仍有效,因网关只验签查过期不查是否吊销)。解决主动失效(引入状态):① 黑名单(失效的 JWT 存 Redis、校验时查黑名单,但变有状态)、② 短过期 + Refresh Token(Access Token 短过期缩小失效窗口)、③ 版本号(改密码版本号+1、校验比对)。权衡「纯 JWT 快但不能主动失效 vs JWT+黑名单能失效但有状态」。理解「JWT 无状态优势:自包含、验签查过期不查库、易扩展;失效难题:签发后过期前无法主动失效(登出/封禁仍有效);解决:黑名单(存 Redis 但有状态)/短过期+Refresh/版本号;权衡无状态和可失效」,就掌握了 JWT 的无状态双刃剑。
六、实践与其他方案
总结网关鉴权的实践和其他考量:
实践建议:
① 网关做认证(Token 校验)+ 粗粒度鉴权(URL/角色)
② 后端服务信任网关身份传递,做细粒度业务鉴权
③ 必须做网络隔离(后端不可被外部直接访问)
④ 考虑 JWT 失效需求(要主动失效就加黑名单/短过期)
⑤ 敏感操作在服务内二次校验(纵深防御)
其他方案/考量:
① 认证方式:
JWT(无状态、自包含,主流)
或 Session + 网关共享 session(有状态,需 Redis 共享)
或 OAuth2/OIDC(对接第三方登录、标准协议)
② 网关鉴权的实现:
Spring Cloud Gateway 全局过滤器(GlobalFilter)
或专门的网关(Kong、APISIX,有认证插件)
或 Spring Security 在网关层
③ 服务网格(Istio):
可以在 sidecar 层做 mTLS、认证、授权
→ 更细的流量层安全,不改业务代码
核心总结:
认证鉴权下沉到网关(统一入口一次校验),
后端信任网关身份(配合网络隔离防绕过),
细粒度业务鉴权仍在服务内,
JWT 无状态但失效难(按需加黑名单/短过期)
网关鉴权实践:① 网关做认证+粗粒度鉴权、② 后端信任网关身份做细粒度鉴权、③ 必须网络隔离、④ 考虑 JWT 失效需求、⑤ 敏感操作服务内二次校验。其他方案:认证方式(JWT 无状态主流、Session+共享、OAuth2/OIDC)、实现(Spring Cloud Gateway GlobalFilter、Kong/APISIX 插件、网关层 Spring Security)、服务网格(Istio sidecar 做 mTLS/认证授权、不改业务代码)。理解「实践:网关认证+粗粒度、后端细粒度、网络隔离、JWT 失效需求、敏感操作二次校验;方案:JWT/Session/OAuth2、Spring Cloud Gateway/Kong/Istio」,就掌握了网关鉴权的实践和方案。
记忆钩子:「微服务认证鉴权下沉到网关(每个服务各自鉴权有重复代码/逻辑不一致/改逻辑要改所有);流程:登录颁发 JWT→请求带 Token→网关校验(验签+过期+解析,失败 401 拦截)→把用户信息放请求头(X-User-Id)转发→后端信任网关身份做业务;认证(你是谁)完全网关做、粗粒度鉴权(URL/角色)网关做、细粒度业务鉴权(改自己订单)服务内做;★安全漏洞:绕过网关直接访问后端伪造 X-User-Id 越权→必须网络隔离(后端内网只有网关能访问)+服务间校验(内部 Token/mTLS);JWT 无状态(验签查过期不查库/易扩展)但失效难(签发后无法主动失效,登出仍有效)→黑名单(存 Redis 但有状态)/短过期+Refresh」。
七、常见误区与追问
- 误区:每个微服务自己做认证鉴权最安全。 不好——会导致大量重复代码、逻辑可能不一致(有 bug)、每个服务都要引安全依赖、改鉴权逻辑要改所有服务;主流做法是下沉到网关统一鉴权(一处校验、逻辑统一),后端服务信任网关传来的身份、专注业务。
- 误区:所有鉴权都能在网关做。 认证(校验 Token)和粗粒度鉴权(URL/角色级别)适合在网关做;但细粒度业务鉴权(如「只能改自己的订单」)网关做不了——它不懂业务、不知道订单归属,这种要在服务内根据业务逻辑做;网关做粗、服务做细。
- 误区:网关鉴权后,后端服务就绝对安全了。 有个漏洞——后端信任网关传来的 X-User-Id 请求头,如果攻击者能绕过网关直接访问后端、伪造这个头,就能越权;必须配合网络隔离(后端只能通过网关访问、不能被外部直接访问)+ 服务间校验(内部 Token/mTLS)才安全。
- 误区:JWT 可以随时让它失效。 JWT 是无状态的——网关校验只验签 + 查过期,不查「是否被吊销」,所以 JWT 签发后在过期前无法主动失效(用户登出、被封禁后旧 JWT 仍有效);要支持主动失效得引入黑名单(存 Redis、校验时查)或短过期 + Refresh Token,但这就引入了状态。
- 追问:为什么要把认证鉴权下沉到网关? 避免每个微服务重复做认证鉴权——如果每个服务自己校验 Token、判断权限,会有大量重复代码、逻辑可能不一致、每个服务都要引安全依赖、改鉴权逻辑要改所有服务;下沉到网关后,网关作为统一入口一次校验,通过了才转发给后端,认证鉴权逻辑集中在一处、后端服务专注业务、安全策略统一、易维护。
- 追问:网关校验通过后,怎么把用户身份传给后端服务?后端怎么用? 网关解析出用户信息(userId、roles)后,放到请求头(如 X-User-Id、X-User-Roles)转发给后端;后端服务信任网关传来的这些头(内网信任,网关已校验过 Token),直接用 X-User-Id 做业务(不用再校验 JWT)、做细粒度业务鉴权(如用 X-User-Id 过滤只查自己的数据);前提是后端不能被外部直接访问(网络隔离),否则请求头可被伪造。
- 追问:JWT 的无状态是优点还是缺点? 双刃剑——优点:JWT 自包含(用户信息+签名在 token 里),网关校验只需验签+查过期、不用查数据库/Redis,无状态、快、易水平扩展(网关不用共享 session);缺点:JWT 签发后在过期前无法主动失效(登出、封禁、改密码后旧 JWT 仍有效);要主动失效得引入黑名单或短过期+Refresh Token,但就变有状态了;是「无状态高性能」和「可主动失效」之间的权衡。
八、加强记忆
微服务架构下,把「认证 + 鉴权」下沉到 API 网关做统一鉴权——避免每个服务各自做导致的重复代码、逻辑不一致、改逻辑要改所有服务。核心流程:① 用户登录(认证服务颁发 JWT,含 userId/角色/过期/签名)→ ② 请求带 Token(Authorization: Bearer <JWT>)→ ③ 网关统一校验 JWT(验签 + 查过期 + 解析身份,失败返回 401 拦截、成功放行)→ ④ 网关把用户信息放请求头转发(X-User-Id/X-User-Roles)→ ⑤ 后端服务信任网关传来的身份(内网信任、不再校验 JWT)、做细粒度业务鉴权。认证鉴权的下沉程度:认证(你是谁)完全在网关做、粗粒度鉴权(URL/角色)在网关做、细粒度业务鉴权(改自己的订单)在服务内做(网关不懂业务)。关键安全漏洞:绕过网关直接访问后端伪造 X-User-Id 越权——必须网络隔离(后端只能通过网关访问)+ 服务间校验(内部 Token/mTLS),「信任必须建立在不可绕过之上」。JWT 无状态(验签查过期不查库、快、易扩展)但失效难(签发后过期前无法主动失效,登出仍有效)——要主动失效得加黑名单(存 Redis 但变有状态)或短过期 + Refresh Token。一句话「认证鉴权下沉到网关统一校验(避免每服务重复):登录颁发 JWT→网关校验 JWT(验签+过期+解析,失败 401)→用户信息放请求头转发→后端信任网关身份;认证+粗粒度鉴权网关做、细粒度业务鉴权服务内做;漏洞:绕过网关伪造 X-User-Id 越权→必须网络隔离;JWT 无状态但失效难→黑名单/短过期」。