OAuth 是什么
授权 vs 认证;与 JWT 登录的分工。
OAuth 是什么
这一课你将能做到
- 用自己的话说清 认证(Authentication,你是谁) 与 授权(Authorization,你被允许做什么) 的区别,并指出「用 GitHub 登录」同时触发了这两件事
- 画出 OAuth 2.0 四角色图(Resource Owner / Client / Authorization Server / Resource Server),把浏览器、你的 Spring 后端、GitHub 准确归位
- 用 curl 模拟「不带 token / 带假 token」访问受保护 API,说清 401 与 403 在 OAuth 语境下的语义差别
为什么要学
先看一个每天都在发生的需求:产品经理走过来,「给我们网站加一个『用 GitHub 登录』的按钮」。你面前的路有三条:
- 反模式:在自家页面放表单,让用户把 GitHub 的账号密码填进来,你替他去登。这等于让用户把银行密码交给前台代管——任何一方泄露都是灾难,用户也不傻,不敢填。
- 标准答案:把用户带到 GitHub 的页面完成登录与授权,GitHub 回给你一张「通行证」,你再凭它去取用户资料。这就是 OAuth 2.0。
- 厂商私有方案:早期微信、QQ 有各自的登录 SDK,思想类似但细节私有;如今主流平台基本对齐 OAuth 2.0 / OIDC。
人话版:OAuth 解决的问题是——让应用 A 代表用户去访问应用 B 里的资源,但全程不需要用户把 B 的密码交给 A。
和面试的关系:「OAuth 四种授权模式的差异」「为什么授权码流最安全」「PKCE 解决什么」是后端与全栈面试的高频题。这一课先把角色与全局图建立起来,后面 11 课的细节才不会变成名词堆砌。
和本站的关系:本站登录用的是自研 JWT 方案——用户提交账号密码,backend 校验后签发 JWT,前端带着这个 token 调 /admin/** 等接口。这套方案管「本站认人」;OAuth 管的是「别站认人 / 对外开放授权」。两条轨并存、各管一段,本 track 讲的就是第二条轨。
先建立图像
把 OAuth 想成酒店房卡系统:
房客(Resource Owner,资源的主人 = 用户)
│ 在前台出示身份证,同意「保洁可以在 14:00-15:00 进房间」
▼
前台(Authorization Server,授权服务器,如 GitHub)
│ 发给保洁一张「限时、限用途」的房卡(access_token)
▼
保洁 App(Client,你的应用)
│ 拿房卡开门,全程没碰过房客的身份证原件
▼
房门门锁(Resource Server,资源服务器,如 api.github.com)
验卡:有效吗?时段对吗?用途对吗?→ 开门或拒绝
| 角色 | 一句人话 | 现实中的例子 |
|---|
| Resource Owner | 资源真正的主人 | 用户本人 |
| Client | 想代表用户办事的第三方应用 | 你的网站、你的 App |
| Authorization Server | 验证用户并签发通行证的服务 | GitHub、Google、微信开放平台 |
| Resource Server | 真正存数据、提供 API 的服务 | api.github.com、你自己的 /api/v1/** |
一次完整授权的时序(先看懂箭头,下一课再逐个拆参数):
浏览器 你的后端(Client) 授权服务器(AS) 资源服务器(RS)
│ ①点「GitHub登录」 │ │ │
│ ─────────────────────► │ ②302 跳授权页 │ │
│ ◄────────────────────────────────────────────│ │
│ ③用户在 GitHub 登录并点「同意」 │ │
│ ◄──── 302 回 callback,带一次性 code ─────────│ │
│ ④GET /oauth/callback?code&state │ │
│ ─────────────────────► │ │ │
│ │ ⑤code+secret 换 token│ │
│ │ ───────────────────►│ │
│ │ ◄── access_token ────│ │
│ │ ⑥Bearer token 调 API│ │
│ │ ──────────────────────────────────────► │
│ │ ◄───────────── 用户资料 ──────────────── │
两个关键事实:用户的 GitHub 密码只出现在 GitHub 的页面上;access_token 从头到尾没在浏览器地址栏出现过。
跟着做
步骤 1:拆一次「用 GitHub 登录」,填四角色
1. 用户点击按钮后,浏览器被带去了哪里? → GitHub 的授权页(Authorization Server)
2. 用户点「同意」后,浏览器被带回了哪里? → 你在 GitHub 后台预登记的 redirect_uri(Client)
3. 谁拿着 code 去换 access_token? → 你的后端(Client 的服务器端)
4. 谁最终返回用户的 id 和头像? → GitHub 的 API(Resource Server)
步骤 2:用 curl 建立「资源服务器只认 token」的直觉
# 不带任何 token,请求 GitHub 受保护接口 → 401
curl -i https://api.github.com/user
# 带一个假 token → 仍是 401
curl -i https://api.github.com/user \
-H "Authorization: Bearer ghp_this_is_fake_token"
观察响应头里的 WWW-Authenticate: Bearer。建立直觉:资源服务器不认识你浏览器里的 cookie,它只认请求头里的 Authorization: Bearer <token>。
步骤 3:在浏览器里亲眼看一次授权页
https://github.com/login/oauth/authorize?client_id=某公开应用的client_id
你会看到 GitHub 的授权页面。这就是授权流程的第一跳——浏览器整页跳转,不是 AJAX。URL 里的 client_id 告诉 GitHub 是哪个应用在申请。
步骤 4:对照本站,写出两条轨
本站密码登录(认证轨):
用户名+密码 → POST /auth/login → backend 校验 → 签发本站 JWT → Bearer 调受保护接口
联邦登录(OAuth 轨,本 track 主题):
用户在 GitHub 登录并同意 → 你的后端拿 code 换 token → 拉到用户身份
→ 本站 User 表 findOrCreate → backend 签发本站 JWT → 后续与密码登录完全一样
关键结论:OAuth 认完人之后,本站仍然发自己的 JWT。GitHub 的 access_token 只用来调 GitHub 的 API,不会变成你站内接口的钥匙。
步骤 5:把三个英文词各翻译成一句中文
Authentication —— 证明「你是张三」
Authorization —— 证明「张三同意这个 App 读他的资料」
Delegation —— 张三把「读资料」委托给了 App,但密码没给
讲清楚要点
- OAuth 2.0 是授权框架,不是登录协议。它回答「App 能否访问资源」,不规定「如何证明眼前这人是谁」。产品上的「第三方登录」通常是 OAuth + OIDC(第 4 课)或 OAuth + 用户信息 API 的组合。
- 用户密码绝不过 Client 的手。整个协议的设计目标就是消灭「密码反模式」:密码只出现在 Authorization Server 的页面上。
- token 有两种典型形态:不透明字符串(资源服务器需回问授权服务器才懂)和 JWT(自包含、可验签)。本站自研 JWT 属于后者,OAuth 场景的 access_token 也经常是 JWT——但「是 JWT」不等于「是 OAuth」,协议步骤才是 OAuth。
- 四角色缺一不可。排错的第一问永远是「卡在哪个角色」:授权页打不开是 AS 的事,callback 404 是 redirect_uri 的事,换 token 失败是 code/secret 的事,调 API 401 是 RS 的事。
- Client 分机密客户端(confidential)与公共客户端(public)。有后端、能保管 client_secret 的是前者;SPA、原生 App 藏不住 secret,属于后者,必须用 PKCE(第 3 课)。
- 本站自研 JWT 与 OAuth 不冲突。密码轨和联邦轨最终都汇聚到「backend 签发本站 JWT」,站内权限模型不用因为引入 OAuth 而重写。
你可能会懵的地方
-
「OAuth」这个词里的 Auth 到底是认证还是授权?
是授权(Authorization)。这是面试常挖的坑:OAuth 全名 Open Authorization,解决「代用户访问资源」;标准化的「认证」由 OIDC 补齐。
-
access_token 和本站 JWT 是一个东西吗?
格式上可能都是 JWT,但发行方、受众、校验方式完全不同。本站 JWT 由 backend 用自己的密钥签发、给本站接口用;GitHub 的 access_token 由 GitHub 签发、给 GitHub 的 API 用。互不通用。
-
为什么不让前端直接和 GitHub 交互拿 token?
机密客户端的 client_secret 不能进前端;SPA 没有后端也要走 PKCE 授权码流。在前端拼 secret 是高危错误。
-
Resource Owner 和 Resource Server 名字像,怎么记?
Owner 是人,Server 是机器。房客 vs 门锁。
-
微信登录是 OAuth 吗?
思想同源(跳转授权、code 换 token),但细节是微信自己的私有实现(如网页授权的 snsapi_userinfo)。学透标准 OAuth 后,看厂商文档会非常快。
错法对照
| 错法 | 问题 | 正解 |
|---|
| 自建表单收集用户在第三方平台的密码 | 密码泄露即灾难,用户也不会信任 | 跳转到 IdP 授权页,Client 只接收 code/token |
| 以为「OAuth 就是发个 JWT 当登录态」 | 混淆协议与 token 格式,设计必然走偏 | 先分清协议步骤与 token 形态,JWT 只是常见载体 |
| 把 GitHub 的 access_token 直接拿来调本站 /admin | 发行方不同,本站无法验签,等于裸奔 | 映射本地用户后,由 backend 签发本站 JWT |
| 四角色图只画「前端和数据库」 | 漏掉 AS/RS 后所有排错都找不到方向 | 任何 OAuth 讨论先摆出四角色 |
| 把 scope 授权(OAuth)与站内角色(RBAC)混为一谈 | 语义不同,权限模型会乱 | OAuth 管「用户对 App 的授权」,RBAC 管「站内用户能做什么」 |
卡点急救
- 现象:觉得 OAuth 概念太多,不知道从哪开始。先把四角色图默画三遍,再用本课的 GitHub authorize URL 亲眼看一次授权页。后续所有细节都挂在这张图上加箭头。
- 现象:分不清 401 和 403 该查什么。401 = 没带 token 或 token 无效/过期,查「凭证链」;403 = token 有效但权限不够,查「scope/角色」。本站接口同理。
- 现象:资料里一会儿说 access_token 是 JWT,一会儿说是不透明串,懵了。两种都合法,取决于授权服务器的实现。GitHub 发的是不透明串,很多企业 IdP 发 JWT。遇到具体平台,查文档确认形态。
面试常问
-
OAuth 2.0 有哪四种授权模式(grant type)?
授权码模式(Authorization Code)、隐式模式(Implicit,已废弃)、密码模式(Resource Owner Password Credentials,已不建议)、客户端凭据模式(Client Credentials,用于纯机器对机器)。另有 PKCE 扩展与设备码模式(Device Flow)。答题时点出「授权码是主路,Implicit 与密码模式因安全原因被 OAuth 2.1 淘汰」是加分项。
-
为什么说授权码模式最安全?
它把「浏览器经手的东西」压缩到一枚一次性、短寿命的 authorization code;真正的 access_token 只在 Client 后端与授权服务器之间的服务端信道交换,不出现在 URL、历史记录、Referer 中;再叠加 state 防 CSRF、PKCE 防 code 拦截,攻击面最小。
-
你们站为什么自研 JWT 而不直接上 OAuth?
因为只有自己家的前端在登录,没有第三方应用接入、也没开放 API,引入 OAuth 属于过度设计;自研 JWT(backend 签发、验签)简单可控。当出现「用 GitHub/Google 登录」或「第三方开发者调我们的 API」的需求时,再按本 track 引入 OAuth,且最终仍发本站 JWT 管会话。
自测三问
- 认证与授权各回答什么问题?「用 Google 登录」主要解决哪个、顺带解决哪个?
- 默写 OAuth 四角色,并给每个角色找一个现实系统对号入座。
- 本站密码登录和 GitHub 联邦登录,最终的会话凭证分别是谁签发的?
小结一句话
OAuth 2.0 = 让用户授权第三方应用访问资源、全程不交密码的框架;认清四角色与两条轨(站内 JWT / 联邦 OAuth),后面所有细节都挂在这张图上。
延伸