授权码(authorization code)方式,指的是第三方应用先申请一个授权码,然后再用该码获取令牌。
有些 Web 应用是纯前端应用,没有后端。这时就不能用上面的方式了,必须将令牌储存在前端。RFC 6749 就规定了第二种方式,允许直接向前端颁发令牌。这种方式没有授权码这个中间步骤,所以称为(授权码)“隐藏式”(implicit)。
如果你高度信任某个应用,RFC 6749 也允许用户把用户名和密码,直接告诉该应用。该应用就使用你的密码,申请令牌,这种方式称为"密码式"(password)。
最后一种方式是凭证式(client credentials),适用于没有前端的命令行应用,即在命令行下请求令牌。
授权码模式(authorization code)是功能最完整、流程最严密安全的授权模式。它的特点就是通过客户端的后台服务器,与"服务提供商"的认证服务器进行互动。
注意这种方式适用于那些有后端的 Web 应用。授权码通过前端传送,令牌则是储存在后端,而且所有与资源服务器的通信都在后端完成。
微信公众号授权流程即采用这种方式。
2.1 授权码模式流程如下:
1)用户访问客户端,客户端将用户导向认证服务器。
2)用户选择是否给予客户端授权。
3)假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码(每个用户的授权码不同)。
4)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的 后台服务器 上完成的,对用户不可见。
5)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。
从上述的流程描述可知,只有第 2 步需要用户进行授权操作,之后的流程都是在客户端的后台和认证服务器后台之前进行"静默"操作,对于用户来说是无感知的。
下面是上面这些步骤所需要的参数。
第 1 步骤中,客户端申请认证的URI,包含以下参数:
response_type
:表示授权类型,必选项,此处的值固定为"code"client_id
:表示客户端的ID,必选项redirect_uri
:表示重定向URI,可选项scope
:表示申请的权限范围,可选项state
:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。示例
A 网站提供一个链接,用户点击后就会跳转到 B 网站,授权用户数据给 A 网站使用。下面就是 A 网站跳转 B 网站的一个示意链接:
https://b.com/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=CALLBACK_URL&scope=read
上面 URL 中:
response_type
参数表示要求返回授权码(code
);
client_id
参数让 B 网站知道是谁在请求;
redirect_uri
参数是 B 网站接受或拒绝请求后的跳转网址;
scope
参数表示要求的授权范围(这里是只读)。
第 3 步骤中,服务器回应客户端的URI,包含以下参数:
code
:表示授权码,必选项。该码的有效期应该很短,通常设为10分钟,客户端只能使用该码一次,否则会被授权服务器拒绝。该码与客户端ID和重定向URI,是一一对应关系。state
:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。示例
在第 2 步骤用户表示同意之后,这时 B 网站就会跳回redirect_uri
参数指定的网址。跳转时,会传回一个授权码,就像下面这样。
https://a.com/callback?code=AUTHORIZATION_CODE
上面 URL 中,code
参数就是授权码。
第 4 步骤中,客户端向认证服务器申请令牌的HTTP请求,包含以下参数:
grant_type
:表示使用的授权模式,必选项,此处的值固定为"authorization_code"。code
:表示上一步获得的授权码,必选项。redirect_uri
:表示重定向URI,必选项,且必须与A步骤中的该参数值保持一致。client_id
:表示客户端ID,必选项。示例
在第 3 步骤中,A 网站拿到授权码以后,就可以在后端,向 B 网站请求令牌。
https://b.com/oauth/token?
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET&
grant_type=authorization_code&
code=AUTHORIZATION_CODE&
redirect_uri=CALLBACK_URL
上面 URL 中:
client_id
参数和client_secret
参数用来让 B 确认 A 的身份(client_secret
参数是保密的,因此只能在后端发请求);
grant_type
参数的值是AUTHORIZATION_CODE
,表示采用的授权方式是授权码;
code
参数是上一步拿到的授权码;
redirect_uri
参数是令牌颁发后的回调网址。
第 5 步骤中,认证服务器发送的HTTP回复,包含以下参数:
access_token
:表示访问令牌,必选项。token_type
:表示令牌类型,该值大小写不敏感,必选项,可以是bearer类型或mac类型。expires_in
:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。refresh_token
:表示更新令牌,用来获取下一次的访问令牌,可选项。scope
:表示权限范围,如果与客户端申请的范围一致,此项可省略。h
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":{...}
}
后续
当客户端(第三方应用程序)拿到访问资源服务器的令牌时,便可以使用这个令牌进行资源访问了。例如:拿到了access_token 和user_id (id_token之类的)请求用户详细数据
如何发送给资源服务器这个问题并没有在 RFC6729 文件中定义,而是作为一个单独的 RFC6750 文件中独立定义了。这里做以下简单的介绍,主要有三种方式如下:
Authorization Request Header Field,因为在HTTP应用层协议中,专门有定义一个授权使用的Request Header,所以也可以使用这种方式:
GET /resource HTTP/1.1
Host: server.example.com
Authorization: Bearer mF_9.B5f-4.1JqM
其中"Bearer "是固定的在access_token前面的头部信息。
令牌的刷新
为了防止客户端使用一个令牌无限次数使用,令牌一般会有过期时间限制,当快要到期时,需要重新获取令牌,如果再重新走授权码的授权流程,对用户体验非常不好,于是 OAuth 2.0 允许用户自动更新令牌。
具体方法是,B 网站颁发令牌的时候,一次性颁发两个令牌,一个用于获取数据,另一个用于获取新的令牌(refresh token 字段)。令牌到期前,用户使用 refresh token 发一个请求,去更新令牌。
2.2 隐藏式
第一步,A 网站提供一个链接,要求用户跳转到 B 网站,授权用户数据给 A 网站使用。
https://b.com/oauth/authorize?
response_type=token&
client_id=CLIENT_ID&
redirect_uri=CALLBACK_URL&
scope=read
上面 URL 中,response_type
参数为token
,表示要求直接返回令牌。
第二步,用户跳转到 B 网站,登录后同意给予 A 网站授权。这时,B 网站就会跳回redirect_uri
参数指定的跳转网址,并且把令牌作为 URL 参数,传给 A 网站。
https://a.com/callback#token=ACCESS_TOKEN
上面 URL 中,token
参数就是令牌,A 网站因此直接在前端拿到令牌。
注意,令牌的位置是 URL 锚点(fragment),而不是查询字符串(querystring),这是因为 OAuth 2.0 允许跳转网址是 HTTP 协议,因此存在"中间人攻击"的风险,而浏览器跳转时,锚点不会发到服务器,就减少了泄漏令牌的风险。
本文链接:https://blog.nnwk.net/article/1559
有问题请留言。版权所有,转载请在显眼位置处保留文章出处,并留下原文连接
Leave your question and I'll get back to you as soon as I see it. All rights reserved. Please keep the source and links
友情链接:
子卿全栈
全部评论