Spring Security + OAuth2:基础版 “能登录”==> “真理解”

很多人第一次接触 Spring Security 和 OAuth2,感觉像同时撞上两堵墙:

  • Spring Security 配置很多,看起来每个方法都能影响登录和权限。
  • OAuth2 名词很多,clientscopegrant_typeaccess_tokenresource server 很容易混在一起。

但这套东西可以先记住一句话:

Spring Security 管“请求进来以后怎么判断能不能过”,OAuth2 管“token 怎么申请、怎么颁发、怎么校验”。

这篇文章不按 API 清单讲,也不从源码细节劈头盖脸往下砸。从一个普通请求开始,一层一层往里看,最后再把 Spring Security、OAuth2 Client、Resource Server、Authorization Server 串成一张图。


安全问题-大白话

一个系统做安全,不是“加个登录页”这么简单。它至少要回答五个问题:

  1. 你是谁?
  2. 你怎么证明你是你?
  3. 你能访问什么?
  4. 你的访问行为能不能被追踪?
  5. 你的登录态或 token 什么时候失效?

这里最容易混的两个词是认证和授权。

概念 问题 常见例子
认证 Authentication 你是谁 用户名密码、短信验证码、SSO、Bearer Token
授权 Authorization 你能做什么 ROLE_ADMINsys:user:addSCOPE_message.read

认证像门口验身份。授权像进门以后看你能进哪些房间。


Spring Security 到底在做什么

Spring Security 的核心工作不是“帮你写登录页面”,而是在请求进入业务代码之前,完成一组标准动作:

  • 拦截请求。
  • 提取凭证,比如密码、session id、Bearer token。
  • 发起认证。
  • 保存认证结果。
  • 执行权限判断。
  • 把安全异常转换成 401403

一张图看整体过程:

HTTP Request

Servlet Filter Chain

Spring Security Filters

Authentication

SecurityContext

Authorization

Controller or Service

所以,Spring Security 的主要能力可以理解为:

请求先进过滤器链,过滤器链完成认证和授权,业务代码只处理已经通过安全检查的请求。


过滤器链:Spring Security 的入口

在 Servlet Web 应用里,请求到达 Controller 之前,会先经过 Servlet Filter。Spring Security 正是靠这套机制接管安全流程。

它的入口大概长这样:

Tomcat or Servlet Container

DelegatingFilterProxy

FilterChainProxy

SecurityFilterChain for API

SecurityFilterChain for Admin

Default SecurityFilterChain

Security Filters

DispatcherServlet

Controller

这里有三个对象很关键:

对象 可以怎么理解
DelegatingFilterProxy Servlet 容器和 Spring Bean 之间的桥
FilterChainProxy Spring Security 真正的过滤器总入口
SecurityFilterChain 某一类请求要走哪些安全过滤器

一个应用可以有多条 SecurityFilterChain。比如:

  • /api/** 走 Bearer Token。
  • /admin/** 走表单登录。
  • /actuator/** 走内部监控权限。

/api/**

/admin/**

其他请求

Request

匹配哪条安全链

API SecurityFilterChain

Admin SecurityFilterChain

Default SecurityFilterChain

这也是为什么安全配置不要随便堆在一个大方法里。按请求边界拆开,职责会更清楚,也更符合 KISS(保持简单) 和 SRP(单一职责)。


认证:从凭证到当前用户

认证就是把“请求里带来的凭证”变成“系统认可的身份”。

Spring Security 里最核心的认证对象是 Authentication。它通常包含:

  • principal:当前主体,也就是用户或客户端。
  • credentials:凭证,比如密码或 token。
  • authorities:权限集合。
  • authenticated:是否已认证。
  • details:请求细节。

要注意一点:认证前和认证后都可能是 Authentication,区别在于认证后它会被标记为可信,并带上完整的主体和权限。

典型认证链路如下:

SecurityContextHolder AuthenticationProvider ProviderManager AuthenticationManager Authentication Filter Client SecurityContextHolder AuthenticationProvider ProviderManager AuthenticationManager Authentication Filter Client 携带凭证发起请求 authenticate(authentication) 委托认证 找能处理的 AuthenticationProvider 返回已认证 Authentication 返回认证结果 返回认证结果 保存到 SecurityContext

这条链里,每个角色职责都很窄:

对象 职责
AuthenticationManager 认证统一入口
ProviderManager 按顺序调度多个 AuthenticationProvider
AuthenticationProvider 真正执行某一种认证逻辑
SecurityContextHolder 保存当前请求的安全上下文

以用户名密码登录为例:

UsernamePasswordAuthenticationFilter

UsernamePasswordAuthenticationToken

AuthenticationManager

ProviderManager

DaoAuthenticationProvider

UserDetailsService loadUserByUsername

PasswordEncoder matches

Authenticated UsernamePasswordAuthenticationToken

SecurityContextHolder

这里有一个常见误区:UserDetailsService 不负责校验密码。它只负责“根据用户名把用户查出来”。密码比对通常由 DaoAuthenticationProvider 调用 PasswordEncoder 完成。


授权:知道你是谁以后,再判断你能不能做

认证之后,系统知道了“你是谁”。接下来要判断“你有没有权限访问当前资源”。

Spring Security 新体系里,授权核心是 AuthorizationManager

Yes

No

Request arrives

SecurityContext 中已有 Authentication

AuthorizationManager

是否允许访问

执行 Controller 或 Method

AccessDeniedException

授权通常分两层:

层级 适合做什么
请求级授权 控制 URL、HTTP Method,比如 /admin/** 需要管理员
方法级授权 控制业务方法,比如 @PreAuthorize("hasAuthority('sys:user:add')")

GrantedAuthority 是授权判断里的基础标识。无论权限来自用户表、角色表、token claims,还是 introspection 返回值,最后通常都要映射成类似下面的字符串:

  • ROLE_ADMIN
  • ROLE_USER
  • SCOPE_message.read
  • sys:user:add

复杂业务规则不要全塞进权限字符串。Spring Security 适合做认证、粗粒度授权和标准权限拦截;更复杂的领域规则,应该放到领域服务、自定义 AuthorizationManager 或业务策略组件里。


401403 为什么不一样

安全异常不是普通业务异常。

  • 401 Unauthorized:你还没认证,或者认证失败。
  • 403 Forbidden:你已经认证了,但权限不够。

Spring Security 里主要靠两个扩展点处理:

组件 处理场景
AuthenticationEntryPoint 未认证访问受保护资源,通常返回 401 或跳登录页
AccessDeniedHandler 已认证但权限不足,通常返回 403

这点在前后端分离系统里尤其重要。API 系统一般应该返回清晰的 JSON 错误,而不是把用户重定向到 HTML 登录页。


OAuth2:它不是“登录框架”,而是授权框架

OAuth2 解决的核心问题是:

让客户端在不直接持有用户密码的情况下,访问受保护资源。

OAuth2 有四个核心角色:

Resource Owner 用户

Client 客户端

Authorization Server 授权服务器

Resource Server 资源服务器

角色 说人话
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 应用、移动端和第三方登录。

Resource Server Authorization Server Client 应用 用户 Resource Server Authorization Server Client 应用 用户 点击登录或授权 跳转到 authorize endpoint 登录并确认授权 返回 authorization code 用 code 换 token 返回 access token 和 refresh token 携带 access token 调 API 返回受保护资源

这个流程的重点不是“多跳了几次页面”,而是安全边界:

  • 用户密码只交给授权服务器。
  • Client 拿到的是授权码和 token。
  • 资源服务器只认 access token,不直接处理用户登录。

Resource Server:API 如何验 token

Resource Server 是真正保护 API 的一方。它做四件事:

  1. 从请求中提取 Bearer Token。
  2. 校验 token。
  3. 把 token 转成 Spring Security 的 Authentication
  4. 继续执行权限判断。

基本处理流程:

HTTP Request with Authorization Bearer token

BearerTokenAuthenticationFilter

AuthenticationManager

AuthenticationProvider

Authenticated Principal

SecurityContextHolder

AuthorizationManager

Resource Server 常见两种 token 校验方式:JWT 和 Opaque Token。

JWT:本地验签,性能更好

JWT 是自包含 token。资源服务器可以本地解析 claims,并通过公钥验签。

Bearer token

BearerTokenAuthenticationFilter

BearerTokenAuthenticationToken

AuthenticationManager

JwtAuthenticationProvider

JwtDecoder decode and verify

JwtAuthenticationConverter claims to authorities

JwtAuthenticationToken

SecurityContextHolder

JWT 的优点是快、适合分布式、资源服务器自治能力强。缺点是撤销和踢下线更麻烦,因为 token 一旦签出,只要没过期,资源服务器通常就会认。

Opaque Token:回源确认,控制更集中

Opaque Token 不自带业务可读信息,更像一个随机令牌。资源服务器要调用授权服务器的 introspection endpoint,确认 token 是否有效。

Authorization Server OpaqueTokenAuthenticationProvider Resource Server Client Authorization Server OpaqueTokenAuthenticationProvider Resource Server Client 携带 Bearer Token 调 API authenticate(token) introspect(token) 返回 active principal scopes claims BearerTokenAuthentication

Opaque Token 的优点是更容易集中控制、撤销和踢下线。缺点是每次校验可能依赖网络和授权服务器,通常需要缓存配合。

选型可以简单理解为:

目标 更常见选择
高性能、本地验签、微服务自治 JWT
强撤销、集中会话控制、实时状态 Opaque Token

OAuth2 Client:应用如何拿 token、续 token

OAuth2 Client 关注的是“怎么拿 token”,不是“怎么验 token”。

Spring Security 里这部分核心对象如下:

ClientRegistrationRepository

OAuth2AuthorizedClientManager

OAuth2AuthorizedClientService

OAuth2AuthorizedClientRepository

OAuth2AuthorizedClientProvider

authorization_code

client_credentials

refresh_token

几个关键对象可以这样理解:

对象 作用
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 模型之上。

Client

Authorization Endpoint

Token Endpoint

Introspection Endpoint

Revocation Endpoint

Authorization Server SecurityFilterChain

RegisteredClientRepository

OAuth2AuthorizationService

OAuth2AuthorizationConsentService

OAuth2TokenGenerator

这里最重要的几个模型:

对象 说人话
RegisteredClient 已注册客户端,包含 client id、secret、grant type、redirect uri、scope
OAuth2Authorization 一次授权的完整状态,不只是 token
OAuth2AuthorizationConsent 用户对客户端和 scope 的授权同意
OAuth2AuthorizationService 授权状态存储接口
OAuth2TokenGenerator token 生成器
OAuth2TokenCustomizer 定制 token claims 或 headers

一次 token 签发流程可以这样看:

OAuth2TokenGenerator OAuth2AuthorizationService AuthenticationProvider AuthenticationConverter Security Filter Chain Client OAuth2TokenGenerator OAuth2AuthorizationService AuthenticationProvider AuthenticationConverter Security Filter Chain Client POST token endpoint 解析协议请求 grant Authentication authenticate grant request generate tokens access token and refresh token save OAuth2Authorization token response

看懂这张图后,你会发现授权服务器并不神秘:

它仍然是 Filter -> Authentication -> Provider -> Store,只是这里的 Authentication 表示 OAuth2 协议请求。


把 Spring Security 和 OAuth2 串起来

最终可以用一张图建立统一认知:

Spring Security core

Authentication 认证模型

Authorization 授权模型

SecurityFilterChain 过滤器链

SecurityContext 安全上下文

OAuth2 Client

OAuth2 Resource Server

Spring Authorization Server

Get token 拿 token

Validate token 验 token

Issue token 发 token

也就是说:

  • 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_ADMINSCOPE_read 适合做通用拦截。复杂业务规则,比如“只能审批自己部门下属的订单”,不要硬塞成一堆权限字符串。更好的方式是放到业务策略或自定义授权逻辑里。


从 0 到 1 的学习路径

如果要系统掌握,可以按这个顺序来:

阶段 1 安全基础

阶段 2 Spring Security 过滤器链

阶段 3 Authentication 认证模型

阶段 4 Authorization 授权模型

阶段 5 Resource Server 验 token

阶段 6 OAuth2 Client 拿 token

阶段 7 Authorization Server 发 token

阶段 8 自定义 Provider Manager Token Claims Grant

每个阶段的目标也很明确:

阶段 你应该掌握什么
安全基础 认证、授权、session、token、scope、authority
Spring Security 过滤器链 DelegatingFilterProxyFilterChainProxySecurityFilterChain
认证模型 AuthenticationAuthenticationManagerAuthenticationProvider
授权模型 AuthorizationManagerGrantedAuthority、方法安全
Resource Server Bearer Token、JWT、Opaque Token
OAuth2 Client ClientRegistrationOAuth2AuthorizedClientManager
Authorization Server RegisteredClientOAuth2AuthorizationOAuth2TokenGenerator
扩展能力 自定义认证、自定义授权、自定义 claims、自定义 grant

最后一张心智速记表

问题 看哪里
请求为什么没进 Controller? SecurityFilterChain 和过滤器顺序
为什么返回 401 认证失败或未认证,查 AuthenticationEntryPoint
为什么返回 403 已认证但权限不足,查 AuthorizationManagerAccessDeniedHandler
用户密码在哪里校验? AuthenticationProviderPasswordEncoder
token 为什么无效? Resource Server 的 JWT 解码或 Opaque introspection
scope 为什么没生效? scope 到 GrantedAuthority 的转换
token 里怎么加业务字段? OAuth2TokenCustomizer
token 怎么撤销? Authorization Server 的授权状态和 revocation 机制

真正掌握 Spring Security + OAuth2 的标志,不是能背出多少配置,而是看到一个安全问题时,能快速判断它属于哪一层:

  • 是请求没有匹配到正确的安全链?
  • 是凭证提取失败?
  • 是认证 Provider 没生效?
  • 是权限映射不对?
  • 是 token 颁发、校验、撤销的边界没设计清楚?

能定位到这一层,问题通常就已经解决了一半。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐