OAuth2.0 — 简化授权模式详解

本期图文咱们看一看另外一种可以解决小美问题的授权模式,Implicit Grant Type 简化模式。这种模式我不打算详细介绍,这种授权模式不通过第三方应用程序的后台服务器,直接在浏览器中向认证服务器申请令牌。它的授权流程中取消了授权码的认证过程,简化了授权流程,所以说它是一种简化的模式,所有步骤在浏览器中完成,当在浏览器中回调的网页会打开后,access token 直接在浏览器的地址栏中就能看见,这是一种不安全的模式,容易造成 access_token 的泄露。但是我之所以要写这一篇图文,除了简单介绍一下这种授权模式外,真的是因为有话要说。

 

 OAuth 2.0 简单授权模式

 

 

那咱们先说正经的,完了之后再耍流氓,先看看第三方应用获取 Access Token 的流程:

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

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

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

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

 

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

  • response_type:表示授权类型,此处的值固定为 "token",必选项。
  • client_id:表示客户端的 ID,必选项。
  • redirect_uri:表示重定向的 URI,可选项。
  • scope:表示权限范围,可选项。
  • state:表示客户端的当前状态,为预防 CSRF 攻击,请务必设定此值并进行严格检查,认证服务器会原封不动地返回这个值。

 

 

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

  • access_token:表示访问令牌,必选项。
  • token_type:表示令牌类型,该值大小写不敏感,必选项。
  • expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
  • scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。
  • state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。

 

 

在上面的例子中,认证服务器用 HTTP 头信息的 Location 栏,指定浏览器重定向的网址。浏览器自动打开 Location 中设定的网页后,我们就可以在浏览器地址栏中看到access_token。注意,在这个网址的 Hash 部分包含了令牌,在上图中示例中咱们就可以看到这个令牌。我们到了这一步就可以直接使用 Javascript 代码提取出 Access Token 发送给客户端的后台服务器了。如果你之前看过国内其他人写的 OAuth2.0 简化模式的技术博客的话,你应该会发现我写的这篇图文要简单得多,简化了很多流程和步骤,这也是我为什么要写这篇图文的原因。我们先把国内互相抄袭的版本拿过来看一下,咱们先看一看大家都再传播的一张流程图:

 

 

大家明显会发现这张图中的流程复杂了很多,A 到 C 的步骤跟我说的步骤是完全相同的,也获取到了Access Token,然后再粘贴一下之后的流程介绍

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

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

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

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

 

我最初学习 OAuth 2.0 的时候,我也是看到网上的各种博客,阮一峰的版本发的最早,对相关文档的整理和翻译得也到位,后来国内其他人写的OAuth 2.0技术博客都是照抄阮一峰的或者进行变种,但是关于简化授权模式的这部分我怎么都觉得很别扭,明明在 C 步骤已经获得了 Access Token,提取出来不就完了吗?后面的一堆流程就是请求了一段解析Access Token 的脚本代码,也并没有在安全性、效率、功能以及认证方面有任何辅助性的操作。增加的这些流程只是增加了复杂度,降低了效率,所以完全可以取消这些流程,C 步骤结束后,自己写一段 Javascrip 解析代码提取 Access Token 即可。其实并不是说其他人都错了,大家只是忠实的翻译了一下 RFC 6749 这篇技术文档中的流程罢了。

 

文档和规范终究也是人写的,简化授权模式的官方文档其实犯了一个错误,把技术人员想得太笨了,就像 OAuth 1.0 版本时犯的错误一样,把流程搞得复杂到令人发指,最后大家都不怎么买账,直到后来 OAuth 2.0 版本极大简化了授权机制和流程后,才得到广泛的认可和应用,但是这个模式 OAuth 2.0 还是把它搞复杂了,多了后续那些不必要的流程,如果有 OAuth 3.0,我想肯定不会再这么设计了。

 

最后,还是那句话,在这个信息爆炸的时代,保持一份理性和质疑的精神很重要,技术人员的最宝贵的思考力和逻辑推理能力不能丢。人越浮躁,就越下不了功夫,我图文写的很少,但是只要是我写东西,我会非常认真严谨的写,我懒的时候也会去摘抄别人的东西,但是核心的东西我从来不敢懈怠。规矩是死的,人是活的,宪法都能改,一个小小的技术文档和有瑕疵授权标准又有什么不能质疑的。

评论区
o00oo000oo 2019.12.01 04:29

喜欢你的态度,和我很像

(●—●)

国营 2019.12.01 05:11

同道中人,欢迎常来做客

kaykay 2019.12.11 05:51

学习了

国营 2019.12.11 05:54

欢迎朋友来到 Coding10 网站做客,共同进步 ~

kaykay 2019.12.11 10:19

好,收藏了 😄

娃哈哈 2020.09.23 02:14

会员没有群吗,有问题的话在哪里可以互相讨论呢

国营 2020.09.23 02:52

没有群,不玩群营销,大部分群就是耽误时间精力,希望大家专注在自己的目标上,而不是浪费在没有什么实际意义的无聊闲谈上。

娃哈哈 2020.10.15 13:04

老师您好,Model 多键查询 这个课程我想问个问题,如果用户表中,有用户名、昵称、邮箱、手机号 等等字段。要求是只有一个搜索框,用户可以随便输入用户名、昵称、邮箱... 就可以查找到用户。现在我的做法是写了一个方法,里面有好多个 orWhere,这个功能有什么奇技淫巧么? User::orWhere('name','like','%test%')->orWhere('email','like','sdfafewf@sdf.com')->orWhere('phone','like','%213123213%')->get();

娃哈哈 2020.10.15 13:04

老师您好,Model 多键查询 这个课程我想问个问题,如果用户表中,有用户名、昵称、邮箱、手机号 等等字段。要求是只有一个搜索框,用户可以随便输入用户名、昵称、邮箱... 就可以查找到用户。现在我的做法是写了一个方法,里面有好多个 orWhere,这个功能有什么奇技淫巧么? User::orWhere('name','like','%test%')->orWhere('email','like','sdfafewf@sdf.com')->orWhere('phone','like','%213123213%')->get();

国营 2020.10.15 13:33

还真没啥奇技淫巧,但是最好也别这么干,你可以借助Scout,不管是algolia还是TNT,都可以更加简单高效的实现这个功能,而不是用这么低效的msql查询方式,数据量大了之后,使用这种方式简直就是自杀行径。

娃哈哈 2020.10.22 10:11

老师您好。我想问一下在记录API请求日志的时候,怎么记录当前接口返回的状态码呢。怎么样可以获取一个请求的所有 response信息呢。 谢谢老师。

娃哈哈 2020.10.22 10:23

用这个是可以获取,有没有更好的方法呢【日志记录有没有更好的方式】现在是用observe监听。response('http_code')->status()。

国营 2020.10.22 12:13

你也可以直接使用response()-&gtgetStatusCode(), 不过哪种方式都没关系,这个相互比较也不能说另一个就更好。至于为接口调用进行记录或统计的话,最好在一个地方书写逻辑,而不是所有的接口里都写这个东西,高效一点的可以直接在入口文件index.php里边写,并且记录的工作放在队列里,不要用同步的方式,那样效率就低下了。如果低效一点的话,可以用Event的方式,我记得有一个ResponseEvent,你可以实验一下,我自己没用过它。最后说一句,我就是个站长,就别叫我老师了,叫站长就行。

娃哈哈 2020.10.23 01:43

站长,这个方法我倒是试了一下,报错了。response()-&gtgetStatusCode()。应该使用哪个呢 ? 我用的是路由里面的这个 BadMethodCallException: Method Illuminate\Routing\ResponseFactory::getStatusCode does not exist.

国营 2020.10.23 01:50

那就用你知道的那个就行了,没必要这这上面纠结耽误时间和精力,就是一些调用方法而已,跟技术水平提升没有啥关系。

微信扫码登录