OAuth 背后的思想是,提供一种能与第三方匿名共享资源的身份验证技术。匿名资源共享可以理解为无需提供用户身份就能访问信息和资源的方式。
OAuth 为客户端提供了一种代表资源拥有者访问受保护资源的方法。在客户端访问受保护资源之前,它必须先从资源拥有者获取授权(访问许可),然后用访问许可交换访问令牌(代表许可的作用域、持续时间和其它属性)。客户端通过向资源服务器出示访问令牌来访问受保护资源。
访问令牌提供了一个抽象层,将不同的授权结构(如用户名密码、断言)替换成资源服务器可以理解的单一令牌。这种抽象使得分发短期有效的访问令牌成为可能,也使得资源服务器不必理解多种多样的授权机制。
角色
OAuth 定义了四个角色:
- 资源所有者(Resource Owner):能够授予对受保护资源的访问权限的实体,即用户。
- 资源服务器(Resource Server):服务提供商存放用户生成的资源的服务器。
- 客户端(Client):第三方应用程序。
- 授权服务器(Authorization Server):服务提供商专门用来处理授权认证的服务器。
协议流程
+--------+ +---------------+
| |--(A)- Authorization Request ->| Resource |
| | | Owner |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| Authorization |
| Client | | Server |
| |<-(D)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| Resource |
| | | Server |
| |<-(F)--- Protected Resource ---| |
+--------+ +---------------+
(A)「第三方应用程序」要求「资源所有者」给予访问其资源的授权。
(B)「资源所有者」同意授权,并向「第三方应用程序」返回一个授权码。
(C)「第三方应用程序」使用上一步获得的授权码,向「授权服务器」申请资源访问凭证(Access Token),即访问令牌。
(D)「授权服务器」对「第三方应用程序」进行权限认证,认证通过后,向「第三方应用程序」返回一个该「资源所有者」的资源访问凭证(Access Token)。
(E)「第三方应用程序」使用获得的资源访问凭证(Access Token),向「资源服务器」请求相关资源。
(F)「资源服务器」验证凭证(Access Token)无误后,将「第三方应用程序」请求的资源返回。
客户端的授权模式
客户端必须得到用户的授权(authorization grant),才能获得令牌(access token)。OAuth 2.0 定义了四种授权方式。
- 授权码模式(authorization code);
- 简化模式(implicit);
- 密码模式(resource owner password credentials);
- 客户端模式(client credentials);
授权码模式
授权码模式(authorization code)是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与 "服务提供商" 的认证服务器进行互动。
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI ---->| |
| User- | | Authorization |
| Agent -+----(B)-- User authenticates --->| Server |
| | | |
| -+----(C)-- Authorization Code ---<| |
+-|----|---+ +---------------+
| | ^ v
(A) (C) | |
| | | |
^ v | |
+---------+ | |
| |>---(D)-- Authorization Code ---------' |
| Client | & Redirection URI |
| | |
| |<---(E)----- Access Token -------------------'
+---------+ (w/ Optional Refresh Token)
(A)用户访问客户端,后者将前者导向认证服务器。
(B)用户选择是否给予客户端授权。
(C)假设用户给予授权,认证服务器将用户导向客户端事先指定的 "重定向 URI"(redirection URI),同时附上一个授权码。
(D)客户端收到授权码,附上早先的 "重定向 URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。
(E)认证服务器核对了授权码和重定向 URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。
申请客户端 ID 和密钥
当第三方网站想要获取用户在资源服务器上的资源时,通常需要提前向资源服务器登记以申请接入,获得客户端ID和密钥信息,比如我通过向 GitHub 申请开通 OAuth2.0 服务时,返回的密钥信息如下:
参数 | 内容 |
---|---|
Client ID(20位) | 9fdd83d1eb9983cbacbb |
Client Secret(40位) | e0e62d3b1813affdss7c26827f7a9f5fc665840e |
- Client ID:应用的唯一标识。
- Client Secret:应用对应的密钥,访问用户资源时用来验证应用的合法性。
授权码
- 授权码在基于重定向的授权流程中使用。一个授权码只能使用一次。
- 官方建议授权码的有效期最多设置为 10 分钟。
- 授权码和客户端 ID 、客户端重定向 URL 是一一对应关系。
客户端请求体参数:
参数 | 必要性 | 说明 |
---|---|---|
response_type | 必须 | 授权类型,此处的值固定为 code
|
client_id | 必须 | 客户端 id |
redirect_uri | 可选 | 重定向 URL |
scope | 可选 | 表示申请的权限范围 |
state | 推荐 | 表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。 |
客户端请求示例:
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com
授权服务器返回带授权码的响应体参数:
参数 | 必要性 | 说明 |
---|---|---|
code | 必须 | 授权码,用于绑定客户端重定向 URL 和客户端标识符的访问权限 |
state | 必须 | 该参数用于防跨站请求伪造(CSRF),如果该参数由客户端上传了,则服务端需要原样返回 |
授权服务器返回示例:
HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
&state=xyz
访问令牌
- 访问令牌是用于访问受保护资源的短期令牌。
- 客户端(通常是第三方服务器后台执行)需要通过授权码获取访问令牌。
客户端请求体参数:
参数 | 必要性 | 说明 |
---|---|---|
grant_type | 必须 | 表示使用的授权模式,此处的值固定为 authorization_code
|
code | 必须 | 上一步获得的授权码 |
redirect_uri | 必须 | 重定向 URL,必须与上一步中的参数值保持一致 |
client_id | 必须 | 客户端 ID |
client_secret | 必须 | 客户端密钥 |
客户端请求示例:
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
「客户端的后台服务器」请求「授权服务器」获取访问令牌成功后的响应体参数:
参数 | 必要性 | 说明 |
---|---|---|
access_token | 必须 | 授权服务器签发的访问令牌 |
token_type | 必须 | 令牌类型,告知客户端如何利用访问令牌请求用户资源,可以是 bearer 类型、mac 类型 或 JSON Web Token 类型 |
expires_in | 推荐 | 存活时间,单位为秒。如果省略该参数,必须以其他方式设置访问令牌的过期时间 |
refresh_token | 可选 | 更新令牌,表示使用相同的授权许可刷新访问令牌,以获取下一次的访问令牌 |
scope | 可选 | 可以访问的资源范围,如果与客户端申请的范围一致,此项可省略 |
服务端响应示例:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"scope":"",
"example_parameter":"example_value"
}
更新令牌
如果用户访问的时候,客户端的 "访问令牌" 已经过期,则需要使用 "更新令牌" 申请一个新的访问令牌。
客户端更新令牌请求参数:
参数 | 必要性 | 说明 |
---|---|---|
grant_type | 必须 | 表示使用的授权模式,此处的值固定为 refresh_token
|
refresh_token | 必须 | 早前收到的更新令牌 |
scope | 可选 | 表示申请的授权范围,不可以超出上一次申请的范围,如果省略该参数,则表示与上一次一致。 |
client_id | 必须 | 客户端 ID |
client_secret | 必须 | 客户端密钥 |
客户端请求示例:
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
处理错误
OAuth 规范中,为了帮助实现者查明错误原因,提供了错误代码,见 RFC-6749
错误代码 | 说明 |
---|---|
invalid_request |
参数缺失,参数值无效或参数重复 |
unauthorized_client |
恶意请求 |
access_denied |
资源属主或授权服务器拒绝请求 |
unsupported_response_type |
授权服务器不支持使用这个方法获得授权码 |
invalid_scope |
请求的范围无效、未知或损坏 |
invalid_grant |
提供的授权许可或刷新令牌无效、过期、或者客户端详情(重定向 uri、客户端自身)与授权请求中定义的不一致 |
server_error |
无法给客户端发送 500 错误时可使用内部服务器错误 |
temporarily_unavailable |
服务器目前无法处理请求。重定向无法返回 503 错误时,可以使用这个代码 |
网友评论