OAuth2 的几种方式

2026/06/09 oauth2

多年前,阮一峰老师写过一篇著名的《OAuth 2.0 的四种方式》,我经常把它当成字典来查。最近在折腾 Skills 开发时,随手写了一段设备码(Device Code)授权的逻辑,索性就把常见的 OAuth2 授权方式都重新整理一遍。

传统 4 种方式

先来复习一下老牌的 4 种授权方式,也就是阮一峰老师 2019 年文章中提到的经典模式。

1. 授权码模式 (Authorization Code)

这是什么? 最安全、最常见的授权方式,也就是你平时用微信、GitHub 扫码登录第三方网站的底层逻辑。

它是如何工作的?

  1. 前端网页引导用户跳转到 IDP(身份提供商)服务器:oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=CALLBACK_URL&scope=read
  2. 用户在 IDP 登录成功后,IDP 会重定向回 redirect_uri,并带上一个一次性的 code
  3. 后端服务器拿到 code 之后,在后台用自己的 client_secret 偷偷去 IDP 访问:oauth/token?grant_type=authorization_code&code=AUTHORIZATION_CODE... 兑换真正的 Token。

优点:非常安全,因为真正的 Token 只在后端服务器之间传递,前端根本接触不到。

img

2. 客户端凭证模式 (Client Credentials)

这是什么? 也叫 M2M (Machine to Machine) 方式,纯后端服务之间的“通行证”。

为什么需要它? 当没有真实用户参与,只有微服务 A 需要调用微服务 B 的接口时,就不需要弹窗让用户登录了。

它是如何工作的? 微服务的服务器直接拿着自己的账号密码找 IDP 兑换 Token: token?grant_type=client_credentials&client_id=CLIENT_ID&client_secret=CLIENT_SECRET

注意:这样换回来的 Token 权限通常很大(比如能读取所有数据),不像授权码模式的 Token 会绑定具体用户并限制权限。

img

3. 隐藏式 (Implicit) - 💀 已废弃

这是什么? 专为没有后端的纯前端应用设计。前端直接通过 client_id 获取 Token,Token 会放在 URL 的锚点(Hash)里返回,避免向服务器发送请求。

为什么废弃? Token 直接暴露在前端,极易被窃取,安全性太差。

4. 密码式 (Password) - 💀 已废弃

这是什么? 用户直接把账号密码交给第三方应用,第三方应用拿着密码去 IDP 换 Token。 token?grant_type=password&client_id=CLIENT_ID&client_secret=CLIENT_SECRET&username=USERNAME&password=PASSWORD

为什么废弃? 相当于你把银行卡密码直接告诉了记账软件,如果应用偷偷把密码记录在日志里,风险极高。


现代追加的几种方式

随着终端设备的丰富和安全要求的提升,后来又追加了新的授权方式。

1. 设备码模式 (Device Code)

这是什么? 专为没有浏览器的设备(如 CLI 工具、智能电视、物联网设备)设计的授权方式。

它是如何工作的?

  1. 设备先访问 IDP 的 authorize/device/code,生成用户码设备码
  2. 设备在屏幕上显示用户码,提示用户用手机或电脑打开指定网页并输入该码确认登录。
  3. 同时,设备(如 CLI)在后台不断用设备码轮询 IDP:“用户登录成功了吗?”
  4. 用户在手机上确认后,CLI 轮询成功,拿到 Token。

img

2. PKCE 模式 (Proof Key for Code Exchange)

这是什么? 授权码模式的“威力加强版”,专门用来保护 CLI、手机 App、SPA 前端等无法安全存储 client_secret 的公共客户端

为什么需要它(如何防止劫持)? 如果 CLI 是个开源工具,别人很容易逆向拿到写死在代码里的 client_idclient_secret。如果不验证 Secret,恶意程序就可以监听本地端口,截获 IDP 返回的 code 并自己去换取 Token。

它是如何工作的?(动态暗号机制) PKCE 的核心是不再依赖固定的 Secret,而是每次登录临时捏造一个暗号

  1. 埋下伏笔:CLI 随机生成一个明文暗号(Code Verifier),并算出它的哈希值(Code Challenge)。CLI 带着哈希值去请求授权,让 IDP 先把哈希值记在账上。
  2. 获取 Code:用户登录成功,IDP 下发 code(假设此时被恶意程序截获)。
  3. 验证暗号:恶意程序拿着 code 去换 Token,但 IDP 要求提供明文暗号。恶意程序只有哈希值,无法反推明文,直接被拒。
  4. 成功兑换:真正的 CLI 带着 code明文暗号去请求,IDP 验证明文的哈希值与账本一致,安全发放 Token。

总结:PKCE 就像是“先给哈希值,后验明文”,即使中间的 code 被小偷截获,小偷也因为对不上暗号而无法拿到最终的 Token。