OAuth2四种授权流程:哪种适合你的App?

引言:选择困难症的解药
OAuth2有四种授权流程,每种都有不同的适用场景。
如果你是开发者,可能会纠结:我的App该用哪种流程?
今天,我用最白话的方式,给你讲明白这四种流程的区别。
第一章:授权码流程(最常用)
一句话解释
就像去银行取钱:先拿号,再取钱。
流程比喻
想象你去银行取钱:
- 你:我要取钱
- 银行:好的,给你一个号(授权码)
- 你:拿着号去柜台
- 银行:验证号,给你钱(访问令牌)
技术流程
1 2 3 4 5 6 7 8 9 10 11
| 用户 → 点击"微信登录" ↓ App → 跳转到微信授权页面 ↓ 微信 → 显示授权页面,用户确认 ↓ 微信 → 返回授权码给App ↓ App → 用授权码换访问令牌 ↓ App → 用访问令牌获取用户信息
|
适用场景
- Web应用:有后端服务器
- 移动App:原生应用
- 单页应用:需要安全存储令牌
安全优势
- 令牌不经过浏览器
- 支持刷新令牌
- 可以验证客户端身份
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| auth_url = "https://wechat.com/oauth2/authorize?" + \ "client_id=YOUR_CLIENT_ID&" + \ "redirect_uri=https://your-app.com/callback&" + \ "response_type=code&" + \ "scope=snsapi_userinfo"
code = request.args.get('code')
token_url = "https://wechat.com/oauth2/token" data = { 'grant_type': 'authorization_code', 'code': code, 'client_id': 'YOUR_CLIENT_ID', 'client_secret': 'YOUR_CLIENT_SECRET', 'redirect_uri': 'https://your-app.com/callback' } response = requests.post(token_url, data=data) access_token = response.json()['access_token']
|
第二章:隐式流程(已废弃)
一句话解释
就像快餐店点餐:直接给你餐,不用排队。
流程比喻
想象你去快餐店:
- 你:我要一个汉堡
- 快餐店:好的,直接给你汉堡(访问令牌)
- 你:拿着汉堡走人
技术流程
1 2 3 4 5 6 7
| 用户 → 点击"登录" ↓ App → 跳转到授权页面 ↓ 授权服务器 → 直接返回访问令牌(在URL中) ↓ App → 从URL中提取令牌
|
为什么废弃?
安全问题:
- 令牌暴露在URL中
- 可能被浏览器历史记录
- 可能被中间人攻击
替代方案
现在推荐使用授权码流程 + PKCE:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| code_verifier = generate_random_string(128) code_challenge = base64url_encode(sha256(code_verifier))
auth_url = "https://example.com/oauth2/authorize?" + \ "code_challenge=" + code_challenge + "&" + \ "code_challenge_method=S256"
data = { 'code_verifier': code_verifier, 'code': code, ... }
|
第三章:客户端凭证流程
一句话解释
就像公司内部调用:员工之间直接沟通,不用经过前台。
流程比喻
想象公司内部:
- 员工A:我需要销售数据
- 员工B:好的,给你(访问令牌)
- 员工A:拿着令牌去数据库
技术流程
1 2 3 4 5
| 客户端 → 直接向授权服务器请求令牌 ↓ 授权服务器 → 验证客户端身份,返回令牌 ↓ 客户端 → 用令牌访问资源
|
适用场景
- 服务间通信:微服务架构
- 后台任务:定时任务
- 机器对机器:没有用户参与
代码示例
1 2 3 4 5 6 7 8 9 10
| token_url = "https://api.example.com/oauth2/token" data = { 'grant_type': 'client_credentials', 'client_id': 'YOUR_CLIENT_ID', 'client_secret': 'YOUR_CLIENT_SECRET', 'scope': 'read write' } response = requests.post(token_url, data=data) access_token = response.json()['access_token']
|
安全考量
- 客户端密钥必须安全存储
- 只用于服务间通信
- 不要暴露给前端
第四章:设备授权流程
一句话解释
就像电视扫码登录:电视显示二维码,你用手机扫码确认。
流程比喻
想象你用电视登录:
- 电视:显示二维码
- 你:用手机扫码
- 手机:确认授权
- 电视:获得访问令牌
技术流程
1 2 3 4 5 6 7 8 9
| 设备 → 请求设备码 ↓ 授权服务器 → 返回设备码和用户码 ↓ 设备 → 显示二维码(包含用户码) ↓ 用户 → 用手机扫码,确认授权 ↓ 设备 → 轮询授权服务器,获取令牌
|
适用场景
- 智能电视:没有键盘
- 游戏机:输入不便
- IoT设备:屏幕小
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| device_code_url = "https://example.com/oauth2/device/code" data = { 'client_id': 'YOUR_CLIENT_ID', 'scope': 'read write' } response = requests.post(device_code_url, data=data) device_code = response.json()['device_code'] user_code = response.json()['user_code'] verification_uri = response.json()['verification_uri']
print(f"请访问 {verification_uri} 并输入 {user_code}")
while True: token_url = "https://example.com/oauth2/token" data = { 'grant_type': 'urn:ietf:params:oauth:grant-type:device_code', 'device_code': device_code, 'client_id': 'YOUR_CLIENT_ID' } response = requests.post(token_url, data=data) if response.status_code == 200: access_token = response.json()['access_token'] break time.sleep(5)
|
第五章:如何选择?
选择指南
| 场景 |
推荐流程 |
原因 |
| Web应用(有后端) |
授权码流程 |
最安全 |
| 单页应用(SPA) |
授权码+PKCE |
安全且简单 |
| 移动App |
授权码+PKCE |
安全且原生支持 |
| 服务间通信 |
客户端凭证 |
无用户参与 |
| 智能设备 |
设备授权 |
输入不便 |
| 遗留系统 |
隐式流程 |
已废弃,不推荐 |
决策树
1 2 3 4 5
| 你的App有用户参与吗? ├── 是 → 用户能方便输入吗? │ ├── 是 → 授权码流程 │ └── 否 → 设备授权流程 └── 否 → 客户端凭证流程
|
第六章:实战建议
开发者清单
常见错误
- 使用隐式流程:已废弃,不要用
- 不验证重定向URI:可能导致令牌泄露
- 令牌存储不安全:可能被窃取
- 不处理令牌过期:用户体验差
总结
核心要点
- 授权码流程:最常用,最安全
- 隐式流程:已废弃,不要用
- 客户端凭证:服务间通信
- 设备授权:输入不便的设备
选择建议
- 大多数场景:使用授权码流程
- 单页应用:授权码 + PKCE
- 服务间通信:客户端凭证
- 智能设备:设备授权
给开发者的建议
- 不要自己发明轮子:使用成熟的OAuth2库
- 遵循最佳实践:参考RFC和官方文档
- 安全第一:令牌存储和传输必须安全
- 测试充分:各种场景都要测试
选择正确的OAuth2流程,就像选择正确的交通工具:选对了,事半功倍。