3、 resource owner password credentials(密码模式)
一、什么是OAuth
所谓的OAuth(Open Authorization)本质上就是一张开放的协议,OAuth协议为用户资源的授权提供了一个安全的、开放而又简易的标准。 与以往的授权方式不同该授权不会使第三方触及到用户的账户信息,即第三方无需使用用户名密码就可以申请该用户资源的授权,因此OAuth是安全的。这是来自百度百科对OAuth的解释。
而OAuth2.0呢,故名思义就是OAuth的下一个版本,同时它也是授权领域的行业标准协议,OAuth2.0不支持向后兼容,即不支持OAuth1.0,彻底废止了OAuth1.0协议,OAuth2.0致力于使客户端开发者通过更简单的流程为Web应用、桌面应用以及手机客户端等设备进行授权。2012年10月,OAuth2.0协议正式发布为RFC6749。目前各大开放平台,如腾讯、新浪、百度等等也都是以OAuth2.0协议作为支撑。
二、什么场景下会用到OAuth授权
目前我们大多数的应用场景,也是我们最熟悉的使用环境就是使用第三方登录,如:微博,QQ,微信,豆瓣等社交平台登录,互联网发展到今天,我们也都习惯了这种登录方式,最大的好处就是便捷,简化了我们一系列的注册流程,还有一大堆帐号密码记不住的困扰,当你碰到一个不支持第三方登录的网站或者应用的时候,第一感觉就是厌恶,一顿喷。人永远离不开社交,所以通过社交平台去登录其他的平台,无疑是最受大家欢迎的方式了。
就拿CSDN为例,CSDN是支持第三方平台登录的,截图如下:
若用户想使用QQ登录,那么在传统的认证模式中,客户端(CSDN)请求访问服务器上受限的资源(QQ),需要通过资源所有者(用户的QQ账户)的凭证在服务器上进行认证。为了支持第三方应用程序(对于QQ来说,CSDN是第三方应用程序,是CSDN去请求访问QQ的资源)访问受限资源,资源所有者需要向第三方应用共享其凭证(如用户QQ的帐号和密码)。那这样就会造成以下几个问题:
-
第三方应用(CSDN)为了以后继续使用,那么会存储资源所有者的凭证,如密码;
-
服务端需要支持密码认证,尽管密码认证不安全;
-
第三方应用若想访问资源所有者的其他资源,资源所有者无法对其进行限制;
-
资源所有者无法收回第三方的访问权限,除非用户主动修改密码;
-
如果此时第三方的数据泄漏,那也会导致资源所有者其他数据的泄漏,造成重大损失。
若此时,CSDN知道了用户的QQ密码,那CSDN可以任意去访问该用户的所有信息,而QQ无法去限制它的访问,这不管是对QQ还是用户本身都会是一件可怕的事,作为使用者,你永远都无法知道应用都干了些什么。由此OAuth2.0的出现,就是为了解决这样的问题。
三、OAuth 2.0中的4个成员
在OAuth2.0中有4个成员,Resource Owner、Resource Server、Client、Authorization Server,如图所示:
在上面4个成员中,授权服务器可能与资源服务器在同一台服务器。
四、OAuth 2.0授权流程
流程如下,图片来自RFC6749:
流程解析:
-
用户打开客户端,客户端要求向资源所有者(即用户)给予授权;
-
用户同意授权;
-
客户端得知用户同意授权后,向授权服务器获取授权;
-
授权服务器给予客户端授权,并将授权码(Access Token);
-
客户端携带授权码去请求资源服务器;
-
资源服务器将受限的资源开放给客户端。
五、OAuth 2.0授权模式
授权许可是表示客户用来获取访问令牌的资源所有者授权的凭证。此规范协议规定了4种授权类型:
-
authorization code(授权码模式)
-
implicit(简化模式)
-
resource owner password credentials(密码模式)
-
client credentials(客户端模式)
下面详细说明各种授权模式的具体流程:
1、authorization code(授权码模式)
授权代码授权类型用于获取访问令牌和刷新令牌,并针对机密客户端进行优化。它是一个基于重定向的流程,因此客户端必须能够与资源所有者的用户代理(通常是Web浏览器)并且能够从授权服务器接收传入请求(通过重定向)。授权码模式是功能最完整、流程最严密的授权模式,它的特点就是通过客户端的后台服务器,与"服务提供商"的授权服务器进行互动。授权流程如下:
流程解析:
-
客户端通过用户代理,重定向请求授权服务器,所需参数:客户端标识及重定向URL;
-
用户选择是否给予客户端授权;
-
授权服务器给予客户端一个认证授权码,并跳转到指定的URI;
-
客户端使用该授权码,重定向到授权服务器,获取令牌;
-
授权服务器校验该授权码以及重定向的URI后,向客户端发送令牌或者更新令牌。
以上均个人解释,简化了RFC6749官方文档说明,大致意思是一样的。注意的一点是,在用户同意授权后,授权服务器并未直接将令牌发送给客户端,而是先向客户端发送了一个授权码,authorization code,然后再携带该授权码,再一次请求授权服务器,校验无误后,再发送令牌(access token),这一步是在后台自动完成的,这样也使OAuth授权更加安全。
在该授权流程中,我们所需的几个参数,官方文档中要求指定的参数如下:
-
response_type 必选项 表示的是要求指定的授权类型,此处必须设置为:code
-
client_id 必选项 客户端的唯一标识
-
redirect_uri 可选项 重定向的URI
-
scope 可选项 授权的管道
-
state 建议项 表示客户端状态,授权服务器会将该状态原值返回
看完该流程以及所需的参数后,我们结合实际情况,还是上面提到的例子,以CSDN使用QQ登录为例,看一下该过程:
(A步骤)点击QQ登录,跳转到QQ帐号安全登录界面,即腾讯的互联平台,我们可以直接拿到此时CSDN要请求的地址:
https://graph.qq.com/oauth2.0/show?which=Login&display=pc&
response_type=code&
client_id=100270989&
redirect_uri=https://passport.csdn.net/account/login?oauth_provider=QQProvider&
state=test
这样我们可以很清楚的看到在上面的URL中,发起了一个get请求,他的授权类型为code 授权码模式,以及client_id,redirect_uri,state等信息,而此时页面也跳转到了用户代理的页面,让用户去决定是否要同意授权 :
(C步骤)当用户输入用户名密码后点击授权并登录,服务器会先验证你输入的是否正确,如果正确,页面就会跳转到redirect_uri的地址中,而在这个过程中发生的变化是这样的,我们继续查看这时的URL地址:
https://passport.csdn.net/account/login?oauth_provider=QQProvider&
code=D185F3ED93E4F1B3C5F557E6112C7A9B&
state=test
(D步骤)我们可以看到授权并登录后它重定向到了我们指定的地址,而且还给予了一个code授权码,另外state状态还是在请求登录时的状态,在下一个步骤中,是客户端向授权服务器申请令牌,包含以下参数:
-
grant_type 表示授权模式,此处固定为authorization_code,必选
-
code 表示上一步获取到的授权码,必选
-
redirect_uri 表示重定向URI,必选
-
client_id 表示客户端ID,必选
在使用腾讯开放平台时,它会要求有一个client_secrect,是对应你客户端ID的一个私钥,它也是在客户端注册时产生的,所以在使用QQ登录获取令牌时,也必须指定该选项:client_secret,具体的可以看腾讯的开发文档。那么实际上申请令牌的这个过程我们是看不到的,返回authorization code 后,我们会看到我们的客户端此时已经登录成功了。例如:
POST /token HTTP/1.1
Host: graph.qq.com/oauth2.0/token
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&client_secret=fdhsjfdsj32jjfhjdk
(E步骤)若以上返回成功,那么在E步骤中会返回如下信息:
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",
"example_parameter":"example_value"
}
里面包含了我们所需要的令牌access token以及有效期等信息。实际上在我们实际应用过程中,我们不止是到这里就结束了,我们会使用access token令牌再去请求资源服务器,获取可以被调用的资源,以及一些其他的操作。
2、implicit(简化模式)
简化模式用于获取访问令牌(但它不支持令牌的刷新),并对运行特定重定向URI的公共客户端进行优化,而这一些列操作通常会使用脚本语言在浏览器中完成,令牌对访问者是可见的,且客户端也不需要验证。具体流程如下:
步骤解析:
-
客户端携带客户端标识以及重定向URI到授权服务器;
-
用户确认是否要授权给客户端;
-
授权服务器得到许可后,跳转到指定的重定向地址,并将令牌也包含在了里面;
-
客户端不携带上次获取到的包含令牌的片段,去请求资源服务器;
-
资源服务器会向浏览器返回一个脚本;
-
浏览器会根据上一步返回的脚本,去提取在C步骤中获取到的令牌;
-
浏览器将令牌推送给客户端。
(A步骤)中需要用到的参数,注意在这里要使用"application/x-www-form-urlencoded"格式:
-
response_type 必选项,此值必须为"token"
-
client_id 必选项
-
redirect_uri 可选项
-
scope 可选项
-
state 建议选项
例如:
GET /authorize?response_type=token&client_id=s6BhdRkqt3&state=xyz
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com
(C步骤)中返回的参数包含:
-
access_token 必选项
-
token_type 必选项
-
expires_in 建议选项
-
scope 可选项
-
state 必选项
例如:
HTTP/1.1 302 Found
Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA
&state=xyz&token_type=example&expires_in=3600
3、resource owner password credentials(密码模式)
密码模式适合建立在客户端与资源所有者具有信任关系的情况下,例如它是一个设备的操作系统或者具有很高权限的应用。这种模式用户要向客户端提供自己的用户名密码,从而达到向服务提供商索取授权。授权服务器在启动此类型时,要特别小心,只有在其他的授权方式不被允许的情况下才可以使用这种授权模式。流程如下:
-
客户端要求使用资源所有者的密码;
-
资源所有者给予用户名密码后,客户端向授权服务器发起申请令牌的请求;
-
授权服务器将令牌发放给客户端。
步骤解析:
(B步骤)所需参数:
-
grant_type 必选项 此值必须为"password"
-
username 必选项 用户名
-
password 必选项 密码
-
scope 可选项
例如:
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=johndoe&password=A3ddj3w
(C步骤)发放令牌:
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",
"example_parameter":"example_value"
}
4、client credentials(客户端模式)
客户端模式是4种模式中最简单的一种模式。客户端可以使用客户端凭据请求访问令牌(或者其他支持的认证方式),在这种模式中,客户端占据主导地位,它不需要用户的同意,可以直接向授权服务器索取令牌,严格来说,该模式并不存在授权的问题,流程如下:
-
客户端向授权服务器发起请求索要令牌;
-
授权服务器将令牌发放给客户端。
步骤解析:
(A步骤)中所需的参数::
-
grant_type 必选项 此值必须为client_crendentials
-
scope 可选项
例如:
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
(B步骤)授权服务器返回结果:
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,
"example_parameter":"example_value"
}
六、小结
以以上内容简要介绍了关于OAuth授权的一些基础知识以及各种授权模式的具体流程。在后面的文章中,会结合本篇内容,陆续讲解如何在.net core中借助IdentityServer4 使用 OAuth 2.0授权。
扫描二维码关注我的公众号,共同学习,共同进步!