引自: [认证 & 授权] 1. OAuth2授权.。
OAuth2是一个开放授权标准,它允许让第三方应用访问该用户在某服务的特定私有服务
OAuth2解决问题的关键在于使用Authorization server提供一个访问平局给Client,使得Client可以在不知道Resource owner在Resource server上的用户名和密码的情况下消费Resource owner的受保护资源。
由于OAuth2引入了Authorization server来管理Resource Owner,Client和Resource Server的三角关系,那么想要用上OAuth2,是实现以下功能的。
作为资源服务提供商来说,1,2,3这三件事情是需要完成的。
作为第三方应用程序,要完成的工作是在4和5这两个步骤中。
其中作为Resource owner来说,是不用做什么的,是OAuth2收益的千千万万的最终人类用户。
在Client取得client_id和client_secret之后。使用这些信息来发起授权请求、获取access_token请求和消费受包含的资源。
在上述OAuth完整流程中,(A) -> (B) -> © -> (D) 是授权的过程;(E) -> (F)是消费资源的过程。
授权许可是一个代表资源所有者授权(访问收保护资源)的凭据,客户端用它来获取访问令牌。即,授权许可是资源拥有者授予客户端获取资源访问令牌的一个凭据。
OAuth2定义了四种许可类型以及用于定义其他类型的可扩展性机制:
这是OAuth2最常用的一种授权许可类型。要求Client具体可公开访问的Server服务器来接受Authoriation Code,具体的流程如下:
对应步骤(A),客户端土工以下参数请求Authorization Server:
示例如下:
GET /authorize?response_type=code&client_id=1&state=xyz&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Foauth2&scope=user,photo HTTP/1.1
Host: server.example.com
对应步骤©,Authorization Server会返回如下信息:
HTTP/1.1 302 Found
Location: https://client.example.com/oauth2code=SplxlOBeZQQYbYS6WxSbIA&state=xyz
Location头部指向步骤(A)提供的redirect_uri地址,同时携带code信息和state信息给client,这样浏览器在重定向的时候就会以GET的方式访问Client提供的redirect_uri,同时Client接收到code信息和state信息。下一步就可以请求access_token了。
对应步骤(D),客户端提供以下参数请求Authorization Server:
POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=123&client_id=1&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Foauth2
对应步骤(E),Authorization Server会返回如下典型的信息:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
这个是Authorization Code的简化版本。其中省略了拌饭授权码(Authorization Code)给客户端的过程,而是之间返回访问令牌和可选的刷新令牌。其适用于没有Server服务器来接受处理Authorization Code的第三方应用,其流程如下:
和Authorzation Code类型下重要的区分就是省略了Authorzation Respongse和Access Token Request。而是直接由Authorzation Request返回Access Token Response信息,具体如下。
客户端提供以下参数请求Authorization Server:
GET /authorize?response_type=token&client_id=1&state=xyz&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Foauth2&scope=user,photo HTTP/1.1
Host: server.example.com
Authorization Server会返回如下典型的信息:
示例如下:
HTTP/1.1 302 Found
Location: http://client.example.com/oauth2#access_token=2YotnFZFEjr1zCsicMWpAA&state=xyz&expires_in=3600
注意其和Authorization Code的最大区别在于它是把token信息放在了url的hash部分(#后面),而不是作为参数(?后面)。这样浏览器在访问重定向的Location指定的url时,就不会把这些数据发送到服务器。而Client可以通过读取Location头信息中获取到access_token信息。
这种模式再一步简化,和Authorzation Code类型下重要的区分就是省略了Authorization Request和Authorization Response。而是Client直接使用Resource owner提供的username和password来直接请求access_token(直接发起Access Token Request然后返回Access Token Response信息)。这种模式一般适用于Resource server高度信任第三方Client的情况下。
客户端提供以下参数请求Authorization Server:
示例如下:
POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=blackheart&password=1234
Access Token Response和Authorization Code一致,就不列出来了。
这种类型就更简化了,Client直接以自己的名义而不是Resource owner的名义去要求访问Resource server的一些受保护资源。
客户端提供以下参数请求Authorization Server:
示例如下:
POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
在上述得到访问令牌(access_token)时,一般会提供一个过期时间和刷新令牌。以便在访问令牌过期失效的时候可以由客户端自动获取新的访问令牌,而不是让用户再次登陆授权。那么问题来了,是否可以把过期时间设置的无限大呢,答案是可以的。如下是刷新令牌的收客户端需要提供给Authorization Server的参数:
示例如下:
POST /token HTTP/1.1
Host: server.example.com
grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
响应信息和 Access Token Response保持一致。
在第三方Client拿到access_token后,如何发送给Resouce Server这个问题并没有在RFC6729种定义,而是作为一个单独的RFC6750来独立定义了。这里做以下简单的介绍,主要有三种方式如下:
这种使用途径应该是最常见的一种方式,非常简单,比如:
GET /resource?access_token=mF_9.B5f-4.1JqM HTTP/1.1
Host: server.example.com
在我们请求受保护的资源的Url后面追加一个access_token的参数即可。另外还有一点要求,就是Client需要设置以下Request Header的Cache-Control:no-store,用来阻止access_token不会被Web中间件给log下来,属于安全防护方面的一个考虑。
因为在HTTP应用层协议中,专门有定义一个授权使用的Request Header,所以也可以使用这种方式:
GET /resource HTTP/1.1
Host: server.example.com
Authorization: Bearer mF_9.B5f-4.1JqM
其中"Bearer "是固定的在access_token前面的头部信息。
使用Request Body这种方式,有一个额外的要求,就是Request Header的"Content-Type"必须是固定的“application/x-www-form-urlencoded”,此外还有一个限制就是不可以使用GET访问,这个好理解,毕竟GET请求是不能携带Request Body的。
POST /resource HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
access_token=mF_9.B5f-4.1JqM
在OAuth2早期的时候爆发过不少相关的安全方面的漏洞,其实仔细分析后会发现大都都是没有严格遵循OAuth2的安全相关的指导造成的,相关的漏洞事件百度以下就有了。
其实OAuth2在设计之初是已经做了很多安全方面的考虑,并且在RFC6749中加入了一些安全方面的规范指导。比如
要求Authorization server进行有效的Client验证;
client_serect,access_token,refresh_token,code等敏感信息的安全存储(不得泄露给第三方)、传输通道的安全性(TSL的要求);
维持refresh_token和第三方应用的绑定,刷新失效机制;
维持Authorization Code和第三方应用的绑定,这也是state参数为什么是推荐的一点,以防止CSRF;
保证上述各种令牌信息的不可猜测行,以防止被猜测得到;
安全无小事,这方面是要靠各方面(开放平台,第三方开发者)共同防范的。如QQ互联的OAuth2 API中,state参数是强制必选的参数,授权接口是基于HTTPS的加密通道等;同时作为第三方开发者在使用消费这些服务的时候也应该遵循其相关的安全规范。