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 串成一张图。


安全问题-大白话

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

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

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

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

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


Spring Security 到底在做什么

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

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

一张图看整体过程:

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

这里有三个对象很关键:

对象可以怎么理解
DelegatingFilterProxyServlet 容器和 Spring Bean 之间的桥
FilterChainProxySpring 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,区别在于认证后它会被标记为可信,并带上完整的主体和权限。

典型认证链路如下:

SecurityContextHolderAuthenticationProviderProviderManagerAuthenticationManagerAuthentication FilterClientSecurityContextHolderAuthenticationProviderProviderManagerAuthenticationManagerAuthentication FilterClient携带凭证发起请求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 或业务策略组件里。


401 和 403 为什么不一样

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

  • 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 ServerAuthorization ServerClient 应用用户Resource ServerAuthorization ServerClient 应用用户点击登录或授权跳转到 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 ServerOpaqueTokenAuthenticationProviderResource ServerClientAuthorization ServerOpaqueTokenAuthenticationProviderResource ServerClient携带 Bearer Token 调 APIauthenticate(token)introspect(token)返回 active principal scopes claimsBearerTokenAuthentication

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授权状态存储接口
OAuth2TokenGeneratortoken 生成器
OAuth2TokenCustomizer定制 token claims 或 headers

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

OAuth2TokenGeneratorOAuth2AuthorizationServiceAuthenticationProviderAuthenticationConverterSecurity Filter ChainClientOAuth2TokenGeneratorOAuth2AuthorizationServiceAuthenticationProviderAuthenticationConverterSecurity Filter ChainClientPOST token endpoint解析协议请求grant Authenticationauthenticate grant requestgenerate tokensaccess token and refresh tokensave OAuth2Authorizationtoken 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_ADMIN、SCOPE_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 过滤器链DelegatingFilterProxy、FilterChainProxy、SecurityFilterChain
认证模型Authentication、AuthenticationManager、AuthenticationProvider
授权模型AuthorizationManager、GrantedAuthority、方法安全
Resource ServerBearer Token、JWT、Opaque Token
OAuth2 ClientClientRegistration、OAuth2AuthorizedClientManager
Authorization ServerRegisteredClient、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 颁发、校验、撤销的边界没设计清楚?

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

Logo

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

更多推荐