资源鉴权的选型,不能只看“鉴权代码应该放在哪里”,还需要结合权限粒度、请求并发、数据一致性、系统可用性和运维复杂度综合判断。
需要先区分几个概念:
- 认证:确认访问者是谁。
- 令牌校验:验证 Access Token 的签名、发行方、受众和有效期。
- 授权决策:判断访问者是否有权限执行某项操作。
- 权限执行:将授权结果应用到具体接口、资源或数据。
Spring Authorization Server 是基于 Spring Security 构建的 OAuth 2.0 和 OpenID Connect 授权服务器实现,负责授权流程以及 Access Token、Refresh Token 等令牌的签发,并可与用户认证流程集成。它并不等同于资源鉴权服务。
在微服务系统中,可以从“授权决策在哪里完成、权限检查在哪里执行”两个维度,讨论三种常见模式。它们可以组合使用,并不是互斥的选项。
一、网关入口鉴权
网关鉴权适合权限规则相对简单的场景。
网关通常负责:
- 校验 Access Token;对于 JWT,验证签名、发行方、受众和过期时间;
- 根据业务要求检查令牌是否被撤销,或账号是否处于可用状态;
- 根据路由、角色或 Scope 执行基础访问控制;
- 执行黑名单、白名单、限流等入口策略。
对于只要求“令牌合法且接口允许访问”的系统,网关可以完成大部分鉴权工作。如果业务服务只允许通过可信网关访问,并能验证网关传递的身份信息,就可以复用入口的基础校验结果。若服务存在其他访问入口,则仍需要独立验证身份和权限。
这种模式的优点是:
- 不合规请求可以在系统入口被拦截;
- 业务服务的鉴权代码较少;
- 入口权限规则集中,便于统一管理;
- 可以减少无效请求进入内部网络。
但随着权限粒度变细,网关可能需要查询用户、角色、组织、资源和数据权限等信息。此时,如果每次请求都访问数据库或集中式权限服务,鉴权链路就可能成为系统瓶颈。
网关集群和负载均衡可以解决网关节点自身的 CPU、连接数和吞吐问题,但不能自动解决权限查询瓶颈。实际部署中通常还需要配合:
- 权限策略缓存;
- 本地 Token 校验;
- 批量查询;
- 权限数据预加载;
- 鉴权超时与故障处理策略;
- 集中式权限服务的高可用部署。
因此,网关鉴权并不等于“所有权限都必须在网关完成”。复杂的数据权限通常仍需要由业务服务在内部进一步判断。
二、集中式授权决策
集中式授权决策是指将权限策略管理和授权决策交给一个独立的权限服务或策略引擎。网关或业务服务向它提交主体、资源、操作和上下文信息,再由权限服务返回允许或拒绝的结果。
典型流程如下:
客户端请求
↓
网关或业务服务
↓
权限服务 / 策略引擎
↓
返回允许或拒绝
↓
网关或业务服务执行权限检查
这种模式的优势在于:
- 权限规则集中维护;
- RBAC、ABAC 等复杂策略可以统一实现;
- 不同业务服务使用相同的权限模型;
- 权限变更不需要同时修改所有业务服务。
但集中式决策也会引入新的依赖:
- 每次鉴权可能增加一次网络调用;
- 权限服务故障可能影响大量业务请求;
- 高并发场景下,权限服务可能成为集中瓶颈;
- 权限决策缓存会带来一致性和失效问题。
因此,集中式权限服务需要重点设计缓存、超时、熔断和高可用。故障处理必须明确:无法获得有效授权结果时,受保护的操作应默认拒绝;如果允许复用缓存结果,则需要限定适用操作、缓存有效期和权限撤销的生效窗口。
对于要求权限变更立即生效的操作,仅缩短缓存时间仍不够,还需要同步查询或能够保证及时失效的机制。对于允许短暂延迟生效的普通权限,则可以使用更长的本地缓存周期。
集中式策略管理不一定意味着每次请求都必须远程调用。常见的优化方式是:
- 集中管理策略;
- 将策略同步到网关或业务服务本地;
- 在本地执行大部分决策;
- 只有复杂或特殊场景才访问中心服务。
此时,策略管理仍然集中,而授权决策的执行已经部分下沉到本地。
三、服务本地鉴权与分布式策略同步
当权限粒度深入到业务接口、业务操作甚至数据行级别时,仅依靠网关通常不够。此时可以由各个微服务在 Controller、Service 或方法安全层执行本地鉴权。
例如:
@PreAuthorize("@permission.canAccess(authentication, #orderId)")
public OrderDetail getOrderDetail(Long orderId) {
return orderService.getDetail(orderId);
}
此时请求仍然会经过网关,但具体的业务权限由目标微服务结合本地业务数据完成判断。
这种模式的优点是:
- 权限判断可以使用最接近业务的数据;
- 更适合数据权限、资源所有权和操作级权限;
- 鉴权负载可以随业务请求分散到各个服务;
- 网关可以专注于路由、基础令牌校验和入口控制。
代价也比较明显:
- 若入口没有先做基础校验,不合规请求会进入内部网络,增加流量消耗;
- 每个服务都需要实现或接入鉴权能力;
- 不同服务之间可能出现权限规则不一致;
- 权限模型升级和策略发布更加复杂;
- 必须防止绕过 Controller 或内部接口直接访问业务逻辑。
服务本地鉴权通常需要遵守几个原则:
- 默认拒绝,明确授权;
- 按访问入口覆盖权限检查,不能只依赖某一个 Controller 切面;
- 服务间调用也要验证调用方身份,并根据操作检查权限;
- 权限判断不能只依赖客户端传入的参数;
- 权限缓存必须具备过期和失效机制;
- 策略更新需要能够及时同步到各个实例。
权限配置可以集中保存在数据库或配置中心,再通过发布订阅机制通知各个服务刷新本地缓存。这个过程更适合称为“集中式策略存储与分布式缓存同步”,不必强行归类为黑板架构。
四、三种模式的对比
| 模式 | 主要决策或执行位置 | 优势 | 主要代价 |
|---|---|---|---|
| 网关入口鉴权 | 网关 | 入口拦截早、规则集中、服务改造少 | 网关负载集中,难以处理复杂业务权限 |
| 集中式授权决策 | 权限服务或策略引擎决策,调用方执行 | 权限模型统一,适合复杂策略 | 增加网络依赖,存在中心瓶颈 |
| 服务本地鉴权 | 业务微服务 | 接近业务数据,适合细粒度权限 | 需要解决策略一致性与检查覆盖问题 |
实际系统很少只采用一种模式,更常见的是组合使用:
客户端请求
↓
网关:Token 校验、路由级权限
↓
业务服务:资源权限、数据权限、操作权限
├── 基于本地策略和业务数据判断
└── 必要时调用权限服务进行复杂策略决策
↓
允许后执行业务操作
例如,网关可以拒绝无效令牌和没有基础 Scope 的请求,业务服务再判断用户是否拥有某个订单、账户或组织的数据权限。
五、架构选择需要权衡的质量属性
微服务授权设计本质上是一个架构权衡问题。它至少会影响以下质量属性:
- 延迟:是否需要额外的远程鉴权调用;
- 吞吐:鉴权逻辑会集中在网关、权限服务,还是分散到业务服务;
- 可用性:权限服务故障时,业务请求是否仍可安全处理;
- 一致性:权限变更需要多长时间才能在所有节点生效;
- 安全性:是否存在绕过网关、伪造身份或使用过期权限的问题;
- 可扩展性:是否支持更细的角色、资源和数据权限;
- 运维复杂度:策略发布、缓存失效和问题排查是否可控;
- 流量与成本:无效请求是否会进入内部服务,鉴权调用是否造成额外资源消耗。
因此,权限粒度越细,并不意味着所有鉴权都应该下沉到业务服务;并发越高,也不意味着所有鉴权都应该集中到网关。
更合理的做法是:
- 在网关完成低成本、通用的入口校验;
- 在业务服务完成依赖业务数据的细粒度权限判断;
- 使用集中式策略管理保证规则统一;
- 通过本地缓存和事件同步降低远程鉴权开销;
- 根据一致性要求设计缓存失效和权限变更机制。
架构设计没有绝对完美的授权方案。真正需要回答的问题是:系统需要多细的权限粒度,需要多高的吞吐和可用性,以及权限变更允许多长时间的延迟。只有明确这些约束,才能决定鉴权应该集中在哪里、执行在哪里,以及哪些能力值得为之承担额外的复杂度。