OAuth 2.0 理解

zjmcn2000 6年前
   <p>OAuth 是一个用于定于授权的开放标准,目前已经发展到了 2.0 的版本。它可以让用户允许第三方应用程序访问该用户存储在某一个网站或者系统上面的资源(比如照片,联系人)而不用提供密码给该第三方应用程序,并且还可以限制权限和有效期。OAuth 做到了一下这两点:</p>    <ul>     <li> <p>不需要将用户名和密码提供给第三方应用而是通过令牌让第三方应用访问资源</p> </li>     <li> <p>每一个令牌授权一个特定的应用在特定的时段内访问特定的资源</p> </li>    </ul>    <h2>应用场景</h2>    <p>OAuth 的应用场景遍布互联网应用的方方面面,最典型的就是微博。比如我们在开发一个第三方的微博客户端(下文简称为:客户端),需要可以登录用户并且操作用户的一系列微博信息。最简单的办法就是把微博账号密码告诉这个客户端,但是这样就会存在一系列的问题:</p>    <ul>     <li> <p>客户端为了后续的服务,会保存用户的密码,这样不安全</p> </li>     <li> <p>客户端拥有了客户微博的权力,用户没法限制授权的范围和有效期</p> </li>     <li> <p>用户只有修改密码,才能收回权限,但是这样会使得其他的第三方应用程序失效</p> </li>    </ul>    <p>除了这些还会导致只要有一个第三方应用程序被破解,就会导致用户密码泄漏,以及所有被密码保护的数据泄漏。而且 OAuth 就是来解决这个问题的。</p>    <h2>理解 OAuth</h2>    <p>在理解 OAuth 之前,需要说明几个名词:</p>    <ul>     <li> <p>Client:第三方应用程序,就是本文中的客户端</p> </li>     <li> <p>Resource owner : 资源所有者,本文中称为"yonghu"。</p> </li>     <li> <p>Authorization server :认证服务器,服务提供商专门用来处理认证的服务器</p> </li>     <li> <p>Resource server:资源服务器,服务提供商存放用户生成的资源的服务器,它和认证服务器,可以是同一台服务器,也可以是不同的服务器。</p> </li>    </ul>    <p>来看看流程图:</p>    <p><img src="https://simg.open-open.com/show/76babf113bb4e79a73976016686d6687.jpg"></p>    <p>整个授权流程按照顺序分为六个步骤:</p>    <ul>     <li> <p>用户打开客户端以后,客户端要求用户给予授权</p> </li>     <li> <p>用户同意授权</p> </li>     <li> <p>客户端使用上一步获得的授权,向认证服务器申请令牌</p> </li>     <li> <p>认证服务器对客户端进行认证以后,确认无误并且发放令牌</p> </li>     <li> <p>客户端使用令牌向资源服务器申请获取资源</p> </li>     <li> <p>资源服务器确认令牌无误同意向客户端开放资源</p> </li>    </ul>    <h2>授权模式</h2>    <p>OAuth 2.0 总共定义了 4 中授权模式,分别是以下四种:</p>    <ul>     <li> <p>授权码模式</p> </li>     <li> <p>简化模式</p> </li>     <li> <p>密码模式</p> </li>     <li> <p>客户端模式</p> </li>    </ul>    <h2> </h2>    <p>授权码模式</p>    <p>其中授权码模式是功能最完整,流程最严密的授权模式。授权流程图:</p>    <p><img src="https://simg.open-open.com/show/0b1bb798a8241b1808344efc53240306.png"></p>    <p>首先用户打开客户端应用,这时候客户端要求进行授权并且会跳转到授权页面:</p>    <p><img src="https://simg.open-open.com/show/693aefb8a7ec55ea6cdd02605a7d1518.jpg"></p>    <p>我们可以看到跳转到的授权 URL 是这样的:</p>    <ul>     <li> <p>https://api.weibo.com/oauth2/authorize :认证服务器</p> </li>     <li> <p>client_id :客户端的 client id,用于给认证服务器识别该客户端</p> </li>     <li> <p>redirect_uri :获得授权码之后,认证服务器重定向用户代理(比如浏览器)的地址</p> </li>     <li> <p>response_type : 表明授权类型,默认是 code,即授权码模式</p> </li>     <li> <p>state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值,用于抵御 CSRF 攻击</p> </li>    </ul>    <p>这时候用户同意进行授权,认证服务器将用户跳转到客户端事先指定的 redirect_uri, 同时附上一个授权码</p>    <p>客户端收到授权码,附上之前的 redirect_uri 向认证服务器申请令牌,这一步是在客户端的后台服务器上完成的,对用户不可见。比如:</p>    <ul>     <li> <p>https://api.weibo.com/v1/oauth2/authorize :获取 access_token 的服务器地址</p> </li>     <li> <p>client id :client id 用于验证应用程序。</p> </li>     <li> <p>client secret:client secret 用于验证应用程序。</p> </li>     <li> <p>grant_type :刚刚获得的授权码</p> </li>     <li> <p>redirect_uri :重定向URI,和第一步一致</p> </li>    </ul>    <p>认证服务器确认无误后,向客户端发送访问令牌和更新令牌:</p>    <p>这个返回的包括了以下这些,有些还有包含一个权限范围 scope,这里不撰述:</p>    <ul>     <li> <p>access_token:访问令牌</p> </li>     <li> <p>token_type :令牌类型</p> </li>     <li> <p>expires_in :过期时间,单位为秒</p> </li>     <li> <p>refresh_token: 更新令牌,用来获取下一次的访问令牌</p> </li>    </ul>    <h2> </h2>    <p>简化模式</p>    <p>简化模式不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了授权码这个步骤,所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。这种模式一般是用于手机应用,桌面客户端应用程序和运行于浏览器上的 Web 应用程序, 授权令牌会交给用户代理,再由用户代理交给应用程序。如下图:</p>    <p><img src="https://simg.open-open.com/show/8909c4982eecbf5124076936adb44bf6.jpg"></p>    <ul>     <li> <p>首先用户访问客户端,客户端将用户导向认证服务器, 并且用户选择同意授权:</p> </li>    </ul>    <ul>     <li> <p>认证服务器将用重定向客户端事先指定的 URI,并且在 URI 的 hash 部分包含了访问令牌:</p> </li>    </ul>    <ul>     <li> <p>浏览器向资源服务器发出请求,其中不包括上一步收到的 hash 值</p> </li>     <li> <p>资源服务器返回一个网页,其中包含的代码可以获取 hash 值中的令牌</p> </li>     <li> <p>浏览器执行上一步获得的脚本并且取出令牌</p> </li>     <li> <p>浏览器将令牌发送给客户端</p> </li>    </ul>    <h2> </h2>    <p>密码模式</p>    <p>用户向客户端提供用户名密码,客户端使用这些信息,向 服务商提供商 索要授权。在这种模式下,客户端不得存储密码。 <strong>这通常用在用户对客户端高度可信的情况下</strong> , 一般认证服务器只有在其他授权模式无法执行的情况下,并且客户端是由某一个著名公司出品才能考虑使用这种模式。如下图:</p>    <ul>     <li> <p>用户向客户端提供用户名和密码</p> </li>     <li> <p>客户端将用户名和密码发给认证服务器,向后者请求令牌</p> </li>     <li> <p>认证服务器确认无误后,向客户端提供访问令牌</p> </li>    </ul>    <h2> </h2>    <p>客户端模式</p>    <p>客户端使用自己的名义而不是用户的名义,向服务提供商进行认证。在这种模式下,用户直接向客户端注册,客户端以自己的名义要求服务提供商提供服务,其实并不存在授权。如下图:</p>    <p><img src="https://simg.open-open.com/show/c08d5aaded931318e77d4b77598b88af.jpg"></p>    <p>对于这种方式,用在访问一些和用户无关的公共接口,比如一些首页数据,这些数据和用户无关,但是又不想任何人都可以调用这个 Api,那么就可以采用这种模式。</p>    <h2>刷新令牌</h2>    <p>认证服务器颁发的令牌是有有效期限制的,所以客户端需要自行去刷新令牌。如果用户访问的时候,客户端的访问令牌已经过期,则需要使用更新令牌申请一个新的访问令牌。客户端通过一个POST 请求并且携带上参数:</p>    <p><img src="https://simg.open-open.com/show/43787bfb31412097421e375fb28f998a.jpg"></p>    <ul>     <li> <p>granttype:表示使用的授权模式,此处的值固定为 refreshtoken,必选</p> </li>     <li> <p>refresh_token:表示早前收到的更新令牌,必选</p> </li>     <li> <p>scope:表示申请的授权范围,不可以超出上一次申请的范围,如果省略该参数,则表示与上一次一致</p> </li>    </ul>    <p> </p>    <p>来自:http://mp.weixin.qq.com/s/hzy8gV0eDdumg0TD5mi5cA</p>    <p> </p>