文章详细介绍了 OAuth 2.0 协议,这是一种开放标准授权协议,允许第三方应用在资源所有者的许可下访问其资源。文章解释了 OAuth 2.0 的基本概念、授权流程(包括授权码模式、简化模式、密码模式和客户端凭证模式),以及涉及的主要角色(资源所有者、客户端、授权服务器和资源服务器)。此外,文章还讨论了 OAuth 2.0 的安全性和实际应用场景,帮助读者理解和实现这一协议。
OAuth(开放授权)是一种开放标准,用于允许用户在不暴露其凭据(如用户名和密码)的情况下,让第三方应用程序访问其资源(如用户的照片、视频、联系人列表等)。OAuth 主要用于授权,而不是身份验证。
背景
OAuth,全称为Open Authorization(开放授权),OAuth 始于 2006 年,其设计初衷正是委托授权,就是让最终用户也就是资源拥有者,将他们在受保护资源服务器上的部分权限(例如查询当天订单)委托给第三方应用,使得第三方应用能够代表最终用户执行操作(查询当天订单)。
OAuth 1.0 协议于 2010 年 4 月作为 RFC 5849 发布,这是一份信息性的评论请求。OAuth 2.0 框架的发布考虑了从更广泛的 IETF 社区收集的其他用例和可扩展性要求。尽管基于 OAuth 1.0 部署体验构建,OAuth 2.0 并不向后兼容 OAuth 1.0。OAuth 2.0 于 2012 年 10 月作为 RFC 6749 发布,承载令牌使用作为 RFC 6750 发布。
在 OAuth 协议中,通过为每个第三方软件和每个用户的组合分别生成对受保护资源具有受限的访问权限的凭据,也就是访问令牌,来代替之前的用户名和密码。而生成访问令牌之前的登录操作,又是在用户跟平台之间进行的,第三方软件根本无从得知用户的任何信息。
这样第三方软件的逻辑处理就大大简化了,它今后的动作就变成了请求访问令牌、使用访问令牌、访问受保护资源,同时在第三方软件调用大量 API 的时候,不再传输用户名和密码,从而减少了网络安全的攻击面。
说白了就是集中授权。
值得注意的是,OAuth 并非身份验证,这里的 Auth 是 Authorization,OAuth 是发生在用户做了身份验证后的事情,系统授权用户能做什么操作。互联网中所有的受保护资源,几乎都是以 Web API 的形式来提供访问的。不同的用户能做的事情不同,例如一个 GitHub 项目,有些用户只有读取和提交 PR(pull request)的权限,而管理员用户则能合并 PR。将用户权限在 API 层面细分,是 OAuth 要做的事情。
基本概念
- 资源所有者(Resource Owner):
- 通常是用户,拥有受保护资源的实体。
- 客户端(Client):
- 想要访问资源所有者资源的第三方应用程序。
- 资源服务器(Resource Server):
- 存储资源所有者资源的服务器,能够处理访问令牌并返回资源。
- 授权服务器(Authorization Server):
- 负责验证资源所有者的身份并颁发访问令牌的服务器。
- 访问令牌(Access Token):
- 授权服务器颁发的凭证,客户端使用它来访问资源服务器上的受保护资源。
工作流程
以下是 OAuth 2.0 的典型工作流程:

- 客户端请求授权:
- 客户端向资源所有者请求授权访问其资源。通常通过一个授权请求 URL 来实现。
- 资源所有者授权:
- 资源所有者同意或拒绝客户端的授权请求。如果同意,授权服务器会颁发一个授权码(Authorization Code)。
- 客户端获取访问令牌:
- 客户端使用授权码向授权服务器请求访问令牌。授权服务器验证授权码并颁发访问令牌。
- 客户端访问资源:
- 客户端使用访问令牌向资源服务器请求受保护资源。资源服务器验证访问令牌并返回资源。
OAuth 2.0 授权类型
OAuth 2.0 定义了几种不同的授权类型(Grant Types),以适应不同的使用场景:
- 授权码授权(Authorization Code Grant):
- 最常用的授权类型,适用于服务器端应用程序。客户端首先获取授权码,然后使用授权码获取访问令牌。
- 隐式授权(Implicit Grant):
- 适用于单页应用程序(SPA)或其他客户端应用程序。客户端直接获取访问令牌,而不需要授权码。
- 资源所有者密码凭证授权(Resource Owner Password Credentials Grant):
- 适用于高度信任的客户端应用程序。客户端直接使用资源所有者的用户名和密码获取访问令牌。
- 客户端凭证授权(Client Credentials Grant):
- 适用于服务器到服务器的通信。客户端使用自己的凭证(如客户端 ID 和客户端密钥)获取访问令牌。
OAuth 的优点
- 安全性:
- 用户不需要向第三方应用程序提供用户名和密码,减少了凭据泄露的风险。
- 灵活性:
- 支持多种授权类型,适应不同的使用场景。
- 标准化:
- 是一个开放标准,广泛应用于各种互联网服务和应用程序。
应用场景
- 社交登录:
- 用户可以使用其社交媒体账户(如 Google、Facebook、Twitter)登录第三方应用程序。
- API 访问:
- 第三方应用程序可以使用 OAuth 获取访问令牌,以访问用户在其他服务上的资源(如 Google Drive、Dropbox)。
- 单点登录(SSO):
- 用户可以使用一个账户登录多个不同的应用程序或服务。
示例一:使用 OAuth 2.0 授权码授权
以下是一个使用 OAuth 2.0 授权码授权的简单示例:
- 客户端请求授权码:
- 客户端重定向用户到授权服务器的授权端点:
GET /authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=read
- 资源所有者授权:
- 用户在授权服务器上登录并同意授权请求。授权服务器重定向用户回到客户端,并附带授权码:
GET /callback?code=AUTHORIZATION_CODE
- 客户端获取访问令牌:
- 客户端使用授权码向授权服务器请求访问令牌:
POST /token Content-Type: application/x-www-form-urlencoded grant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=REDIRECT_URI&client_id=CLIENT_ID&client_secret=CLIENT_SECRET
- 授权服务器返回访问令牌:
- 授权服务器验证授权码并返回访问令牌:
{ "access_token": "ACCESS_TOKEN", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "REFRESH_TOKEN" }
- 客户端使用访问令牌访问资源:
- 客户端使用访问令牌向资源服务器请求受保护资源:
GET /resource Authorization: Bearer ACCESS_TOKEN
示例二:Github OAuth2.0 授权接入
先到 https://github.com/settings/applications/new 创建一个 OAuth application,获取 client_id 和 client_secret

一、请求用户的 GitHub 标识
https://github.com/login/oauth/authorize?client_id=

点击授权后,会跳转到回调的地址,url 后面会带上 code 参数,code 的有效期是 10 分钟
二、通过上面的 code 获取 token
POST https://github.com/login/oauth/access_token
参数名称 类型 说明
client_id string 必填。 从 GitHub 收到的 OAuth app 的客户端 ID。
client_secret string 必填。 从 GitHub 收到的 OAuth app 的客户端密码。
code string 必填。 收到的作为对步骤 1 的响应的代码。
redirect_uri string 用户获得授权后被发送到的应用程序中的 URL。
返回 access_token
三、通过上面获取到的 token 访问 API
curl -H "Authorization: Bearer OAUTH-TOKEN" https://api.github.com/user
官方文档:https://docs.github.com/zh/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps
结论
OAuth 是一种强大的授权机制,广泛应用于各种互联网服务和应用程序。通过 OAuth,用户可以安全地授权第三方应用程序访问其资源,而无需暴露其凭据。了解和掌握 OAuth 的工作原理和应用场景,对于开发现代互联网应用程序非常重要。
其他
为什么授权码和访问口令要分开获取呢?
OAuth2 协议中,用户登录成功后,OAuth2 认证服务器会将用户的浏览器回调到一个回调地址,并携带一个授权码 code
。这个授权码 code
一般有效期十分钟且一次有效,用后作废。这避免了在前端暴露 access_token
或者用户信息的风险,access_token
的有效期都比较长,一般为 1~2 个小时。如果泄露会对用户造成一定影响。后端收到这个 code
之后,需要使用 Client Id + Client Secret + Code
去授权服务器换取用户的 access_token
。
在这一步,实际上授权服务器对第三方应用进行了认证,能够确保来授权服务器获取 access_token
的机器是可信任的,而不是任何一个人拿到 code
之后都能来授权服务器进行 code
换 token
。如果 code
被黑客获取到,如果他没有 Client Id + Client Secret
也无法使用,就算有,也要和真正的应用服务器竞争,因为 code
一次有效,用后作废,加大了攻击难度。相反,如果不经过 code
直接返回 access_token
或用户信息,那么一旦泄露就会对用户造成影响。
简单说就是,client secret 不能暴露给前端(验证 client),用户授权(获取 code)又只能前端做,因此需要分两步。
OAuth 2.0 规范
OAuth 2.0 Authorization Framework
API 请求中,访问令牌放在哪里比较合适
访问令牌(Access Token)是用于身份验证和授权的重要信息。将访问令牌放置在合适的位置对于确保安全性和有效性至关重要。以下是一些常见的做法:
HTTP Authorization Header
最常见和推荐的方式是将访问令牌放在 HTTP 请求的 Authorization
头中,而且必须配置 HTTPS,这种方式符合 OAuth 2.0 的标准做法,具有以下优点:
- 安全性:将令牌放在头部而不是 URL 中,可以避免令牌在浏览器历史记录、服务器日志等地方被泄露。
- 清晰性:使用
Authorization
头可以清晰地表明请求的身份验证方式。
GET /api/protected-resource HTTP/1.1
Host: api.example.com
Authorization: Bearer YOUR_ACCESS_TOKEN
URL 查询参数
不推荐这种做法
Cookie
将访问令牌存储在 HTTP Cookie 中也是一种可行的方式,尤其是在 Web 应用中。使用 Cookie 的优点包括:
- 自动发送:浏览器会自动将 Cookie 附加到请求中,无需手动管理。
- 安全性:可以设置 Cookie 的属性(如
HttpOnly
和Secure
)来增强安全性。
注意事项:
- 确保使用
Secure
属性,以便仅在 HTTPS 连接中发送 Cookie。 - 使用
HttpOnly
属性可以防止 JavaScript 访问 Cookie,从而降低 XSS 攻击的风险。
Set-Cookie: access_token=YOUR_ACCESS_TOKEN; HttpOnly; Secure
Local Storage / Session Storage
在前端应用(如单页应用)中,可以将访问令牌存储在 localStorage
或 sessionStorage
中。这种方式的优缺点如下:
- 优点:
- 易于访问和管理。
- 可以在多个请求中使用。
- 缺点:
- 容易受到 XSS 攻击,攻击者可以通过恶意脚本访问存储的令牌。