zoukankan      html  css  js  c++  java
  • 认识OAuth 2.0及实例

    GitHub:https://github.com/JDawnF/learning_note

    一、简介

    1.类比小区门禁

    小区中有门禁,出入需要输入密码,但快递人员等非小区用户进入时,需要先申请小区用户授权方可进入。

    授权机制如下:

    第一步,门禁系统的密码输入器下面,增加一个按钮,叫做"获取授权"。快递员先按这个按钮,去申请授权。

    第二步,他按下按钮以后,小区用户的手机就会跳出对话框:有人正在要求授权。系统还会显示该快递员的姓名、工号和所属的快递公司。

    小区用户确认请求属实,就点击按钮,告诉门禁系统,我同意给予他进入小区的授权。

    第三步,门禁系统得到我的确认以后,向快递员显示一个进入小区的令牌(access token)。令牌就是类似密码的一串数字,只在短期内(比如七天)有效。

    第四步,快递员向门禁系统输入令牌,进入小区。

    有人可能会问,为什么不是远程为快递员开门,而要为他单独生成一个令牌?这是因为快递员可能每天都会来送货,第二天他还可以复用这个令牌。另外,有的小区有多重门禁,快递员可以使用同一个令牌通过它们。

    2.互联网应用

    首先,居民小区就是储存用户数据的网络服务。比如,微信储存了我的好友信息,获取这些信息,就必须经过微信的"门禁系统"。其次,快递员(或者说快递公司)就是第三方应用,想要穿过门禁系统,进入小区。最后,我就是用户本人,同意授权第三方应用进入小区,获取我的数据。

    简单说,OAuth 就是一种授权机制。数据的所有者告诉系统,同意授权第三方应用进入系统,获取这些数据。系统从而产生一个短期的进入令牌(token),用来代替密码,供第三方应用使用。

    二、令牌与密码

    令牌(token)与密码(password)的作用是一样的,都可以进入系统,但是有三点差异。

    (1)令牌是短期的,到期会自动失效,用户自己无法修改。密码一般长期有效,用户不修改,就不会发生变化。

    (2)令牌可以被数据所有者撤销,会立即失效。以上例而言,屋主可以随时取消快递员的令牌。密码一般不允许被他人撤销。

    (3)令牌有权限范围(scope),比如只能进小区的二号门。对于网络服务来说,只读令牌就比读写令牌更安全。密码一般是完整权限。

    上面这些设计,保证了令牌既可以让第三方应用获得权限,同时又随时可控,不会危及系统安全。这就是 OAuth 2.0 的优点。

    注意,只要知道了令牌,就能进入系统。系统一般不会再次确认身份,所以令牌必须保密,泄漏令牌与泄漏密码的后果是一样的。 这也是为什么令牌的有效期,一般都设置得很短的原因。

    OAuth 2.0 对于如何颁发令牌的细节,规定得非常详细。具体来说,一共分成四种授权类型(authorization grant),即四种颁发令牌的方式,适用于不同的互联网场景。

    三、名词定义

    (1) Third-party application:第三方应用程序,本文中又称"客户端"(client),即上一节例子中的"云冲印"。

    (2)HTTP service:HTTP服务提供商,本文中简称"服务提供商",即上一节例子中的Google。

    (3)Resource Owner:资源所有者,本文中又称"用户"(user)。

    (4)User Agent:用户代理,本文中就是指浏览器。

    (5)Authorization server:认证服务器,即服务提供商专门用来处理认证的服务器。

    (6)Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同的服务器。

    OAuth的作用就是让"客户端"安全可控地获取"用户"的授权,与"服务商提供商"进行互动。

    OAuth在"客户端"与"服务提供商"之间,设置了一个授权层(authorization layer)。"客户端"不能直接登录"服务提供商",只能登录授权层,以此将用户与客户端区分开来。"客户端"登录授权层所用的令牌(token),与用户的密码不同。用户可以在登录的时候,指定授权层令牌的权限范围和有效期。

    "客户端"登录授权层以后,"服务提供商"根据令牌的权限范围和有效期,向"客户端"开放用户储存的资料。

    四、运行流程

    OAuth 2.0的运行流程如下图,摘自RFC 6749:

    (A)用户打开客户端以后,客户端要求用户给予授权。

    (B)用户同意给予客户端授权。

    (C)客户端使用上一步获得的授权,向认证服务器申请令牌。

    (D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。

    (E)客户端使用令牌,向资源服务器申请获取资源。

    (F)资源服务器确认令牌无误,同意向客户端开放资源。

    五、客户端的授权模式

    客户端必须得到用户的授权(authorization grant),才能获得令牌(access token)。OAuth 2.0定义了四种授权方式。

    • 授权码模式(authorization code)

    • 简化模式(implicit)

    • 密码模式(resource owner password credentials)

    • 客户端模式(client credentials)

    注意,不管哪一种授权方式,第三方应用申请令牌之前,都必须先到系统备案,说明自己的身份,然后会拿到两个身份识别码:客户端 ID(client ID)和客户端密钥(client secret)。这是为了防止令牌被滥用,没有备案过的第三方应用,是不会拿到令牌的。

    1.授权码模式

    授权码(authorization code)方式,指的是第三方应用先申请一个授权码,然后再用该码获取令牌。这种方式是最常用的流程,安全性也最高,它适用于那些有后端的 Web 应用。授权码通过前端传送,令牌则是储存在后端,而且所有与资源服务器的通信都在后端完成。这样的前后端分离,可以避免令牌泄漏。

    • 第一步,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参数表示要求的授权范围(这里是只读)。

    • 第二步,用户跳转后,B 网站会要求用户登录,然后询问是否同意给予 A 网站授权。用户表示同意,这时 B 网站就会跳回redirect_uri参数指定的网址。跳转时,会传回一个授权码,就像下面这样。

     https://a.com/callback?code=AUTHORIZATION_CODE

    上面 URL 中,code参数就是授权码。

    • 第三步,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参数是令牌颁发后的回调网址。

    • 第四步,B 网站收到请求以后,就会颁发令牌。具体做法是向redirect_uri指定的网址,发送一段 JSON 数据。

     {    
       "access_token":"ACCESS_TOKEN",
       "token_type":"bearer",
       "expires_in":2592000,
       "refresh_token":"REFRESH_TOKEN",
       "scope":"read",
       "uid":100101,
       "info":{...}
     }
    • access_token:表示访问令牌,必选项。

    • token_type:表示令牌类型,该值大小写不敏感,必选项,可以是bearer类型或mac类型。

    • expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。

    • refresh_token:表示更新令牌,用来获取下一次的访问令牌,可选项。

    • scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。

    上面 JSON 数据中,access_token字段就是令牌,A 网站在后端拿到了。

    2.简化模式

    简化模式(implicit grant type)不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了"授权码"这个步骤,因此得名。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。

    它的步骤如下:

    (A)客户端将用户导向认证服务器。

    (B)用户决定是否给于客户端授权。

    (C)假设用户给予授权,认证服务器将用户导向客户端指定的"重定向URI",并在URI的Hash部分包含了访问令牌。

    (D)浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值。

    (E)资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。

    (F)浏览器执行上一步获得的脚本,提取出令牌。

    (G)浏览器将令牌发给客户端。

    下面是上面这些步骤所需要的参数。

    A步骤中,客户端发出的HTTP请求,包含以下参数:

    • response_type:表示授权类型,此处的值固定为"token",必选项。

    • client_id:表示客户端的ID,必选项。

    • redirect_uri:表示重定向的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步骤中,认证服务器回应客户端的URI,包含以下参数:

    • 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

    在上面的例子中,认证服务器用HTTP头信息的Location栏,指定浏览器重定向的网址。注意,在这个网址的Hash部分包含了令牌。

    根据上面的D步骤,下一步浏览器会访问Location指定的网址,但是Hash部分不会发送。接下来的E步骤,服务提供商的资源服务器发送过来的代码,会提取出Hash中的令牌。

    3.密码模式

    密码模式(Resource Owner Password Credentials Grant)中,用户向客户端提供自己的用户名和密码。客户端使用这些信息,向"服务商提供商"索要授权。

    在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。这通常用在用户对客户端高度信任的情况下,比如客户端是操作系统的一部分,或者由一个著名公司出品。而认证服务器只有在其他授权模式无法执行的情况下,才能考虑使用这种模式。

    它的步骤如下:

    (A)用户向客户端提供用户名和密码。

    (B)客户端将用户名和密码发给认证服务器,向后者请求令牌。

    (C)认证服务器确认无误后,向客户端提供访问令牌。

    B步骤中,客户端发出的HTTP请求,包含以下参数:

    • 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 Grant)指客户端以自己的名义,而不是以用户的名义,向"服务提供商"进行认证。严格地说,客户端模式并不属于OAuth框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以自己的名义要求"服务提供商"提供服务,其实不存在授权问题。

    它的步骤如下:

    (A)客户端向认证服务器进行身份认证,并要求一个访问令牌。

    (B)认证服务器确认无误后,向客户端提供访问令牌。

    A步骤中,客户端发出的HTTP请求,包含以下参数:

    • granttype:表示授权类型,此处的值固定为"clientcredentials",必选项。

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

    六、令牌的使用和更新

    1.令牌的使用

    A 网站拿到令牌以后,就可以向 B 网站的 API 请求数据了。此时,每个发到 API 的请求,都必须带有令牌。具体做法是在请求的头信息,加上一个Authorization字段,令牌就放在这个字段里面。

     curl -H "Authorization: Bearer ACCESS_TOKEN" 
     "https://api.b.com"

    上面命令中,ACCESS_TOKEN就是拿到的令牌。

    2.更新令牌

    令牌的有效期到了,如果让用户重新走一遍上面的流程,再申请一个新的令牌,很可能体验不好,而且也没有必要。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

    上面 URL 中,grant_type参数为refresh_token表示要求更新令牌,client_id参数和client_secret参数用于确认身份,refresh_token参数就是用于更新令牌的令牌。B 网站验证通过以后,就会颁发新的令牌。

    七、具体实例

    1.第三方登录原理

    很多网站登录时,允许使用第三方网站的身份,这称为"第三方登录"。

    所谓第三方登录,实质就是 OAuth 授权。用户想要登录 A 网站,A 网站让用户提供第三方网站的数据,证明自己的身份。获取第三方网站的身份数据,就需要 OAuth 授权。

    举例来说,A 网站允许 GitHub 登录,背后就是下面的流程。

    1. A 网站让用户跳转到 GitHub。

    2. GitHub 要求用户登录,然后询问"A 网站要求获得 xx 权限,你是否同意?"

    3. 用户同意,GitHub 就会重定向回 A 网站,同时发回一个授权码。

    4. A 网站使用授权码,向 GitHub 请求令牌。

    5. GitHub 返回令牌.

    6. A 网站使用令牌,向 GitHub 请求用户数据。

    2.应用登记

    一个应用要求 OAuth 授权,必须先到对方网站登记,让对方知道是谁在请求。所以,要先去 GitHub 登记一下。访问这个网址,填写登记表。

    应用的名称随便填,主页 URL 填写http://localhost:8080,跳转网址填写 http://localhost:8080/oauth/redirect

    提交表单以后,GitHub 应该会返回客户端 ID(client ID)和客户端密钥(client secret),这就是应用的身份识别码。

    具体实现参照:http://www.ruanyifeng.com/blog/2019/04/github-oauth.html#comment-text

    参照:阮一峰的网路日志

  • 相关阅读:
    CodeForces 347B Fixed Points (水题)
    CodeForces 347A Difference Row (水题)
    CodeForces 346A Alice and Bob (数学最大公约数)
    CodeForces 474C Captain Marmot (数学,旋转,暴力)
    CodeForces 474B Worms (水题,二分)
    CodeForces 474A Keyboard (水题)
    压力测试学习(一)
    算法学习(一)五个常用算法概念了解
    C#语言规范
    异常System.Threading.Thread.AbortInternal
  • 原文地址:https://www.cnblogs.com/baichendongyang/p/13235448.html
Copyright © 2011-2022 走看看