【spring】Spring Security + OAuth2:从“能登录”到“真理解” !
Spring Security + OAuth2:基础版 “能登录”==> “真理解”
很多人第一次接触 Spring Security 和 OAuth2,感觉像同时撞上两堵墙:
- Spring Security 配置很多,看起来每个方法都能影响登录和权限。
- OAuth2 名词很多,
client、scope、grant_type、access_token、resource server很容易混在一起。
但这套东西可以先记住一句话:
Spring Security 管“请求进来以后怎么判断能不能过”,OAuth2 管“token 怎么申请、怎么颁发、怎么校验”。
这篇文章不按 API 清单讲,也不从源码细节劈头盖脸往下砸。从一个普通请求开始,一层一层往里看,最后再把 Spring Security、OAuth2 Client、Resource Server、Authorization Server 串成一张图。
安全问题-大白话
一个系统做安全,不是“加个登录页”这么简单。它至少要回答五个问题:
- 你是谁?
- 你怎么证明你是你?
- 你能访问什么?
- 你的访问行为能不能被追踪?
- 你的登录态或 token 什么时候失效?
这里最容易混的两个词是认证和授权。
| 概念 | 问题 | 常见例子 |
|---|---|---|
认证 Authentication |
你是谁 | 用户名密码、短信验证码、SSO、Bearer Token |
授权 Authorization |
你能做什么 | ROLE_ADMIN、sys:user:add、SCOPE_message.read |
认证像门口验身份。授权像进门以后看你能进哪些房间。
Spring Security 到底在做什么
Spring Security 的核心工作不是“帮你写登录页面”,而是在请求进入业务代码之前,完成一组标准动作:
- 拦截请求。
- 提取凭证,比如密码、session id、Bearer token。
- 发起认证。
- 保存认证结果。
- 执行权限判断。
- 把安全异常转换成
401或403。
一张图看整体过程:
所以,Spring Security 的主要能力可以理解为:
请求先进过滤器链,过滤器链完成认证和授权,业务代码只处理已经通过安全检查的请求。
过滤器链:Spring Security 的入口
在 Servlet Web 应用里,请求到达 Controller 之前,会先经过 Servlet Filter。Spring Security 正是靠这套机制接管安全流程。
它的入口大概长这样:
这里有三个对象很关键:
| 对象 | 可以怎么理解 |
|---|---|
DelegatingFilterProxy |
Servlet 容器和 Spring Bean 之间的桥 |
FilterChainProxy |
Spring Security 真正的过滤器总入口 |
SecurityFilterChain |
某一类请求要走哪些安全过滤器 |
一个应用可以有多条 SecurityFilterChain。比如:
/api/**走 Bearer Token。/admin/**走表单登录。/actuator/**走内部监控权限。
这也是为什么安全配置不要随便堆在一个大方法里。按请求边界拆开,职责会更清楚,也更符合 KISS(保持简单) 和 SRP(单一职责)。
认证:从凭证到当前用户
认证就是把“请求里带来的凭证”变成“系统认可的身份”。
Spring Security 里最核心的认证对象是 Authentication。它通常包含:
principal:当前主体,也就是用户或客户端。credentials:凭证,比如密码或 token。authorities:权限集合。authenticated:是否已认证。details:请求细节。
要注意一点:认证前和认证后都可能是 Authentication,区别在于认证后它会被标记为可信,并带上完整的主体和权限。
典型认证链路如下:
这条链里,每个角色职责都很窄:
| 对象 | 职责 |
|---|---|
AuthenticationManager |
认证统一入口 |
ProviderManager |
按顺序调度多个 AuthenticationProvider |
AuthenticationProvider |
真正执行某一种认证逻辑 |
SecurityContextHolder |
保存当前请求的安全上下文 |
以用户名密码登录为例:
这里有一个常见误区:UserDetailsService 不负责校验密码。它只负责“根据用户名把用户查出来”。密码比对通常由 DaoAuthenticationProvider 调用 PasswordEncoder 完成。
授权:知道你是谁以后,再判断你能不能做
认证之后,系统知道了“你是谁”。接下来要判断“你有没有权限访问当前资源”。
Spring Security 新体系里,授权核心是 AuthorizationManager。
授权通常分两层:
| 层级 | 适合做什么 |
|---|---|
| 请求级授权 | 控制 URL、HTTP Method,比如 /admin/** 需要管理员 |
| 方法级授权 | 控制业务方法,比如 @PreAuthorize("hasAuthority('sys:user:add')") |
GrantedAuthority 是授权判断里的基础标识。无论权限来自用户表、角色表、token claims,还是 introspection 返回值,最后通常都要映射成类似下面的字符串:
ROLE_ADMINROLE_USERSCOPE_message.readsys:user:add
复杂业务规则不要全塞进权限字符串。Spring Security 适合做认证、粗粒度授权和标准权限拦截;更复杂的领域规则,应该放到领域服务、自定义 AuthorizationManager 或业务策略组件里。
401 和 403 为什么不一样
安全异常不是普通业务异常。
401 Unauthorized:你还没认证,或者认证失败。403 Forbidden:你已经认证了,但权限不够。
Spring Security 里主要靠两个扩展点处理:
| 组件 | 处理场景 |
|---|---|
AuthenticationEntryPoint |
未认证访问受保护资源,通常返回 401 或跳登录页 |
AccessDeniedHandler |
已认证但权限不足,通常返回 403 |
这点在前后端分离系统里尤其重要。API 系统一般应该返回清晰的 JSON 错误,而不是把用户重定向到 HTML 登录页。
OAuth2:它不是“登录框架”,而是授权框架
OAuth2 解决的核心问题是:
让客户端在不直接持有用户密码的情况下,访问受保护资源。
OAuth2 有四个核心角色:
| 角色 | 说人话 |
|---|---|
Resource Owner |
资源拥有者,通常是用户 |
Client |
想访问资源的应用 |
Authorization Server |
负责认证用户、认证客户端、颁发 token |
Resource Server |
保护 API,校验 access token |
一句话记住三类 Spring OAuth2 能力:
- OAuth2 Client:负责拿 token。
- Resource Server:负责验 token。
- Authorization Server:负责发 token。
Authorization Code:最常见的用户授权流程
authorization_code 是最主流、最标准的用户授权模式,适合 Web 应用、移动端和第三方登录。
这个流程的重点不是“多跳了几次页面”,而是安全边界:
- 用户密码只交给授权服务器。
- Client 拿到的是授权码和 token。
- 资源服务器只认 access token,不直接处理用户登录。
Resource Server:API 如何验 token
Resource Server 是真正保护 API 的一方。它做四件事:
- 从请求中提取 Bearer Token。
- 校验 token。
- 把 token 转成 Spring Security 的
Authentication。 - 继续执行权限判断。
基本处理流程:
Resource Server 常见两种 token 校验方式:JWT 和 Opaque Token。
JWT:本地验签,性能更好
JWT 是自包含 token。资源服务器可以本地解析 claims,并通过公钥验签。
JWT 的优点是快、适合分布式、资源服务器自治能力强。缺点是撤销和踢下线更麻烦,因为 token 一旦签出,只要没过期,资源服务器通常就会认。
Opaque Token:回源确认,控制更集中
Opaque Token 不自带业务可读信息,更像一个随机令牌。资源服务器要调用授权服务器的 introspection endpoint,确认 token 是否有效。
Opaque Token 的优点是更容易集中控制、撤销和踢下线。缺点是每次校验可能依赖网络和授权服务器,通常需要缓存配合。
选型可以简单理解为:
| 目标 | 更常见选择 |
|---|---|
| 高性能、本地验签、微服务自治 | JWT |
| 强撤销、集中会话控制、实时状态 | Opaque Token |
OAuth2 Client:应用如何拿 token、续 token
OAuth2 Client 关注的是“怎么拿 token”,不是“怎么验 token”。
Spring Security 里这部分核心对象如下:
几个关键对象可以这样理解:
| 对象 | 作用 |
|---|---|
ClientRegistration |
当前应用作为 OAuth2 Client 的注册信息 |
OAuth2AuthorizedClient |
已经授权成功的客户端和它持有的 token |
OAuth2AuthorizedClientManager |
负责授权、续期、保存 token 的协调者 |
OAuth2AuthorizedClientProvider |
具体处理某一种 grant |
实际项目里,一个应用可能同时是 OAuth2 Client 和 Resource Server:
- 它对前端暴露 API,所以要验 token。
- 它调用别的服务,所以也要代表自己或用户去拿 token。
Authorization Server:发 token 的那一方
Authorization Server 是 OAuth2 体系的核心。它负责:
- 认证用户。
- 认证客户端。
- 处理授权确认。
- 生成 access token 和 refresh token。
- 存储授权状态。
- 支持 token introspection 和 revocation。
Spring Authorization Server 不是脱离 Spring Security 的另一套东西。它本质上仍然建立在 Spring Security 的过滤器、认证对象和 Provider 模型之上。
这里最重要的几个模型:
| 对象 | 说人话 |
|---|---|
RegisteredClient |
已注册客户端,包含 client id、secret、grant type、redirect uri、scope |
OAuth2Authorization |
一次授权的完整状态,不只是 token |
OAuth2AuthorizationConsent |
用户对客户端和 scope 的授权同意 |
OAuth2AuthorizationService |
授权状态存储接口 |
OAuth2TokenGenerator |
token 生成器 |
OAuth2TokenCustomizer |
定制 token claims 或 headers |
一次 token 签发流程可以这样看:
看懂这张图后,你会发现授权服务器并不神秘:
它仍然是 Filter -> Authentication -> Provider -> Store,只是这里的 Authentication 表示 OAuth2 协议请求。
把 Spring Security 和 OAuth2 串起来
最终可以用一张图建立统一认知:
也就是说:
- Spring Security 是安全框架底座。
- OAuth2 是协议层。
- OAuth2 Client、Resource Server、Authorization Server 都借用了 Spring Security 的认证、授权、过滤器链和上下文模型。
如果你只记配置 API,很容易忘。
如果你记住这套底层模型,再看配置就会清楚很多。
常见误区
误区一:把 Spring Security 当成配置类
http.authorizeHttpRequests()、oauth2ResourceServer() 只是表面 API。真正重要的是:
- 请求匹配哪条
SecurityFilterChain。 - 哪个 Filter 提取凭证。
- 哪个
AuthenticationProvider完成认证。 - 认证结果如何进入
SecurityContext。 - 哪个
AuthorizationManager做最终判断。
误区二:把 OAuth2 当成 token 生成器
OAuth2 的重点不是“生成一个 token”,而是角色边界和协议流程:
- 谁是 Client?
- 谁是 Resource Server?
- 谁能发 token?
- token 代表用户授权还是客户端自身授权?
- scope 如何映射到资源权限?
误区三:把 JWT 当成 OAuth2 本身
JWT 只是 token 格式之一。OAuth2 可以用 JWT,也可以用 Opaque Token。
技术选型要看目标:
- 想要资源服务器本地验签,倾向 JWT。
- 想要强撤销和集中控制,倾向 Opaque Token。
误区四:把权限字符串当成全部业务规则
ROLE_ADMIN、SCOPE_read 适合做通用拦截。复杂业务规则,比如“只能审批自己部门下属的订单”,不要硬塞成一堆权限字符串。更好的方式是放到业务策略或自定义授权逻辑里。
从 0 到 1 的学习路径
如果要系统掌握,可以按这个顺序来:
每个阶段的目标也很明确:
| 阶段 | 你应该掌握什么 |
|---|---|
| 安全基础 | 认证、授权、session、token、scope、authority |
| Spring Security 过滤器链 | DelegatingFilterProxy、FilterChainProxy、SecurityFilterChain |
| 认证模型 | Authentication、AuthenticationManager、AuthenticationProvider |
| 授权模型 | AuthorizationManager、GrantedAuthority、方法安全 |
| Resource Server | Bearer Token、JWT、Opaque Token |
| OAuth2 Client | ClientRegistration、OAuth2AuthorizedClientManager |
| Authorization Server | RegisteredClient、OAuth2Authorization、OAuth2TokenGenerator |
| 扩展能力 | 自定义认证、自定义授权、自定义 claims、自定义 grant |
最后一张心智速记表
| 问题 | 看哪里 |
|---|---|
| 请求为什么没进 Controller? | SecurityFilterChain 和过滤器顺序 |
为什么返回 401? |
认证失败或未认证,查 AuthenticationEntryPoint |
为什么返回 403? |
已认证但权限不足,查 AuthorizationManager 或 AccessDeniedHandler |
| 用户密码在哪里校验? | AuthenticationProvider 和 PasswordEncoder |
| token 为什么无效? | Resource Server 的 JWT 解码或 Opaque introspection |
| scope 为什么没生效? | scope 到 GrantedAuthority 的转换 |
| token 里怎么加业务字段? | OAuth2TokenCustomizer |
| token 怎么撤销? | Authorization Server 的授权状态和 revocation 机制 |
真正掌握 Spring Security + OAuth2 的标志,不是能背出多少配置,而是看到一个安全问题时,能快速判断它属于哪一层:
- 是请求没有匹配到正确的安全链?
- 是凭证提取失败?
- 是认证 Provider 没生效?
- 是权限映射不对?
- 是 token 颁发、校验、撤销的边界没设计清楚?
能定位到这一层,问题通常就已经解决了一半。
更多推荐


所有评论(0)