转自:http://bylijinnan.iteye.com/blog/2277548

OAuth2的学习,我也是从阮一峰老师的博客中开始的:http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html
下文中以“该博客”指代阮老师的博客。

但,阮一峰老师的博客似乎忽略了很多细节。

OAuth2的实际应用中,最常见的就是“授权码模式”了。 
微博是这种模式,微信也是这种模式。 
总结来说,就是简单的二步:

1.获取code
2.根据code,去获取access_token

以微博为例(http://open.weibo.com/wiki/%E6%8E%88%E6%9D%83%E6%9C%BA%E5%88%B6%E8%AF%B4%E6%98%8E):

假设我们开发一个网站(称为client;相对应的,微博就是server了),网站允许用户以微博账号登录,且需要读取用户的微博信息(账号、评论等等)。 
显然,这过程中需要用户授权。

第一步:

https://api.weibo.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=YOUR_REGISTERED_REDIRECT_URI

第二步:

https://api.weibo.com/oauth2/access_token?client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&grant_type=authorization_code&redirect_uri=YOUR_REGISTERED_REDIRECT_URI&code=CODE

这其中有几个问题: 
1.获取code时,传递的二个变量(clent_id,redirect_url),没有一个是跟用户有关的,那微博怎么知道我们请求的是哪个用户的授权呢? 
这个问题现在看来很可笑,但刚开始确实是蒙圈了。 
其实,在第一步的URI发出后,是要求用户登录的。这很好理解,如果用户不登录,那你怎么知道是哪个用户呢,他又怎么给你授权呢? 
此外,可以看到,第一步的URI,放在我们网站页面的任何位置都可以;它也不要求传递跟session相关的任何信息。 
这个URI对应的页面是微博开发的,跟我们网站没关系。 
事实上,这个URI可以直接拷贝到浏览器里发起请求;这个时候,是浏览器跟微博的一个交互,我们的网站是一无所知的。 
那什么时候我们得知用户给我们授权了呢? 
第一步URI的请求,浏览器得到的是一个http的重定向响应,这个重定向指向的就是我们的网站;此时浏览器向我们的网站发起请求: 
类似于: 
https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz 
站在我们网站后台程序的角度来看,我们是“忽然”(在此这前,它一无所知)收到一个code了,而这个code,是跟微博的一个用户一一对应的,我们拿这个code向微博发起请求,就能获得用户的授权,获得access_token,再发起另一请求,就可以获取用户信息了。

2.为什么在第一个URI请求发出后,微博的302重定向响应中,没有直接返回access_token,而是只返回一个code呢? 
其实最初我的疑问更可笑:我想,为什么微博不直接返回access_token给我们网站呢? 
这个问题的答案是,发起第一步的URI请求的,是浏览器!还记得前面我说的“我们的网站是一无所知”吗?此时微博是与浏览器在通信,而不是我们的网站,当然不能把参数传返回到我们网站了。 
微博只能通过返回一个302响应,让浏览器重新向我们网站发起请求,把code传到我们网站来。

回到第二个问题。 
该博客下有人是这样回答的:

为什么授权码模式需要这个授权码 当然是为了安全性 首先在OAuth体系中access_token是作为访问获取资源的唯一凭据 如果在AS授权完成之后 直接通过重定向传回access_token 那么HTTP 302不是安全的 Attacker有可能会获取到access_token 但是如果只返回Authorization code 就算别人获得了也没什么卵用 因为Authorization code不能获取到资源 在client向AS请求access_token的过程中 是通过HTTPS来保证安全的 而且获得access_token是需要client secret与Authorization code一起的 Attacker知道了Authorization code但并不知道client secret 同样也不能获得到access_token 所以client与AS是有责任保护好client secret的
获得了access_token之后 向RS发起请求 RS其实会与AS交互 来校验access_token 所以你想直接伪造一个access_token 那也是不ok的

突然想到漏说了一块 关于为什么不直接用HTTPS重定向回client
是因为不是所有client server都支持HTTPS~所以为了通用性 和安全性 才衍生出来这么一个Auth code
但是AS肯定是实现HTTPS的 所以在client向AS提起request 是木有问题的~

我自己的理解,是这样的: 
直接返回access_token有可能被黑客拦截到,而拿到access_token就可以获得用户信息了,非常不安全。 
那最容易想到的是,微博通过https返回access_token啊,https会对access_token加密,那不就可以了吗? 
为什么这个方法不可行呢? 
因为有可能我们的网站不支持https!站在微博的角度来看,千千万万的第三方网站,你要求每个网站都支持https,是不太现实的。 
stackoverflow上的一个回答(http://stackoverflow.com/questions/13387698/why-is-there-an-authorization-code-flow-in-oauth2-when-implicit-flow-works-s)说这是“一个巨大的痛苦”:“a huge pain”。

那为什么返回code再去获取access_token就安全了呢? 
因为这时候再去获取access_token,就是https请求了,发起方为我们的网站,接收方为微博,而微博作为OAuth server,肯定提供https。 
但是,另一个问题来了,黑客拿到了code,它也向微博发起请求去获取access_token是不是就可以了呢? 
也是不可以的。 
因为向微博请求access_token时,还要带上我们网站的“密码”(client secret)。 
这个密码,是我们网站在微博开放平台上注册时获得的(同时会得到一个client_id,这个值在第一步的URI里用到了)。 
这一点在该博客没有提出来,最容易让人困惑。

3.为什么简化模式(implicit grant type)是可行的? 
这个在stackoverflow有回答,前面也提到过: 
http://stackoverflow.com/questions/13387698/why-is-there-an-authorization-code-flow-in-oauth2-when-implicit-flow-works-s

这就涉及到一个知识点: 
浏览器不会把URL当中的“hash fragment”发给服务器。hash fragment是只留在浏览器的地址栏的,它可以被网页的js读取到:window.location.hash。 
既然hash fragment没有发给服务器,那即使黑客拦截到请求,也获取不到它。 
什么是hash fragment?就是URL当中#号后面的那一部分。 
博客中的示例:

HTTP/1.1 302 Found
Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA&state=xyz&token_type=example&expires_in=3600

可以得知,“#access_token=2YotnFZFEjr1zCsicMWpAA”这一部分是留在浏览器这边的。 
当浏览器接收到到该响应时,向http://example.com/cb发起请求,而后者的响应页面当中,应当有读取hash fragment的代码。 
这样,client就拿到了access_token了。

解决了这三个问题,我本想到实际场景中抓包看看的,但遗憾的是,不管是蘑菇街还是聚美优品,这些允许以微博账号登录的网站,都只看到获取code那一步,没有看到获取access_token那一步。 
或许改天有空可以自己在微博开放平台上注册一个应用来验证一下。 
不过网上有人验证过微博的: 
https://segmentfault.com/a/1190000000666685

此外还有一些关于微信OAuth2的实际例子: 
http://www.cnblogs.com/txw1958/p/weixin71-oauth20.html 
http://zeusjava.com/2015/04/22/wechat-get-userinfo/ 
都讲得比较详细。

OAuth2疑问解答的更多相关文章

  1. 一个想法(续五):IT联盟创业计划:现阶段进度公示、疑问解答及进行中的计划

    前言: 首先今天是元宵节,先祝大伙元宵节快,单纯的快乐! 然后看看开展中的计划: IT联盟创业计划众筹发起:一个想法(续三):一份IT技术联盟创业计划书,开启众筹创业征程 IT联盟创业计划众筹进度:一 ...

  2. C程序疑问解答 ——可怕的野指针

    本篇为原创,禁止任何形式的他用! 一.疑问点         指针是C语言一个很强大的功能,同时也是很容易让人犯错的一个功能,用错了指针,轻者只是报个错,重者可能整个系统都崩溃了.下面是大家在编写C程 ...

  3. 关于ABViewer的疑问解答

    很多 CAD小伙伴都对 ABViewer 这款软件不陌生吧.ABViewer 是用来处理图纸和工程文档管理的一款通用软件.可以用它来查看,编辑,转换,测量和打印DWG和其他CAD文件,以及3D模型和光 ...

  4. apache rewrite_mod 经典疑问解答

    1.RewriteRule ^(com\/.*)$ index.php?do=$1 问:上面的规则匹配表达式 "^(.*)$" 匹配的内容是什么 答:匹配内容是URI站点目录:/d ...

  5. HTTPS的一些疑问解答

    PHP写的网站怎么用https访问,具体要怎样 这跟用什么语言写的网站没有关系,可以去申请个快速的SSL证书,一年也就几十块. 开启apache server的ssl,自己做个免费的ssl证书或者去申 ...

  6. shiro源码篇 - 疑问解答与系列总结,你值得拥有

    前言 开心一刻 小明的朋友骨折了,小明去他家里看他.他老婆很细心的为他换药,敷药,然后出去买菜.小明满脸羡慕地说:你特么真幸福啊,你老婆对你那么好!朋友哭得稀里哗啦的说:兄弟你别说了,我幸福个锤子,就 ...

  7. 《从零玩转python+人工智能-3》120,122节课深度优先疑问解答

     深度优先(从左往右): 按照这个原则来:至于使用栈,或者队列:根据它们不同的特性:最终务必保证最终结果是原继承结构的“从左往右”:所以,如果是栈,就是右侧先入栈,左侧再入(这样左侧能先出来,遵循从左 ...

  8. cucumber的疑问解答

    在cucumber的自动化测试框架下面,在一个steps文件中定义的@page对象,可以在其他的不同的steps文件中调用,在整个的场景生命周期中都是有效的 原因:cucumber开始执行时,一次性把 ...

  9. java课堂疑问解答与思考2

    问题一 编写一个方法,使用以上算法生成指定数目(比如1000个)的随机整数. 答:Xn+1=(7^5*Xn)mod(2^31-1) 程序源码: import java.util.Random; imp ...

随机推荐

  1. pro_select_roleinfo_p3

    DELIMITER | drop procedure if exists pro_select_roleinfo_p3; CREATE PROCEDURE pro_select_roleinfo_p3 ...

  2. C++ 常见字符处理 收录

    1.string字符串删除 字符串中 指定字符 std::string& HTTPRequestHandlerImpl::replace_all_distinct(std::string&am ...

  3. 我的Android进阶之旅------>解决Android Studio全局搜索搜不到结果的问题

    1.问题描述 今天使用Android Studio时,想通过使用快捷键Ctrl+Shift+F来进行全局搜索指定字符串,如下图所示:想搜索字符串"码农偷懒了", 打开string. ...

  4. Perceptual Generative Adversarial Networks for Small Object Detection

    Perceptual Generative Adversarial Networks for Small Object Detection 感知生成对抗网络用于目标检测 论文链接:https://ar ...

  5. hadoop namenode

    存储文件系统元数据,例如:文件目录结构,不同文件的分块情况,每块存储在那个节点,权限等 这些元数据全部存储在内存中,所以,namenode要求内存比较大 hdfs在存文件的时候会按照块存储,每一块默认 ...

  6. Java泛型四:Java泛型总结

    原文地址https://www.cnblogs.com/lwbqqyumidi/p/3837629.html 一. 泛型概念的提出(为什么需要泛型)? 首先,我们看下下面这段简短的代码: 1 publ ...

  7. APP 自动化测试封装结构模式

    原文出处http://www.toutiao.com/a6268089772108333314/ 做过UI自动化测试同学,都会深深体会几个痛点:维护量大.适配量大.编写代码巨大等.基于这些问题,大家都 ...

  8. Python(常用模块)

    模块 模块 本质上就是一个.py文件 内置模块(解释器层面) 第三方模块(Python lib文件) 自定义模块(当前路径) 模块调用,包的概念 在计算机程序的开发过程中,随着程序代码越写越多,在一个 ...

  9. JavaScript:学习笔记(7)——VAR、LET、CONST三种变量声明的区别

    JavaScript:学习笔记(7)——VAR.LET.CONST三种变量声明的区别 ES2015(ES6)带来了许多闪亮的新功能,自2017年以来,许多JavaScript开发人员已经熟悉并开始使用 ...

  10. 设计模式(四) 手动实现AOP代理

    1.事务的使用: 每次对数据库操作我们都需要开启事务,事务开启后,我们就需要对数据库进行一次或者多次操作,当操作完成后就需要提交事务.比如一个业务中多次操作数据库,但是当某个方法出错的时候,我们需要整 ...