文章目录
- 前言
- 一、spring-security-oauth2
- 二、spring-security-jwt
- 三、OAuth2 详解
- 一、从"门禁卡"说起:OAuth2 到底解决了什么问题
- 1.1 一个你每天都遇到的场景
- 1.2 传统方式的致命缺陷
- 1.3 OAuth2 到底是什么
- 二、四个人一台戏:OAuth2 的核心角色
- 三、令牌三兄弟:Access Token、Refresh Token 和 JWT
- 3.1 Access Token —— 门禁卡
- 3.2 Refresh Token —— 换卡凭证
- 3.3 JWT —— 自包含令牌
- 四、四大授权模式:选对武器才能打赢仗
- 4.1 授权码模式(Authorization Code)—— 王者
- 完整流程图` `
- 关键问题:为什么叫"授权码模式"?为什么要有 code 这一步?
- 4.2 简化模式(Implicit)—— 被时代抛弃的快枪手
- 4.3 密码模式(Resource Owner Password)—— 已死
- 4.4 客户端凭证模式(Client Credentials)—— 机器人的世界
- 4.5 四种模式对比速查表
- 五、PKCE:给授权码模式加一层装甲
- 5.1 授权码模式的隐患
- 5.2 PKCE 做了什么
- 5.3 OAuth 2.1 的规定
- 六、Spring Security 实战:10 分钟搭一个 OAuth2 授权服务器
- 6.1 项目依赖
- 6.2 授权服务器配置` `
- 6.3 资源服务器配置` `
- 6.4 资源接口` `
- 6.5 配置 application.yml` `
- 6.6 验证流程
- 七、细说 Token 安全:Access Token 过期了怎么办
- 7.1 Token 生命周期的设计哲学` `
- 7.2 Refresh Token 轮转(Rotation)
- 7.3 Token 存储:前端到底该放哪?
- 八、OAuth2 + JWT 深度融合:从原理到代码
- 8.1 JWT 的结构深入
- 8.2 资源服务器如何验证 JWT` `
- 8.3 手动解析 JWT(不用 Spring Security)` `
- 8.4 自定义 JWT 中的 Claims
- 九、生产环境的七大安全最佳实践
- 9.1 始终使用 HTTPS
- 9.2 Token 不存前端
- 9.3 最小权限原则
- 9.4 验证 redirect_uri
- 9.5 使用 state 参数防 CSRF
- 9.6 启用 PKCE(即便是机密客户端)
- 9.7 监控异常 Token 使用
- 十、面试八股速通:高频问题一网打尽
- 10.1 OAuth2 的四种角色是什么?
- 10.2 授权码模式为什么要先发 code 再换 token?
- 10.3 PKCE 解决了什么问题?原理是什么?
- 10.4 JWT 和 OAuth2 是什么关系?
- 10.5 Access Token 过期了怎么办?
- 10.6 Refresh Token 轮转是什么?
- 10.7 如何在微服务之间传递 token?
- 10.8 OAuth2 和 SSO(单点登录)是什么关系?
- 10.9 Cookie 和 Token 两种认证方式的区别?
- 写在最后
前言
Spring Security
- spring-security-oauth2
- spring-security-jwt
一、spring-security-oauth2
- 各版本的名称及发布日期:
| 版本 | 适用 Spring Boot | 适用 Spring Security | 状态 | 备注 |
|---|---|---|---|---|
| 2.5.x | 2.5.x - 2.7.x | 5.5.x - 5.7.x | 维护中 | 推荐稳定版 |
| 2.4.x | 2.4.x - 2.6.x | 5.4.x - 5.6.x | 已弃用 | 建议升级 |
| 2.3.x | 2.3.x - 2.5.x | 5.3.x - 5.5.x | 已弃用 | 不推荐 |
| 2.2.x | 2.2.x - 2.4.x | 5.2.x - 5.4.x | 已弃用 | 仅旧项目 |
| 2.1.x | 2.1.x - 2.3.x | 5.1.x - 5.3.x | 已弃用 | 不推荐 |
- Spring Boot 2.7.x + JDK 8的环境推荐版本
<dependency><groupId>org.springframework.security.oauth</groupId><artifactId>spring-security-oauth2</artifactId><version>2.5.2.RELEASE</version><!-- 最新稳定版 --></dependency>- Spring Security 版本匹配
<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-core</artifactId><version>5.7.6</version><!-- 推荐版本 --></dependency>- 注意事项
Spring Security OAuth2 已弃用
- 官方推荐迁移到 Spring Authorization Server(Spring Security 5.7+ 内置)
- 新项目建议直接使用 spring-security-oauth2-authorization-server(Spring Boot 3.x+)
JDK 8 兼容性
- Spring Security OAuth2 2.5.x 仍支持 JDK 8
- 如需升级到 Spring Boot 3.x,需切换至 JDK 17+
常见问题
- 如果遇到 NoSuchMethodError,检查依赖冲突(如旧版 spring-security-jwt)
- 推荐使用 spring-security-oauth2-autoconfigure 简化配置
- 迁移建议
如果计划升级,可参考以下路径:
- 短期方案:继续使用 OAuth2 2.5.x(维护模式)
- 长期方案:迁移到 Spring Authorization Server(需要 Spring Boot 3.x+)
- Spring Authorization Server
二、spring-security-jwt
版本选择
spring-security-jwt 最终稳定版:
<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-jwt</artifactId><version>1.1.1.RELEASE</version><!-- 官方最后维护版本 --></dependency>- 兼容性说明:
- 需搭配 spring-security-oauth2 2.5.x 使用(如 2.5.2.RELEASE)。
- 不支持 Spring Boot 3.x 或 JDK 17+。
2. 基础配置示例
- JWT 密钥配置
JwtConfig.java
@ConfigurationpublicclassJwtConfig{@Value("${jwt.secret}")privateStringsecret;@BeanpublicJwtAccessTokenConverterjwtAccessTokenConverter(){JwtAccessTokenConverterconverter=newJwtAccessTokenConverter();converter.setSigningKey(secret);// 设置签名密钥returnconverter;}@BeanpublicTokenStoretokenStore(){returnnewJwtTokenStore(jwtAccessTokenConverter());}}需在 application.properties 中配置 jwt.secret=your-256-bit-secret。
OAuth2 资源服务器配置
ResourceServerConfig.java
@Configuration@EnableResourceServerpublicclassResourceServerConfigextendsResourceServerConfigurerAdapter{@Overridepublicvoidconfigure(HttpSecurityhttp)throwsException{http.authorizeRequests().antMatchers("/api/public/**").permitAll().anyRequest().authenticated();}}3. 生成和解析 JWT 令牌
- 生成令牌(授权服务器端)
JwtTokenEnhancer.java
classJwtTokenEnhancerimplementsTokenEnhancer{@OverridepublicOAuth2AccessTokenenhance(OAuth2AccessTokenaccessToken,OAuth2Authenticationauthentication){Map<String,Object>additionalInfo=newHashMap<>();additionalInfo.put("organization","example-org");((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(additionalInfo);returnaccessToken;}}- 解析令牌(资源服务器端)
JwtUtils.java
classJwtUtils{publicstaticClaimsextractClaims(Stringtoken,Stringsecret){returnJwts.parser().setSigningKey(secret.getBytes()).parseClaimsJws(token).getBody();}}4. 注意事项
- 密钥安全:
- 替换 JwtAccessTokenConverter 的签名逻辑)。
- 依赖冲突:
- 避免引入 jjwt 旧版本(如 0.9.x),可能引发 NoSuchMethodError。
- 迁移建议:
- 新项目推荐使用 spring-security-oauth2-authorization-server + jjwt 0.12.x。
- jwt
三、OAuth2 详解
朋友老周被一个需求卡住了:新系统要访问老系统的用户数据,两边的鉴权怎么搞?"总不能把老系统密码直接发给新系统吧?"我说你这场景有现成的实现方案——OAuth2,核心思路就三句话:用户不暴露密码,第三方拿一张有权限、有时效的令牌去访问资源,令牌过期了还能静默续上,不用重新登录。老周一拍大腿:“懂了,就是发张门禁卡!”
门禁卡的比喻很贴切,但除了架构师,恐怕大多数开发者对 OAuth2 的认知,也只是停在了这个比喻上。下面的内容不仅停留在会用,还能把核心原理和最佳实践讲得清清楚楚。
一、从"门禁卡"说起:OAuth2 到底解决了什么问题
1.1 一个你每天都遇到的场景
假设你入住一家酒店,前台给了你一张门禁卡。这张卡有以下几个特点:
- 只对特定房门有效(你只能进你的房间,不能进别人的)
- 有时间限制(过了退房时间就打不开了)
- 能干什么是确定的(开门可以,但不能用它去餐厅签单)
- 随时可以挂失(前台可以把卡作废)
这张门禁卡,就是一种授权机制。
现在把这个场景搬到互联网世界:
你打开一个叫"咕咚笔记"的第三方应用,它说"可以用微信账号登录"。你点了授权,微信给你弹了个确认框:“咕咚笔记申请获取你的昵称和头像”。你点了同意,然后咕咚笔记就能拿到你的微信昵称和头像了。
这个过程是怎么实现的?答案是:OAuth2。
1.2 传统方式的致命缺陷
在没有 OAuth2 的年代,第三方应用想要访问你在另一个服务上的数据,只能这样做:
你 → 告诉咕咚笔记你的微信账号密码 → 咕咚笔记拿着你的密码去微信服务器登录 → 拿到你的数据问题在哪?你把微信的密码给了咕咚笔记。
咕咚笔记是好人还好说,但如果它把密码存了明文呢?
如果它不止读取了昵称头像,还偷偷翻了你的聊天记录呢?
如果你在 10 个应用里都用了这种"给密码"的方式,改一次密码 = 10 个应用全部失效
OAuth2 的解决思路非常朴素:让用户在不暴露自己密码的情况下,授权第三方应用访问自己在某个服务上的受保护资源。
你 → 在微信授权页点"同意" → 微信发给咕咚笔记一张"临时门禁卡" → 咕咚笔记拿卡访问你的昵称头像你的密码从头到尾只有你知道,微信服务器知道。咕咚笔记拿到的,只是一个有时效性、有权限范围的令牌。
1.3 OAuth2 到底是什么
OAuth2(Open Authorization 2.0)是一个授权框架,它允许第三方应用在资源拥有者的授权下,获取对受保护资源的有限访问权限。
注意这两个关键词——授权而非认证,有限而非无限。
| 概念 | 通俗解释 |
|---|---|
| 认证(Authentication) | “你是谁?”——出示身份证 |
| 授权(Authorization) | “你能干什么?”——刷门禁卡进房间 |
OAuth2 主要解决的是授权问题,虽然在实际使用中它经常被用来做认证(也就是"用微信登录"这类场景),但那其实是基于 OAuth2 之上的扩展(OpenID Connect,也就是我们常说的 OIDC)。
二、四个人一台戏:OAuth2 的核心角色
在深入流程之前,你必须先把这四个角色刻在脑子里。整个 OAuth2 就是这四个角色之间的交互。
┌──────────────┐ ┌──────────────────┐- 资源拥有者(Resource Owner):用户本人。拥有数据的所有权。
- 客户端(Client):第三方应用。想要访问用户数据的那一方。
- 授权服务器(Authorization Server):负责认证用户并颁发令牌的服务。
- 资源服务器(Resource Server):存放用户资源的服务,验证令牌后提供数据。
在实际的工程实现中,授权服务器和资源服务器经常是同一个系统的不同模块,但它们在 OAuth2 协议中是逻辑上独立的角色。
小贴士:面试的时候面试官经常问"OAuth2 有哪几个角色",这是一道送分题,四个角色一个都不能少。
三、令牌三兄弟:Access Token、Refresh Token 和 JWT
在正式进入授权流程之前,我们先认识 OAuth2 中最核心的三个"令牌"概念。
3.1 Access Token —— 门禁卡
Access Token是 OAuth2 最核心的令牌。客户端拿着它去资源服务器请求数据,资源服务器验证它有效就返回数据。
它的特点:
- 短期有效:通常是几十分钟到几小时
- 包含权限信息:能访问什么、不能访问什么
- 对客户端不透明:客户端不需要(也不应该试图)解析它的内容
3.2 Refresh Token —— 换卡凭证
Refresh Token用于在 Access Token 过期后换取新的 Access Token,而不需要让用户重新授权。
为什么要这样设计?因为 Access Token 是频繁在网络上传输的,泄露风险大,所以设置较短的有效期。Refresh Token 只在与授权服务器通信时才使用,暴露面小得多,所以可以设置较长的有效期(几天到几个月)。
3.3 JWT —— 自包含令牌
JWT(JSON Web Token)不是 OAuth2 协议要求的,但在实际落地中极其常见。它是一种自包含的令牌格式:
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMDAxIn0.xxxxxxxxxxxxxxxxxx为什么 JWT 和 OAuth2 常常一起出现?因为传统的 Access Token 可能只是一个随机字符串(“不透明令牌”),资源服务器每次收到这种令牌,都需要去授权服务器校验。而 JWT 本身携带了用户信息和签名,资源服务器可以自己验证 JWT 的有效性,不需要每次都远程调用授权服务器——这叫"无状态验证",性能更好。
关键区分:OAuth2 是授权协议,定义的是"怎么发令牌、怎么用令牌";JWT 是令牌格式,定义的是"令牌里写什么、怎么签名"。两者不是同一个层面的东西。
四、四大授权模式:选对武器才能打赢仗
OAuth2 定义了四种授权模式(Grant Type)。这是整个 OAuth2 最核心的知识点,也是面试中必考的。
OAuth2 四种授权模式4.1 授权码模式(Authorization Code)—— 王者
这是最经典、最安全、使用最广泛的模式。如果你只记住一种,就记这一种。
完整流程图
资源拥有者 客户端 授权服务器 资源服务器关键问题:为什么叫"授权码模式"?为什么要有 code 这一步?
这是面试中的高频追问。答案是:安全——确保令牌只发给真正的后端服务器,而不是浏览器。
具体来说:
code 是通过**浏览器重定向**传递的,会暴露在 URL 中如果用
code直接当令牌用,任何一个看到 URL 的人都能拿它访问数据但
code是一次性的,而且换 token 时需要带上client_secret(客户端密钥)client_secret 只存在后端服务器上,浏览器里没有所以即使别人截获了
code,没有client_secret也换不到真正的 Access Token
**Authorization Code 流程的核心安全逻辑就是:code 通过不安全的渠道(浏览器)传输,但换 token 的操作只通过安全的渠道(后端到后端)完成。
**
浏览器(不安全通道) 后端服务器4.2 简化模式(Implicit)—— 被时代抛弃的快枪手
简化模式省略了"用 code 换 token"这一步,授权服务器直接把 Access Token 返回给了浏览器。
为什么被抛弃?因为 Access Token 直接暴露在浏览器端:
浏览器的 URL 片段(
#access_token=xxx)虽然不会发给服务器,但可以被 JavaScript 读取浏览器的历史记录、第三方脚本、浏览器插件都可能泄露这个 token
没有
client_secret的保护,任何一个拿到 token 的人都能用
OAuth 2.1 已经明确不推荐使用 Implicit 模式,改为推荐带 PKCE 的授权码模式。
4.3 密码模式(Resource Owner Password)—— 已死
用户直接把用户名和密码交给客户端,客户端拿它们去授权服务器换 token。
这又回到了我们最开始说的那个问题:你把密码给了第三方。只有一个场景勉强可以用:客户端和授权服务器是同一家公司的产品(比如微信 App 和微信服务端)。即便如此,OAuth 2.1 也已经将其标记为废弃。
4.4 客户端凭证模式(Client Credentials)—— 机器人的世界
当客户端本身就是资源拥有者,不涉及"用户"的时候使用。比如:
后端微服务 A 调用微服务 B 的 API
定时任务访问自己的数据
这种模式最简单:客户端拿自己的client_id+client_secret直接去授权服务器换一个 Access Token。
4.5 四种模式对比速查表
| 模式 | 适用场景 | 安全性 | OAuth 2.1 状态 | 是否涉及用户 |
|---|---|---|---|---|
| 授权码模式 | Web 应用(有后端) | ⭐⭐⭐⭐⭐ | ✅ 推荐 | 是 |
| 简化模式 | 纯前端 SPA | ⭐⭐ | ❌ 不推荐 | 是 |
| 密码模式 | 高度可信应用 | ⭐ | ❌ 废弃 | 是 |
| 客户端凭证模式 | 服务间调用 | ⭐⭐⭐⭐ | ✅ 推荐 | 否 |
五、PKCE:给授权码模式加一层装甲
如果你只了解了上面四种模式,面试官很可能追问:“那 PKCE 是什么?”
5.1 授权码模式的隐患
即使是授权码模式,也存在一个攻击场景:
攻击者在自己设备上发起授权流程,拿到 code
攻击者在受害者的应用回调 URL 中拦截到 code(比如通过恶意 App 注册了相同的回调 scheme)
攻击者用自己的 code 替换掉受害者的 code
受害者登录后,攻击者能通过自己的 code 关联到受害者的 session
这种攻击叫授权码拦截攻击(Authorization Code Interception Attack)。
5.2 PKCE 做了什么
PKCE(Proof Key for Code Exchange,发音 “pixy”)在授权码流程中增加了两个步骤:
客户端生成 code_verifier(随机字符串)关键安全点在于:
code_challenge 是 `code_verifier` 的哈希值,无法反推- 只有最初生成
code_verifier的客户端,才能在最后一步提供正确的code_verifier - 任何中间人截获了 code 也没用,因为它没有
code_verifier
一句话总结 PKCE:我先把锁给你(code_challenge),等我拿到授权码后来换 token 时,我再证明我有钥匙(code_verifier)。换不了钥匙的人,拿到授权码也没用。
5.3 OAuth 2.1 的规定
在 OAuth 2.1 中,所有使用授权码模式的客户端都必须使用 PKCE。这不是可选增强,而是硬性要求。
六、Spring Security 实战:10 分钟搭一个 OAuth2 授权服务器
理论讲得差不多了,手该上键盘了。我们来搭一个可以跑的 OAuth2 授权服务器。
6.1 项目依赖
Spring Boot 3.x + Spring Security 6.x:
<!-- pom.xml -->6.2 授权服务器配置
@Configuration6.3 资源服务器配置
@Configuration6.4 资源接口
@RestController6.5 配置 application.yml
server:6.6 验证流程
启动项目后,在浏览器里访问:
http://localhost:8080/oauth2/authorize你会依次看到:
Spring Security 默认的登录页面(输入 zhangsan / 123456)
授权确认页面(“咕咚笔记申请获取你的 read 权限”)
点击同意后,浏览器重定向到
http://localhost:8081/...?code=xxxx
拿到了code,后端就可以用code+client_secret+code_verifier去换 Access Token 了:
curl -X POST http://localhost:8080/oauth2/token \响应:
{七、细说 Token 安全:Access Token 过期了怎么办
7.1 Token 生命周期的设计哲学
Access Token: 有效期短(15分钟 ~ 2小时)为什么要这样分层?最小化暴露面。
Access Token 在每次 API 请求中都会发送,暴露在网络上,所以让它短命
Refresh Token 只在 token 过期时使用一次,暴露次数极少
即使 Access Token 泄露,攻击者也只有十几分钟的窗口
7.2 Refresh Token 轮转(Rotation)
这是 OAuth 2.1 的最佳实践:**每次用 Refresh Token 换新 Access Token 时,同时发放一个新的 Refresh Token,并把旧的 Refresh Token 作废。
**
请求:refresh_token_123 → 换取新的 access_token好处是:如果攻击者偷到了一个 Refresh Token,但用户使用它会触发轮转,那么当用户也用了一下这个 Refresh Token(或攻击者用完之后用户再用),服务端就会发现“这个 token 已经被用过了”——说明可能被泄露了,可以立刻作废该用户的所有 token。
7.3 Token 存储:前端到底该放哪?
这也是面试和实际开发中都绕不过的问题。
| 存储方式 | 安全性 | XSS 风险 | CSRF 风险 | 推荐度 |
|---|---|---|---|---|
| localStorage | 低 | ❌ 可被 JS 读取 | 无 | 不推荐 |
| sessionStorage | 低 | ❌ 可被 JS 读取 | 无 | 不推荐 |
| Cookie(HttpOnly) | 高 | ✅ JS 无法读取 | 需防 CSRF | ⭐ 推荐 |
| 内存变量 | 高 | ✅ | ✅ | 刷新即丢失 |
| BFF(Backend For Frontend) | 最高 | ✅ | ✅ | ⭐⭐ 最推荐 |
最佳实践:Token 存在后端的 session 中,前端只持有一个 session cookie(HttpOnly + Secure + SameSite=Strict)。这就是 BFF 模式——前端根本不碰 token。
八、OAuth2 + JWT 深度融合:从原理到代码
8.1 JWT 的结构深入
一个 JWT 由三部分组成,用.分隔:
Header.Payload.SignatureHeader(算法信息):
{"alg": "RS256","kid": "key-id-001","typ": "JWT"}Payload(声明信息):
{Signature(签名):
RSASHA256(8.2 资源服务器如何验证 JWT
资源服务器收到请求(Authorization: Bearer <jwt>)Spring Security 的oauth2ResourceServer().jwt()配置自动完成了这一切。
8.3 手动解析 JWT(不用 Spring Security)
import io.jsonwebtoken.Claims;8.4 自定义 JWT 中的 Claims
有时候你需要在 JWT 中加入业务相关的信息:
@Bean资源服务器侧读取:
@GetMapping("/admin/info")九、生产环境的七大安全最佳实践
这一节不讲代码,讲原则。每一条都是用血泪教训换来的。
9.1 始终使用 HTTPS
全文最重要的一条。如果授权请求走的是 HTTP,中间人可以截获code;如果 token 端点走的是 HTTP,中间人可以截获client_secret。一旦泄漏,全盘皆输。
生产环境强制 HTTPS,没有任何商量的余地。
9.2 Token 不存前端
我们在 7.3 节已经讨论过了。localStorage 存 token 等价于在大马路上贴银行卡密码。用 HttpOnly Cookie + BFF 模式。
9.3 最小权限原则
申请 scope 时只申请你真正需要的。一个只展示用户昵称的应用,不要申请"读取好友列表""发送私信"的权限。
9.4 验证 redirect_uri
授权服务器必须严格校验redirect_uri,只允许预先注册的地址。不校验的话,攻击者可以诱导用户授权后把 code 发到自己的服务器。
9.5 使用 state 参数防 CSRF
在发起 OAuth2 授权请求时,生成一个随机字符串作为state参数,并在回调时验证。这可以防止攻击者诱导用户点击一个已预授权的链接。
// 发起授权时9.6 启用 PKCE(即便是机密客户端)
虽然 PKCE 最初是为公开客户端(无法安全存储 client_secret 的应用)设计的,但 OAuth 2.1 要求所有客户端都使用。多一层防护总没错。
9.7 监控异常 Token 使用
同一个 Refresh Token 被使用了两次 → 可能是泄露
同一个 Access Token 在短时间内从不同 IP 使用 → 可能被盗用
大量 token 签发请求 → 可能是暴力攻击
十、面试八股速通:高频问题一网打尽
10.1 OAuth2 的四种角色是什么?
资源拥有者、客户端、授权服务器、资源服务器。四个角色,一个都别少。
10.2 授权码模式为什么要先发 code 再换 token?
因为code通过浏览器传输(不安全),但换token需要client_secret(只存在后端)。这样确保了即使code泄露,没有client_secret也拿不到真正的 Access Token。
10.3 PKCE 解决了什么问题?原理是什么?
解决了授权码拦截攻击。原理是客户端先生成code_verifier和它的哈希值code_challenge,授权时传 challenge,换 token 时传 verifier,授权服务器验证两者匹配。只有最初发起授权请求的客户端才知道code_verifier。
10.4 JWT 和 OAuth2 是什么关系?
OAuth2 是授权协议,定义了"怎么发、怎么用";JWT 是令牌格式,定义了"长什么样"。OAuth2 的 Access Token 可以用 JWT 格式,也可以用随机字符串(不透明令牌)。
10.5 Access Token 过期了怎么办?
用 Refresh Token 去换新的 Access Token,不需要用户重新登录。如果 Refresh Token 也过期了,才需要用户重新授权。
10.6 Refresh Token 轮转是什么?
每次使用 Refresh Token 时,授权服务器同时发放一个新的 Refresh Token,旧 token 作废。用于检测 Refresh Token 是否被泄露。
10.7 如何在微服务之间传递 token?
下游服务从请求头中获取 token,透传或由网关统一处理:
用户 → Gateway → 服务A → 服务B或者服务 A 使用客户端凭证模式,以自己的身份去调用服务 B。
10.8 OAuth2 和 SSO(单点登录)是什么关系?
OAuth2 本身是授权协议,不能直接用于认证。OpenID Connect (OIDC)是基于 OAuth2 的身份认证层,可以实现 SSO。简单理解:
OAuth2:给你一张门禁卡,你可以进房间
OIDC:给你一张门禁卡,卡上还印了你的名字和照片,可以证明你是谁
10.9 Cookie 和 Token 两种认证方式的区别?
| 维度 | Cookie + Session | JWT Token |
|---|---|---|
| 状态 | 服务端有状态(存 session) | 服务端无状态(token 自包含) |
| 扩展性 | 需要共享 session(如 Redis) | 天然支持分布式 |
| 注销 | 简单(删 session) | 复杂(token 未过期仍有效,需黑名单) |
| 移动端 | 不友好 | 友好(HTTP Header) |
| 安全性 | 需防 CSRF | 需防 XSS(若存前端) |
写在最后
OAuth2 不是一个能"一眼看懂"的协议。它的规范 RFC 6749 是一份篇幅不小的文档,而 OAuth 2.1 还在持续演进中。但好消息是:对于绝大多数 Java 开发者来说,你不需要通读 RFC,你只需要搞懂本文覆盖的这些核心概念和原则。
回顾一下你的学习路径:
- 理解问题:OAuth2 是为了让第三方在不拿密码的情况下访问用户数据
- 记住角色:四个角色、四种模式
- 吃透流程:授权码模式为什么分两步、PKCE 做了什么
- 动手实践:Spring Security 搭一套授权/资源服务器
- 守住安全:七大实践,条条都是真金白银的经验
本文的引用仅限自我学习如有侵权,请联系作者删除。
参考知识
多系统认证授权利器:OAuth2,究竟该如何使用?