前言 无论是“微信扫码登录”、“使用 GitHub 账号登录”,还是企业级微服务架构中的统一鉴权中心,背后都离不开一个大名鼎鼎的协议:OAuth 2.0。 很多后端开发者在学习 Spring Security 时,面对满屏的
OAuth2UserService、OidcUserService、ClientRegistration配置往往是一头雾水,只会复制粘贴。 这篇文章,我们将抛开繁杂的代码,用生活中的大白话和生动的例子,带你从零看透 OAuth 2.0 与 OIDC 的底层逻辑。搞懂了这些理论,再看任何框架的源码都将如同呼吸般自然。
一、 痛点起源:为什么需要 OAuth 2.0?
在理解技术之前,先理解它解决了什么问题。
假设你开发了一个**“云打印”网站(第三方应用)。用户想把你网站里的照片打印出来,但用户的照片都存放在“百度网盘”**里。
❌ 传统的灾难性做法(密码共享模式): 你让用户在你的“云打印”网站里,输入他的百度网盘账号和密码。你的程序拿着密码去模拟登录百度网盘,下载照片,然后打印。
作为程序员,你立刻能闻到这种做法的“坏味道”:
- 权限无限大:你不仅能下载照片,还能删掉用户网盘里的所有资料,甚至修改密码。
- 安全风险极高:万一你的“云打印”数据库被黑客“脱裤”,用户的百度账号就彻底沦陷了。
- 无法单独撤销:用户如果不想让你打印了,他唯一的办法就是去改百度的密码,极其反人类。
✅ OAuth 2.0 的优雅解法(委托授权): 用户点击“导入百度网盘照片”,浏览器跳转到百度的官方页面。百度问用户:“‘云打印’想读取你的照片,你同意吗?”。用户点击同意。 百度给“云打印”后端发一个临时授权码(Token)。 “云打印”拿着这个 Token 去找百度要照片。百度一看 Token,上面写着“只能读照片,不能删,有效期2小时”,于是把照片数据给了“云打印”。
核心定义: OAuth 2.0 的本质是一个委托授权(Delegated Authorization)框架。它允许第三方应用获取有限的访问权限,而绝对不暴露用户的账号密码。
二、 核心概念:不要搞混“认证”与“授权”
这是安全领域的两个基石概念,也是初学者极易踩坑的地方:
- Authentication (认证 - AuthN):证明“你是谁”
- 现实例子:你进高铁站,乘警检查你的身份证。确认你真的是“张三”。
- Authorization (授权 - AuthZ):决定“你能做什么”
- 现实例子:你上高铁,乘务员检查你的车票。这张车票只允许你坐“二等座 05A”,你不能去开动高铁。
🔥 划重点:OAuth 2.0 纯粹是一个“授权”协议! 它关心的是怎么安全地颁发那张“车票”(Token),它其实并不关心那个拿身份证的人到底是谁。(记住这句话,我们后面讲 OIDC 时会考)。
OAuth 2.0 的四大核心角色
为了搞懂协议流转,必须对号入座这四个角色。以**“网页游戏使用微信扫码登录”**为例:
- Resource Owner(资源拥有者):用户(你)。因为微信头像是你的,只有你能决定是否同意给游戏用。
- Client(客户端):网页游戏。它想访问你的资源。(注意:Client 指的是业务侧的第三方应用,不是指浏览器)。
- Authorization Server(授权服务器):微信开放平台授权中心。负责验证用户,询问是否同意,并颁发令牌(Token)。
- Resource Server(资源服务器):存放你头像数据的微信 API 服务器。负责校验 Token 并返回数据。
三、 核心工作流:最经典的“授权码模式”
OAuth 2.0 定义了四种获取 Token 的方式,但目前业界最常用、最安全的是授权码模式 (Authorization Code Grant)。
在整个流程开始前,有一个大前提(应用注册): “网页游戏”必须先去“微信开放平台”注册备案。注册成功后,微信会发给游戏两个极其重要的凭证:
- Client ID(公开的):游戏的账号标识。
- Client Secret(极其保密):游戏的密码,只能存在游戏厂商的后端服务器里,绝不能下发给前端。
准备就绪,我们来看最核心的 5 步流转(以 GitHub 授权为例):
- 请求授权(重定向):用户的浏览器被重定向到 GitHub 授权页面。URL 中带有
client_id。 - 用户同意(登录并授权):用户在 GitHub 页面输入账号密码,点击“同意”。(密码始终在 GitHub 手里)。
- 发放授权码 (Code):GitHub 将浏览器重定向回第三方应用的网页(Callback URL),并在 URL 后面附带一个临时授权码 (
code=xyz123)。 - 用 Code 换 Token(幕后交易):第三方应用的 后端服务器 拿到前端传来的
code,再加上保密的Client Secret,在后台发起 HTTP 请求,直接向 GitHub 索要 Token。 - 请求资源:第三方后端拿着换来的
access_token,去 GitHub 的 API 获取数据。
💡 灵魂拷问:为什么不直接返回 Token,非要给个 Code 多此一举?
看到这里,很多人会产生一个巨大的疑惑:既然第 2 步用户都已经同意授权了,为什么 GitHub 不直接把 access_token 返回给前端浏览器,非要先给个 code,让 Java 后端再去换一次?这不是“脱裤子放屁”吗?
答案恰恰隐藏在刚才提到的 Client Secret(客户端密钥)里。
如果直接把真正的 Token 放在前端的回调 URL 里(比如:?token=xxx),这是极其危险的。浏览器的地址栏记录、恶意插件、甚至截包工具,都能轻易偷走它。
而“授权码模式”精妙地利用了前后端分离的环境,完成了一次完美的“暗度陈仓”:
- 前端只当传声筒:前端浏览器只拿到了一个对黑客毫无用处的临时
Code,并把它传回给 Java 后端。 - 后端进行幕后交易:Java 后端在极其安全的服务器环境里,拿着前端传来的
Code,加上只有服务器才知道的Client Secret,向 GitHub 发起幕后请求。 - 安全交付:GitHub 验证了 Code 和 Secret 都对,这才把真正的 Token 发给 Java 后端。
结论: 真正的 Token 始终只在 GitHub 服务器和你的 Java 后端之间流转,从未暴露在用户的浏览器中。这就是 OAuth 2.0 如此设计的最核心机密!
四、 Token 的真面目:为什么都在用 JWT?
第三方后端千辛万苦拿到了 access_token,它到底长什么样?
以前,Token 是一串毫无意义的随机乱码(透明令牌 Opaque Token)。资源服务器拿到它,必须去查一次数据库或 Redis,才能知道“这个 Token 属于谁?有没有过期?”。
现在,主流架构(尤其是微服务)都在使用 JWT (JSON Web Token)。
JWT 是一段自带信息的 Base64 字符串,由三部分组成(Header.Payload.Signature)。
- Header:记录加密算法。
- Payload (载荷):包含了真正的业务数据,如用户 ID (
sub)、过期时间 (exp) 等。 - Signature (签名):授权服务器用私钥对前两部分生成的防篡改签名。
JWT 的杀手锏:资源服务器拿到 JWT 后,不需要查数据库,只需用公钥验证“签名”是否合法,合法即可直接读取 Payload 里的内容。这种“无状态”特性完美契合了微服务的高并发需求。
五、 OIDC:OAuth 2.0 的终极进化
1. OAuth 2.0 的“天生缺陷”
还记得前面说的吗?OAuth 2.0 只管“授权”。
就像酒店前台给了你一张房卡(access_token),房门(资源服务器)只认卡不认人。它不知道刷卡的是张三还是李四。
如果第三方应用想要在右上角显示**“欢迎你,张三”**,单纯的 OAuth 2.0 是做不到的。 在过去,各大厂商只能自己造轮子,提供各种五花八门、字段名完全不同的获取用户信息的 API。这让接入第三方登录的开发者痛苦不堪。
2. OIDC 救场
为了统一江湖,大佬们推出了基于 OAuth 2.0 的扩展协议:OIDC (OpenID Connect)。
核心公式:OIDC = OAuth 2.0 + 身份认证层 (Identity Layer)
OIDC 并没有推翻原有的流程,它只是在申请授权时,要求在参数里加上一个神奇的词:scope=openid。
授权服务器看到 openid 后,除了返回原有的 access_token 外,还会额外返回一个 id_token!
3. ID Token 与 UserInfo 标准
- ID Token (身份令牌):
它必定是一个 JWT。它相当于用户的“电子身份证”。第三方后端拿到它,直接解码就能拿到标准化的用户信息(如
sub唯一标识、name姓名、picture头像)。 - UserInfo Endpoint:
OIDC 还强制规定了一个标准的
/userinfo接口。如果id_token里的信息不够全,拿着access_token去请求这个标准接口,就能拿到完整的用户 JSON 数据。
终极对比:Access Token vs ID Token
| 特性 | Access Token (访问令牌) | ID Token (身份令牌 / OIDC专属) |
|---|---|---|
| 生动比喻 | 酒店房卡、游乐场门票 | 身份证、护照 |
| 受众是谁 | 资源服务器(用来校验接口访问权限) | 客户端(第三方应用用来知道谁登录了) |
| 包含内容 | 权限范围(能干啥)、时效 | 标准化的用户身份信息(你是谁) |
| 格式限制 | 随便(乱码 或 JWT 均可) | 必须是 JWT 格式 |
六、 总结与展望
通过以上梳理,我们终于拨开了 OAuth 2.0 与 OIDC 的迷雾:
- OAuth 2.0 负责授权,用极其安全的“授权码模式”保证了第三方应用在不接触密码的情况下获取资源权限。
- JWT 负责减负,让微服务架构下的 Token 校验变得无状态且高效。
- OIDC 负责认证,通过标准化的
ID Token彻底解决了第三方登录获取用户身份的混乱局面。