第一章.OAuth2.0 介绍
OAuth认证
OAuth认证是为了做到第三方应用在未获取到用户敏感信息(如:账号密码、用户PIN等)的情况下,能让用户授权予他来访问开放平台(主要访问平台中的资源服务器Resource Server)中的资源接口。
其流程主要是:
* 1.用户首先要保持登录,即已认证通过的状态
* 2.第三方应用请求用户授权(我理解是弹出一个显示的操作界面让用户确认给第三方授权)
* 3.用户授权成功之后会向Authorization Server(认证服务器)请求“授权码”(指authorization_code而不是最终的access_token),请求中还会携带redirect_uri(跳转至第三方应用的链接)
* 4.获得“授权码”之后用户所在的浏览器网页将跳转到redirect_uri(即第三方应用)
* 5.第三方应用携带“授权码”和应用认证信息(client_id & client_secret)到Authorization Server换取access_token
* 6.第三方应用就可以在访问开放平台时带上access_token
SSO单点登录
单点登录是指用户在某个应用系统上登录之后,进入其子应用或相关应用系统可以免去登录步骤,从而实现一次登录,到处操作。这次项目我所负责的正好是单点登录功能模块,所以来更新下实际项目中SSO的实现流程,我们要实现的效果是,现在有业务服务器a.com和b.com,还有SSO服务器sso.com,在a.com上完成登录操作之后访问b.com时是已登录状态。
* 1.用户首次访问a.com/user时,到a.com服务器获取不到session
* 2.给用户弹出一个SSO的登录页面sso.com/login.html,这个页面会带上两个参数,分别是开发者平台映射值key(即某个业务服务器在SSO所映射的key)和回调地址redirect(即登录成功之后跳转到的url,一般是所发起业务服务器开放的路由)
* 3.用户输入账号密码之后到SSO服务器上进行身份校验,若校验成功则给用户生成一个ticket,并将该ticket值及用户基本信息一起创建SSO服务器session
* 4.选择某种加密方式对该请求的session进行加密之后生成一个token值,然后在回调地址redirect里带上token参数并跳转回去a.com服务器。
* 5.a.com对token参数进行解析后拿到用户基本信息和ticket值,相应地初始化本地session并将ticket值也记录下来。
* 6.用户再次访问a.com/user或其他页面的时候,到a.com上获取session并到sso.com上校验ticket值得有效性,两个操作都成功之后才返回用户信息;若校验ticket值失败,则重新要求用户进行登录操作,即从步骤1开始。
* 7.现在用户在a.com上的登录和访问流程已完成,那这时用户访问b.com/user,到b.com上获取不到session,则会重定向到sso.com/session,因为之前a.com已在sso.com上登录过,故请求sso.com/session的时候SSO服务器能将session中用户信息和ticket值返回给b.com。
* 8.b.com获取到用户信息和ticket值之后和a.com做相同的事情即可,这样就完成了单点登录的整个流程。
SSO单点登出
单点登出则是指用户只要在a.com服务器上进行登出操作,则在其他业务服务器如b.com上也应处于未登录状态,单点登录流程的步骤6中提到每次用户访问业务服务器路由检验用户当前状态时不仅仅查找本地有没有session,还要到SSO服务器上校验ticket值的有效性,所以只要有一个业务服务器登出时令ticket失效即可。
* 1.现在用户是登录状态,他从a.com进行了退出操作,则自然a.com已将其session清除。
* 2.a.com服务器同时还要携带ticket值发请求到sso.com将对应的记录清除。
* 3.当访问某个b.com某个页面时,虽然在b.com服务器上获取到session,此外还需要到SSO服务器上校验一遍ticket值,而发现ticket值校验失败,则会提示用户当前处于未登录状态。
* 4.这样相当于b.com也已经是退出登录,完成了单点登出的整个流程
第二章.OAuth2.0更为具体的解释
一. OAutho2.0 介绍
第三方登录是应用开发中的常用功能,通过第三方登录,我们可以更加容易的吸引用户来到我们的应用中。现在,很多网站都提供了第三方登录的功能,在他们的官网中,都提供了如何接入第三方登录的文档。但是,不同的网站文档差别极大,各种第三方文档也是千奇百怪,同时,很多网站提供的SDK用法也是各不相同。对于不了解第三方登录的新手来说,实现一个支持多网站第三方登录的功能可以说是极其痛苦。
实际上,大多数网站提供的第三方登录都遵循OAuth协议,虽然大多数网站的细节处理都是不一致的,甚至会基于OAuth协议进行扩展,但大体上其流程是一定的。今天,我们就来看看基于OAuth2的第三方登陆功能是这样一个流程。
OAuth协议目前已经升级到了2.0,大部分的网站也是支持OAuth2.0的,因此让我们先看看OAuth2。

上图中所涉及到的对象分别为:
- Client 第三方应用,我们的应用就是一个Client
- Resource Owner 资源所有者,即用户
- Authorization Server 授权服务器,即提供第三方登录服务的服务器,如Github
- Resource Server 拥有资源信息的服务器,通常和授权服务器属于同一应用
根据上图的信息,我们可以知道OAuth2的基本流程为:
- 用户访问第三方应用。
- 第三方应用请求用户授权。(A)
- 用户同意授权,并返回一个凭证(code)。(B)
- 第三方应用通过第二步的凭证(code)向授权服务器请求授权。(C)
- 授权服务器验证凭证(code)通过后,同意授权,并返回一个资源访问的凭证(Access Token)。(D)
- 第三方应用通过第四步的凭证(Access Token)向资源服务器请求相关资源。(E)
- 资源服务器验证凭证(Access Token)通过后,将第三方应用请求的资源返回。(F)
二. OAuth2.0的具体实现
看完OAuth的基本流程后,大家实际上对到底如何做OAuth验证还是不太清楚,同时,也会产生疑问,为什么需要两次获取凭证,而不是直接用户授权拿到凭证后就直接获取资源呢?这和OAuth的具体实现有关,让我们来看看OAuth2的具体实现吧。
1.用户授权
在用户授权这一步中,我们将得到一个用户凭证(code),我们的应用可以通过该用户凭证(code)来和授权服务器交换一个资源访问凭证(Access Token)。
这里我们只讨论第三方登录功能的实现情况,经过分析,我们可以得出用户凭证(code)具有三个特性:
- 用户凭证(code)需要由用户授权,也就是说该行为是用户的主动行为, 此时为了保证用户身份正确,实际上也需要用户通过授权服务器的验证(登录操作或者已登录状态)
- 用户凭证(code)需要授权服务器颁发,因为最终用户凭证(code)是由授权服务器验证的,同时用户授权操作为了保证用户身份,也需要在授权服务器上进行操作,因此,用户凭证(code)是由授权服务器进行颁发的
- 用户凭证(code)需要由授权服务器传递给我们的第三方应用,很明显,由用户在资源服务器上拿到凭证再手动的提交至第三方应用太麻烦,因此,当用户主动授权行为发生后,资源服务器需要将用户凭证(code)发送给第三方应用。
我们以Github为例,看看主流的第三方登录是如何满足上述三个特性的。
第0步. 引导用户进行授权:
当用户希望使用第三方登录进行登录时,第三方应用会通过类似下图【快速登录】的方式将用户引导至授权页面,为了和OAuth基本流程一致,我们将这一步定义为第0步。

第0.5步. 用户身份验证
当我们点击Github的图标,我们会进入到Github应用下面,此时实际上进行了一次用户身份的验证,之前没有登录Github的用户需要输入Github的账号密码,已登录的用户Github会根据Session进行身份确认。此操作我们称为0.5步。接下来正式进入OAuth2的流程

第1步. 用户授权
用户身份确认后会进入下面这个页面,该页面由授权服务器提供,授权服务器会告诉用户该第三方在授权服务器中提交的相关信息(如果需要实现第三方登录功能,第三方应用需要向Github、微博等应用中提交应用的相关信息,不同服务可能会需要审核等不同的步骤),以及授权后第三方应用能够获取哪些资源。在Github中,最基础的认证可以访问用户的公共信息。如果用户同意授权,需要主动的点击【Authorize application】按钮。

第2步. 返回用户凭证(code)
当用户点击按钮同意授权后,授权服务器将生成一个用户凭证(code),此时授权服务器如何将用户凭证(code)传递给第三方应用呢?
当我们向授权服务器提交应用信息时,通常需要填写一个redirect_uri,当我们引导用户进入授权页面时,也会附带一个redirect_uri的信息(如https://github.com/login/oauth/authorize?client_id=XXX&redirect_uri=http%3A%2F%2Ftianmaying.com%2Foauth%2Fgithub%2Fcallback&state=XXX),当授权服务器验证两个URL一致时,会通知浏览器跳转到redirect_uri,同时,在redirect_uri后附加用户凭证(code)的相关信息,此时,浏览器返回第三方应用同时携带用户凭证(code)的相关信息。授权后访问的redirect_uri如下:
http://tianmaying.com/oauth/github/callback?code=9e3efa6cea739f9aaab2&state=XXX
这样,第二步返回用户凭证(code)就完成了。
2.授权服务器授权
从这一步开始,OAuth2 的授权将有两个重大变化的发生:
用户主动行为结束,用户理论上可以不需要再做任何主动的操作,作为第三方应用的我们可以在后台拿到资源服务器上的资源而对用户是不可见的,当用户的浏览器跳到下一个页面时,整个OAuth2的流程已经结束
浏览器端行为结束,从OAuth2 的基本流程可以看出,接下来的流程已经不需要和用户进行交互,接下来的行为都在第三方应用与授权服务器、资源服务器之间的交互。
我们知道浏览器端的行为实际上是不安全的,甚至安全凭证的传递都是通过URL直接传递的。但是由于用户凭证(code)不是那么敏感,其他攻击者拿到用户凭证(code)后依然无法获取到相应的用户资源,所以之前的行为是允许的。接下来我们看看服务器如何交互来安全的获得授权服务器授权。
第3步. 请求授权服务器授权
要拿到授权服务器的授权,需要以下几个信息:
* client_id 标识第三方应用的id,由授权服务器(Github)在第三方应用提交时颁发给第三方应用
* client_secret 第三方应用和授权服务器之间的安全凭证,由授权服务器(Github)在第三方应用提交时颁发给第三方应用
* code 第一步中返回的用户凭证redirect_uri 第一步生成用户凭证后跳转到第二步时的地址
* state 由第三方应用给出的随机码
我们看到,上述信息还涉及到第三方应用的安全凭证(client_secret),因此要求OAuth要求该请求必须时POST请求,同时,还必须时HTTPS服务,以此保证获取到的验证凭证(Access Token)的安全性。
第4步. 拿到验证凭证(Access Token)
当授权服务器拿到第3步中的所有信息,验证通过后,会将Access Token返回给第三方应用。
3.访问资源
第5步. 请求访问用户资源
拿到验证凭证(Access Token)后,剩下的事情就很简单了,资源服务器会提供一系列关于用户资源的API,拿验证凭证(Access Token)访问相应的API即可,例如,在GIthub中,如果你想拿到用户信息,可以访问以下API:
GET https://api.github.com/user?access_token=...
注意,此时的访问不是通过浏览器进行的,而是服务器直接发送HTTP请求,因此其安全性是可以保证的。
第6步. 返回资源
如果验证凭证(Access Token)是正确的,此时资源服务器就会返回资源信息,此时整个OAuth流程就结束了。
三. 第三方登录
看完OAuth2的资源访问流程,我们的第三方登录是如何做的呢?首先我们需要知道两点:
用户授权信息在授权服务器中是有记录的,当用户第一次授权给相应的第三方应用后,不需要进行再次授权
每个用户在资源服务器中都有一个唯一的ID,第三方应用可以将其存储起来并与本地用户系统一一对应起来
这样,当用户第一次授权并且注册后(主动注册或者第三方应用使用用户信息默认注册),当再次点击第三方登录的按钮,浏览器跳转到授权服务器,授权服务器通过Session找到用户的授权信息,发现该用户已经授权给该第三方应用,将直接跳转到redirect_uri,此时第三方应用通过唯一的用户ID找到相应的本地用户,自动帮助其登录。
这样,就可以达到直接点击第三方登录按钮,不需要任何操作,经过几次跳转后自动登录的效果了。