Skip to content

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. 返回受保护资源

流程步骤:

  1. 用户访问客户端应用
  2. 客户端将用户重定向到授权服务器
  3. 用户在授权服务器上登录并授权
  4. 授权服务器将用户重定向回客户端,并附带授权码
  5. 客户端使用授权码向授权服务器请求访问令牌
  6. 授权服务器验证授权码并返回访问令牌
  7. 客户端使用访问令牌访问资源服务器

隐式授权模式(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.0OAuth 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 提供商的具体实现细节。

Released under the MIT License.