OAuth 2.0 介绍
什么是 OAuth 2.0
OAuth 2.0 是一个行业标准的授权协议,允许用户授权第三方应用访问他们存储在其他服务提供者上的信息,而无需将用户名和密码提供给第三方应用。
简单来说,OAuth 2.0 让用户能够安全地授予应用程序有限的访问权限,而不需要共享密码。
核心概念
角色定义
OAuth 2.0 定义了四个角色:
- 资源所有者(Resource Owner):通常是最终用户,能够授予对受保护资源的访问权限
- 客户端(Client):代表资源所有者请求访问受保护资源的应用程序
- 资源服务器(Resource Server):托管受保护资源的服务器,能够接受并响应使用访问令牌的请求
- 授权服务器(Authorization Server):在成功认证资源所有者并获得授权后,向客户端颁发访问令牌的服务器
令牌类型
- 访问令牌(Access Token):用于访问受保护资源的凭证
- 刷新令牌(Refresh Token):用于获取新的访问令牌的凭证
授权流程
授权码模式(Authorization Code)
这是最常用且最安全的授权方式,适用于有后端服务器的应用。
mermaid
sequenceDiagram
participant User as 用户
participant Client as 客户端应用
participant AuthServer as 授权服务器
participant ResourceServer as 资源服务器
User->>Client: 1. 访问应用
Client->>AuthServer: 2. 请求授权
AuthServer->>User: 3. 显示授权页面
User->>AuthServer: 4. 同意授权
AuthServer->>Client: 5. 返回授权码
Client->>AuthServer: 6. 使用授权码请求令牌
AuthServer->>Client: 7. 返回访问令牌
Client->>ResourceServer: 8. 使用令牌访问资源
ResourceServer->>Client: 9. 返回受保护资源流程步骤:
- 用户访问客户端应用
- 客户端将用户重定向到授权服务器
- 用户在授权服务器上登录并授权
- 授权服务器将用户重定向回客户端,并附带授权码
- 客户端使用授权码向授权服务器请求访问令牌
- 授权服务器验证授权码并返回访问令牌
- 客户端使用访问令牌访问资源服务器
隐式授权模式(Implicit)
适用于纯前端应用,令牌直接在浏览器中获取。
安全提示
此模式已被认为不够安全,现代应用推荐使用带 PKCE 的授权码模式。
密码模式(Resource Owner Password Credentials)
用户直接向客户端提供用户名和密码。
不推荐使用
此模式违背了 OAuth 2.0 的初衷,仅在高度信任的应用中使用。
客户端凭证模式(Client Credentials)
适用于机器对机器的通信,不涉及用户授权。
安全最佳实践
使用 PKCE
PKCE(Proof Key for Code Exchange)是授权码模式的扩展,为移动应用和单页应用提供额外的安全保护。
javascript
// 生成 code_verifier
const codeVerifier = generateRandomString(128);
// 生成 code_challenge
const codeChallenge = base64UrlEncode(sha256(codeVerifier));
// 在授权请求中包含 code_challenge
const authUrl = `${authorizationEndpoint}?
response_type=code&
client_id=${clientId}&
redirect_uri=${redirectUri}&
code_challenge=${codeChallenge}&
code_challenge_method=S256`;令牌存储
- 访问令牌:不要存储在 localStorage,推荐使用内存或 sessionStorage
- 刷新令牌:应当安全存储,并通过 HTTPS 传输
- 生产环境:考虑使用 HttpOnly Cookie 存储敏感令牌
其他建议
- 始终使用 HTTPS
- 验证重定向 URI
- 使用 state 参数防止 CSRF 攻击
- 限制令牌的作用域(scope)
- 设置合理的令牌过期时间
- 实施令牌撤销机制
实际应用示例
使用第三方登录
典型的场景是"使用 Google 登录"或"使用 GitHub 登录":
javascript
// 构建授权 URL
const params = new URLSearchParams({
client_id: 'your_client_id',
redirect_uri: 'https://yourapp.com/callback',
response_type: 'code',
scope: 'user:email',
state: 'random_state_string'
});
window.location.href = `https://github.com/login/oauth/authorize?${params}`;处理回调
javascript
// 在回调页面处理授权码
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
const state = urlParams.get('state');
// 验证 state 并交换令牌
if (state === savedState) {
const response = await fetch('/api/oauth/token', {
method: 'POST',
body: JSON.stringify({ code })
});
const { access_token } = await response.json();
// 使用访问令牌
}OAuth 2.0 vs OAuth 1.0
| 特性 | OAuth 1.0 | OAuth 2.0 |
|---|---|---|
| 签名机制 | 需要签名 | 依赖 HTTPS |
| 令牌类型 | 单一令牌 | 访问令牌 + 刷新令牌 |
| 移动端支持 | 较差 | 良好 |
| 复杂度 | 复杂 | 相对简单 |
| 安全性 | 较好 | 依赖 HTTPS |
常见问题
访问令牌过期怎么办?
使用刷新令牌获取新的访问令牌,无需用户重新授权。
javascript
async function refreshAccessToken(refreshToken) {
const response = await fetch('https://oauth.provider.com/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
grant_type: 'refresh_token',
refresh_token: refreshToken,
client_id: 'your_client_id',
client_secret: 'your_client_secret'
})
});
return await response.json();
}如何撤销令牌?
大多数 OAuth 提供商支持令牌撤销端点:
javascript
await fetch('https://oauth.provider.com/revoke', {
method: 'POST',
body: JSON.stringify({
token: accessToken,
token_type_hint: 'access_token'
})
});参考资源
总结
OAuth 2.0 是现代应用授权的基石,它提供了灵活的授权方式,同时保护了用户的安全。选择合适的授权流程并遵循安全最佳实践,可以为您的应用构建安全可靠的授权系统。
提示
在实施 OAuth 2.0 之前,建议仔细阅读官方规范并了解您所使用的 OAuth 提供商的具体实现细节。
