OAuth2基本概念和运做流程

OAuth(开放受权)是一个关于受权的开放标准,容许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。目前的版本是2.0版,本文将对OAuth2.0的一些基本概念和运行流程作一个简要介绍。主要参考RFC-6749html

应用场景

这里有两个典型的例子:git

  • 好比你浏览某个网站的技术文章,发现其中某段介绍的不够详细,想留言给做者提问,点击评论,结果发现须要有这个网站的帐号才能留言,此时有两个选择,一个是新注册一个此网站的帐号,二是点击经过github快速登陆。前者你以为过于繁琐,直接点击了github登陆,此时,OAuth的认证流程就开始了。经过引导跳转到github界面,会提示你是否受权该网站使用你的github用户信息,点击确认,跳转回原网站,发现已经使用你的github帐号默认注册了一个用户,并且还不须要用户名和密码,便捷高效。
  • 假若有一个云冲印的网站,能够将你存储在Google的照片冲印出来,用户为了使用该服务,必须让云冲印读取Google上的照片。为了拿到照片,云冲印必须得拿到一个用户的受权,如何获取这个用户受权呢?传统方法是用户将用户名和密码告诉云冲印,那么云冲印就能够自由无限制的访问了(至关于用户本身访问),这样显然是不行的,有几个严重的缺点:github

    • 云冲印为了保存后续服务,会保存用户的密码,这样很不安全。
    • 云冲印拥有了获取用户存储在Google的全部资料的权力,用户无法限制云冲印获得的受权范围和受权有效期。
    • 用户只有修改密码,才能收回赋予云冲印的权力,可是若是还受权给了其余的应用,那么密码的修改将影响到全部被受权应用。
    • 只要有一个第三方应用程序被破解,就会致使用户密码泄漏,以及全部被密码保护的数据泄漏。(例子来自阮一峰-理解OAuth2.0)

能够看出,OAuth就是为解决如上例子而诞生的。web

名词解释

如下几个名词相当重要:json

  • Resource Owner:资源全部者。即用户。
  • Client:客户端(第三方应用)。如云冲印。
  • HTTP service:HTTP服务提供商,简称服务提供商。如上文提到的github或者Google。
  • User Agent:用户代理。本文中就是指浏览器。
  • Authorization server:受权(认证)服务器。即服务提供商专门用来处理认证的服务器。
  • Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,能够是同一台服务器,也能够是不一样的服务器。
  • Access Token:访问令牌。使用合法的访问令牌获取受保护的资源。

运行流程

运行流程

  • (A)客户端向资源全部者请求受权。受权请求能够直接对资源全部者(如图所示)进行,或者经过受权服务器做为中介进行间接访问(首选方案)。
  • (B)资源全部者容许受权,并返回凭证(如code)。
  • (C)客户端经过受权服务器进行身份验证,并提供受权凭证(如code),请求访问令牌(access token)。
  • (D)受权服务器对客户端进行身份验证,并验证受权凭证,若是有效,则发出访问令牌。
  • (E)客户端向资源服务器请求受保护的资源,并经过提供访问令牌来进行身份验证。
  • (F)资源服务器验证访问令牌,若是正确则返回受保护资源。

受权

从运行流程不难看出,要获取access token必须先获得用户受权(authorzation grant),那么若是获取这么用户受权呢?OAuth 2.0定义了四种类型的受权类型:api

  • 受权码模式(authorization code
  • 简化模式(implicit
  • 密码模式(resource owner password credentials
  • 客户端模式(client credentials

受权码模式(authorization code

受权码模式是功能最完整、使用最普遍、流程最严密的受权模式。浏览器

因为这是一个基于重定向的流,因此客户端必须可以与资源全部者的用户代理(一般是web浏览器)进行交互,而且可以从受权服务器接收传入的请求(经过重定向)。
受权码模式安全

  • (A)用户访问客户端,客户端将用户导向受权服务器,经过用户代理(User-Agent)发送包括它的客户端标识符、请求的范围、本地状态和一个重定向URI,受权服务器在授予(或拒绝)访问权后将其发送给用户代理。
  • (B)受权服务器对资源全部者进行身份验证(经过用户代理),并肯定资源全部者是否授予或拒绝客户端的访问请求。
  • (C)假如资源全部者赞成受权请求,那么受权服务器将会使用前面提供的或者事先指定的重定向URI(redirection URI),重定向到客户端,并附上一个受权码(code)和一个前面提供的本地状态(state)(若是有的话,则会原值返回)。
  • (D)客户端收到受权码,附上早先的重定向URI,向受权服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。在发出请求时,受权服务器对客户端进行身份验证。请求参数包含受权代码、用于得到验证的受权代码的重定向URI、标识客户端身份的client idclient secret
  • (E)受权服务器对客户端进行身份验证,验证受权代码,并确保所收到的重定向URI与用于在步骤(C)中对客户端重定向的URI相匹配,若是有效,受权服务器将发送访问令牌access token和刷新令牌refresh token(可选)。

接着来介绍下各个步骤所需的参数服务器

对于步骤A,客户端申请受权请求的URI,包含如下参数:app

  • response_type受权类型。必选项,其值固定为code
  • client_id客户端id。必选项,用于标识受权服务器中已注册的客户端。
  • redirect_uri重定向URI。可选项,若是不填写则使用注册在受权服务器端与client_id对应的redirect_uri。
  • scope申请的权限范围,如readwrite。可选项,若是申请的请求访问超出受权服务器定义的可操做范围则会失败。
  • state表示客户端当前状态。可选项,能够指定任意值,受权服务器会原封不动地返回这个值。

eg:

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

C步骤中,服务器回应客户端的URI,包含如下参数:

  • code受权码。必选项,受权码必须在颁发后很快过时以减少泄露风险,建议最长时间设为10分钟,客户端只能使用该码一次,不然会被受权服务器拒绝。该码与client id和重定向URI,是一一对应关系。
  • state若是客户端的请求中包含这个参数,认证服务器的回应也必须如出一辙包含这个参数。

eg:

HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
          &state=xyz

D步骤中,客户端向认证服务器申请令牌的HTTP请求,包含如下参数:

  • grant_type许可类型(受权模式)。必选项,此处固定值为authorization_code
  • code上一步得到的受权码。必选项。
  • redirect_uri表示重定向URI。必选项,且必须与A步骤中的该参数值保持一致。
  • client_id表示客户端ID,必选项。

eg:

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

E步骤中,认证服务器发送的HTTP回复,包含如下参数:

  • access_token表示访问令牌。必选项。
  • token_type表示令牌类型。该值大小写不敏感,必选项,能够是bearer类型或mac类型。
  • expires_in表示过时时间,单位为秒。若是省略该参数,必须其余方式设置过时时间。
  • refresh_token表示更新令牌。可选项,用来获取下一次的访问令牌。
  • scope表示权限范围。可选项,若是与客户端申请的范围一致,此项可省略。

eg:

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"
}

简化模式(implicit

简化模式(implicit grant type)不经过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了"受权码"这个步骤,所以得名。全部步骤在浏览器中完成,令牌对访问者是可见的,且客户端不须要认证。具体步骤可参阅RFC6749 4.2节。

密码模式(resource owner password credentials

密码模式中,用户向客户端提供本身的用户名和密码。客户端使用这些信息,向"服务商提供商"索要受权。
在这种模式中,用户必须把本身的密码给客户端,可是客户端不得储存密码。这一般用在用户对客户端高度信任的状况下,好比客户端是操做系统的一部分,或者由一个著名公司出品。而认证服务器只有在其余受权模式没法执行的状况下,才能考虑使用这种模式。可参阅RFC6749 4.3节。

客户端模式(client credentials

客户端模式(Client Credentials Grant)指客户端以本身的名义,而不是以用户的名义,向"服务提供商"进行认证。严格地说,客户端模式并不属于OAuth框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以本身的名义要求"服务提供商"提供服务,其实不存在受权问题。可参阅RFC6749 4.4节。

基于Github登陆的受权码模式例子

前文提到了一个Github登陆留言的例子,假设咱们要使用OAuth2.0协议搭建一个网站,利用Github做为受权和资源服务器,实现第三方登陆功能。
转载)现概况一下主要流程:

1) 网站和Github之间的协商

Github会对用户的权限作分类好比读取仓库信息的权限、写入仓库的权限、读取用户信息的权限、修改用户信息的权限等等。若是我想获取用户的信息,Github会要求我,先在它的平台上注册一个应用,在申请的时候标明须要获取用户信息的哪些权限,而且在申请的时候填写你的网站域名,Github只容许在这个域名中获取用户信息。

此时个人网站已经和Github之间达成了共识,Github也给我发了两张门票,一张门票叫作Client Id,另外一张门票叫作Client Secret

2)用户和Github之间的协商

用户进入个人网站,点击github登陆按钮的时候,个人网站会将Github给个人Client Id交给用户,让他进入Github受权界面,若是此时用户没有登陆,Github会提示登陆(固然这不是OAuth2.0客户端部分应该关注的)。假设用户已经登陆Github,那么Github看到用户手中的门票,就知道是个人网站让他过来的,因而就把个人网站获取的权限摆出来,并询问用户是否容许网站获取这些权限。

// 用户登陆 github,协商
GET //github.com/login/oauth/authorize
// 协商凭证
params = {
  client_id: "xxxx",
  redirect_uri: "http://my-website.com"
}

若是用户赞成,在受权页面点击了确认受权后,页面会跳转到我预先设定的 redirect_uri并附带一个盖了章的门票code

// 协商成功后带着盖了章的 code
Location: http://my-website.com?code=xxx

这个时候,用户和 Github 之间的协商就已经完成,Github 也会在本身的系统中记录此次协商,表示该用户已经容许在个人网站访问上直接操做和使用他的部分资源。

3)告诉Github个人网站要来访问

第二步中,咱们已经拿到了盖过章的门票code,但这个code 只能代表,用户容许个人网站从github上获取该用户的数据,若是我直接拿这个code去github访问数据必定会被拒绝,由于任何人均可以持有code,github并不知道code持有方就是我本人。

还记得以前申请应用的时候github给个人两张门票么,Client Id在上一步中已经用过了,接下来轮到另外一张门票Client Secret

// 网站和 github 之间的协商
POST //github.com/login/oauth/access_token
// 协商凭证包括 github 给用户盖的章和 github 发给个人门票
params = {
  code: "xxx",
  client_id: "xxx",
  client_secret: "xxx",
  redirect_uri: "http://my-website.com"
}

拿着用户盖过章的code和可以标识我的身份的client_idclient_secret去拜访 github,拿到最后的绿卡access_token

// 拿到最后的绿卡
response = {
  access_token: "e72e16c7e42f292c6912e7710c838347ae178b4a"
  scope: "user,gist"
  token_type: "bearer",
  refresh_token: "xxxx"
}

4)用户开始使用Github帐号在个人网站上留言

// 访问用户数据
GET //api.github.com/user?access_token=e72e16c7e42f292c6912e7710c838347ae178b4a

上一步github已经把最后的绿卡access_token给我了,经过github提供的 API 加绿卡就可以访问用户的信息了,能获取用户的哪些权限在response 中也给了明确的说明,scopeusergist,也就是只能获取user组和gist组两个小组的权限,user组中就包含了用户的名字和邮箱等信息了。

// 告诉我用户的名字和邮箱
response = {
  username: "barretlee",
  email: "barret.china@gmail.com"
}
本文到此结束,介绍的整个流程比较简陋,具体细节仍是阅读 RFC6749
相关文章
相关标签/搜索