The OAuth 2.0 authorization framework enables a third-party
application to obtain limited access to an HTTP service, either on
behalf of a resource owner by orchestrating an approval interaction
between the resource owner and the HTTP service, or by allowing the
third-party application to obtain access on its own behalf. This
specification replaces and obsoletes the OAuth 1.0 protocol described
in RFC 5849.
更多可以访问:https://tools.ietf.org/html/rfc6749
主要用于第三方登录的场景。比如A平台上的用户U要去B平台上读取该用户的一些信息,这时候A平台需要获取用户允许读取B平台上信息的授权。如果直接让用户输入 B平台的登录用户名和密码,那么A平台为了用户后续的操作,会保存用户的密码,而且当像A平台这样的第三方平台比较多的时候,这种操作就变得相当不安全。这就是 OAuth该发挥作用的场景了。
+--------+ +---------------+
| |--(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 ---| |
+--------+ +---------------+
Figure 1: Abstract Protocol Flow
这种方式是最常用的流程,安全性也最高,它适用于那些有后端的 Web 应用。授权码通过前端传送,令牌则是储存在后端,而且所有与资源服务器的通信都在后端完成。这样的前后端分离,可以避免令牌泄漏。
https://graph.qq.com/oauth2.0/show?which=Login&display=pc&
response_type=code&client_id=101484447
&redirect_uri=https%3A%2F%2Fna.abc.cn%2Fsign%2Foauth%2Fcallback
&scope=get_user_info&state=state
上面B网站为腾讯qq,做过三方登录的应该都会比较熟悉。 url里面的response_type参数为code表示要求返回授权码,client_id是标识第三方应用的,redirect_uri参数是 B 接受或拒绝请求后的跳转网址,scope参数表示要求的授权范围
https://na.abc.cn/sign/oauth/callback?code=4443C02228E47694BEDA&state=state
上面这个code就是授权码,一般这个回调地址对应的是A平台后台服务的接口。
https://b.com/oauth/token?
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET&
grant_type=authorization_code&
code=AUTHORIZATION_CODE&
redirect_uri=CALLBACK_URL
注:qq登录时在这里是没有redirect_uri这一项的,它是直接在响应中返回令牌信息的。然后在后台可以通过这个access token去qq请求用户信息,union id和open id等。
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"ACCESS_TOKEN",
"token_type":"bearer",
"expires_in":2592000,
"refresh_token":"REFRESH_TOKEN",
"scope":"read",
"uid":100101,
"info":{...}
}
一般对应的是A 网站的后台接口。注意:HTTP头信息中明确指定不得缓存。
RFC 6749 就规定了第二种方式,允许直接向前端颁发令牌。这种方式没有授权码这个中间步骤,所以称为(授权码)"隐藏式"(implicit)。
The implicit grant is a simplified authorization code flow optimized
for clients implemented in a browser using a scripting language such
as JavaScript. In the implicit flow, instead of issuing the client
an authorization code, the client is issued an access token directly
(as the result of the resource owner authorization). The grant type
is implicit, as no intermediate credentials (such as an authorization
code) are issued (and later used to obtain an access token).
A网站请求:
https://b.com/oauth/authorize?
response_type=token&
client_id=CLIENT_ID&
redirect_uri=CALLBACK_URL&
scope=read
B网站会跳转到redirect_url并带上令牌:
https://a.com/callback#token=ACCESS_TOKEN
注意,令牌的位置是 URL 锚点(fragment),而不是查询字符串(querystring),这是因为 OAuth 2.0 允许跳转网址是 HTTP 协议,因此存在"中间人攻击"的风险,而浏览器跳转时,锚点不会发到服务器,就减少了泄漏令牌的风险。 这种方式把令牌直接传给前端,是很不安全的。因此,只能用于一些安全要求不高的场景,并且令牌的有效期必须非常短,通常就是会话期间(session)有效,浏览器关掉,令牌就失效了。
The resource owner password credentials (i.e., username and password) can be used directly as an authorization grant to obtain an access token. The credentials should only be used when there is a high degree of trust between the resource owner and the client (e.g., the client is part of the device operating system or a highly privileged application), and when other authorization grant types are not available (such as an authorization code).
A网站请求:
https://oauth.b.com/token?
grant_type=password&
username=USERNAME&
password=PASSWORD&
client_id=CLIENT_ID
B网站直接在响应中获取到令牌。
最后一种方式是凭证式(client credentials),适用于没有前端的命令行应用,即在命令行下请求令牌。 A网站请求:
https://oauth.b.com/token?
grant_type=client_credentials&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET
grant_type参数等于client_credentials表示采用凭证式,client_id和client_secret用来让 B 确认 A 的身份。B网站验证通过后,直接返回令牌。这种方式给出的令牌,是针对第三方应用的,而不是针对用户的,即有可能多个用户共享同一个令牌。
A网站拿到令牌后,就可以携带令牌通过B网站的api接口向B网站的API请求数据了。具体做法是在请求的头信息,加上一个Authorization字段,令牌就放在这个字段里面。ACCESS_TOKEN就是拿到的令牌。
curl -H "Authorization: Bearer ACCESS_TOKEN" \
"https://api.b.com"
用于更新accessToken的,令牌的有效期到了,如果让用户重新走一遍上面的流程,再申请一个新的令牌,很可能体验不好,而且也没有必要。OAuth 2.0 允许用户自动更新令牌。 具体方法是,B 网站颁发令牌的时候,一次性颁发两个令牌,一个用于获取数据,另一个用于获取新的令牌(refresh token 字段)。令牌到期前,用户使用 refresh token 发一个请求,去更新令牌。
https://b.com/oauth/token?
grant_type=refresh_token&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET&
refresh_token=REFRESH_TOKEN
grant_type参数为refresh_token表示要求更新令牌,client_id参数和client_secret参数用于确认身份,refresh_token参数就是用于更新令牌的令牌。B 网站验证通过以后,就会颁发新的令牌。
更新令牌流程图:
+--------+ +---------------+
| |--(A)------- Authorization Grant --------->| |
| | | |
| |<-(B)----------- Access Token -------------| |
| | & Refresh Token | |
| | | |
| | +----------+ | |
| |--(C)---- Access Token ---->| | | |
| | | | | |
| |<-(D)- Protected Resource --| Resource | | Authorization |
| Client | | Server | | Server |
| |--(E)---- Access Token ---->| | | |
| | | | | |
| |<-(F)- Invalid Token Error -| | | |
| | +----------+ | |
| | | |
| |--(G)----------- Refresh Token ----------->| |
| | | |
| |<-(H)----------- Access Token -------------| |
+--------+ & Optional Refresh Token +---------------+
Figure 2: Refreshing an Expired Access Token
参考链接: