周末逛简书,看了一篇写的极好的文章,点击大红心点赞,就直接给我跳转到登陆界面了,原来点赞是须要登陆的。数据库
但是没有我并无简书帐号,一直使用的QQ的集成登陆。下面有一排社交登陆按钮,咱们能够用第三方社交帐号登录便可。点击QQ图标,就给我跳转到了QQ登陆受权页面,以下图:浏览器
从图片上咱们能够看到主要包括两个部分,一个是左边的用户登陆,一个是右边告知简书将获取哪些权限。输入QQ帐号和密码,点击受权并登陆,就成功登陆到简书了,并成功获取到了我QQ的帐号和昵称,以下图:安全
简书集成的社交登陆,大大简化了咱们的注册登陆流程,真是一号在手上网无忧啊。
这看似简单的集成,但背后的技术原理『OAuth2.0』可没那么简单,那咱们废话很少说,一探究竟吧。服务器
OAuth 2.0是用于受权的行业标准协议。OAuth 2.0取代了在2006年建立的原始OAuth协议上所作的工做。OAuth 2.0专一于客户端开发人员的简单性,同时为Web应用程序,桌面应用程序,手机和客厅设备提供特定的受权流程。cookie
在传统的client-server认证模型中,客户端请求访问服务器上受限的资源(protected resource),须要经过使用资源全部者(resource owner)的凭证在服务器上进行认证。为了支持第三方应用程序访问受限资源,资源全部者须要向第三方应用共享其凭证。这就会形成如下问题:网络
想想这样一个场景,若是简书是直接使用QQ用户名密码登陆,简书就颇有可能会为了后续业务的须要而擅自保存QQ用户名及密码,简书只要拿到了QQ用户名密码就能够访问不只仅QQ昵称、头像等信息,甚至能够获取到QQ用户的全部通信录列表。若是简书的帐号密码泄露,就会直接影响到QQ数据的安全。这是一个可怕的问题。session
因此OAuth应运而生,来解决这一问题。post
下面咱们就以简书使用QQ受权登陆为例,来捋一捋OAuth 2.0的流程。
先来看看OAuth 2.0的流程,以下图所示:
网站
这里面主要包含四个角色:ui
圣杰打开简书网页,简书跳转到登陆界面,要求用户登陆。但是圣杰未在简书注册账号,因此就点击了QQ图标,使用QQ账号进行集成登陆。跳转到QQ登陆界面后,QQ要求用户受权。
这一步中简书主要作了这样一件事就是引导用户到受权服务器。
很显然【QQ互联平台】就是受权服务器。
如何引导?固然是页面跳转。
那受权服务器如何知道是简书过来的认证请求?
固然是传参。
那须要传递哪些参数呢?
我们看看简书实际发送的受权请求Url是:
https://graph.qq.com/oauth2.0/authorize?client_id=100410602 &redirect_uri=http://www.jianshu.com/users/auth/qq_connect/callback &response_type=code &state=bb38108d1aaf567c72da0f1167e87142d0e20cb2bb24ec5a
无图无真相,我们看看控制台的网络监控:
如图所示,除了scope参数外,其余四个参数均有传参。
此时你可能惟一对state参数比较迷惑,传递一个state参数,受权服务器会原封不动返回,那还干吗要传递state参数呢?
个人理解是,简书用一个guid加长版字符串来惟一标识一个受权请求。这样才会正确获取受权服务器返回的受权码。
这里你可能会问了,既然我知道了这些参数,我岂不是能够伪造简书认证请求,修改redirect_uri
参数跳转到我的的网站,而后不就能够获取QQ受权?
跟我同样太傻太天真,简书在QQ互联平台申请时确定已经预留备案了要跳转返回的URL。QQ互联平台在收到简书的受权请求时确定会验证回调Url的。
这一步,对于用户来讲,只须要使用资源全部者(QQ)的用户名密码登陆,并赞成受权便可。点击受权并登陆后,受权服务器首先会post一个请求回服务器进行用户认证,认证经过后受权服务器会生成一个受权码,而后服务器根据受权请求的redirect_uri
进行跳转,并返回受权码code
和受权请求中传递的state
。
这里要注意的是:受权码有一个短暂的时效
无图无真相,我们仍是看一下控制台网络监控:
从图中便可验证咱们上面所说,最终跳转回简书的Url为:
http://www.jianshu.com/users/auth/qq_connect/callback?code=093B9307E38DC5A2C3AD147B150F2AB3 &state=bb38108d1aaf567c72da0f1167e87142d0e20cb2bb24ec5a
和以前的受权请求URL进行对比,能够发现redirect_uri
、state
彻底一致。
而code=093B9307E38DC5A2C3AD147B150F2AB3
就是返回的受权码。
从这一步开始,对于用户来讲是察觉不到的。简书后台默默的在作后续的工做。
简书拿到QQ互联平台返回的受权码后,须要根据受权码再次向受权服务器申请令牌(access token)。
到这里有必要再理清两个概念:
那如何申请令牌呢?
简书须要后台发送一个get请求到受权服务器(QQ互联平台)。
那要携带哪些必要信息呢?
是的,要携带如下参数:
根据以上信息咱们能够模拟一个申请AccessToken的请求:
https://graph.qq.com/oauth2.0/token?client_id=100410602 &client_secret=123456jianshu &redirect_uri=http://www.jianshu.com/users/auth/qq_connect/callback &grant_type=authorization_code &code=093B9307E38DC5A2C3AD147B150F2AB3
发送完该请求后,受权服务器验证经过后就会发放令牌,并返回到简书后台,其中应该包含如下信息:
一样,咱们能够模拟出一个返回的token:
http://www.jianshu.com/users/auth/qq_connect/callback?access_token=548ADF2D5E1C5E88H4JH15FKUN51F &expires_in=36000&refresh_token=53AD68JH834HHJF9J349FJADF3
这个时候简书还有一件事情要作,就是把用户token写到cookie里,进行用户登陆状态的维持。我们仍是打开控制器验证一下。
从图中能够看出简书把用户token保存在名为remember_user_token
的cookie里。
不用打cookie的歪主意了,确定是加密了的。
能够尝试下手动把remember_user_token
这条cookie删除,保证刷新界面后须要你从新登陆简书。
有了token,向资源服务器提供的资源接口发送一个get请求不就好了,资源服务器校验令牌无误,就会向简书返回资源(QQ用户信息)。
一样我们也来模拟一个使用token请求QQ用户基本信息资源的URL:
https://graph.qq.com/user/get_user_info?client_id=100410602 &qq=2098769873 &access_token=548ADF2D5E1C5E88H4JH15FKUN51F
到这一步OAuth2.0的流程能够说是结束了,可是对于简书来讲还有重要的事情要作。那就是:
拿到token、reresh_token和用户数据这么重要的东西不存数据库傻呀?
你确定对第四步返回的refresh_token
比较好奇。
它是用来对令牌进行延期(刷新)的。为何会有两种说法呢,由于可能受权服务器会从新生成一个令牌,也有可能
对过时的令牌进行延期。
好比说,QQ互联平台为了安全性考虑,返回的access_token
是有时间限制的,假如用户某天不想受权了呢,总不能给了个access_token
你几年后还能用吧。咱们上面模拟返回的令牌有效期为10小时。10小时后,用户打开浏览器逛简书,浏览器中用户的token对应的cookie已过时。简书发现浏览器没有携带token信息过来,就明白token失效了,须要从新向认证平台申请受权。若是让用户再点击QQ进行登陆受权,这明显用户体验很差。咋搞呢?refresh_token
就派上了用场,能够直接跳过前面申请受权码的步骤,当发现token失效了,简书从浏览器携带的cookie中的sessionid找到存储在数据库中的refresh_token
,而后再使用refresh_token
进行token续期(刷新)。
那用refresh_token进行token续期须要怎么作呢?
一样须要向受权服务器发送一个get请求。
须要哪些参数?
根据上述信息,咱们又能够模拟一个令牌刷新的URL:
https://graph.qq.com/oauth2.0/token?client_id=100410602 &client_secret=123456jianshu &redirect_uri=http://www.jianshu.com/users/auth/qq_connect/callback &grant_type=refresh_token &refresh_token=53AD68JH834HHJF9J349FJADF3
那返回的结果呢?
和第四步返回的结果同样。
这里你可能又有疑问了,那既然每次进行令牌延期后都会从新返回一个refresh_token
,那岂不是我可使用refresh_token
无限延期?
天真如我啊,refresh_token
也是有过时时间的。而这个过时时间具体是由受权服务器决定的。
通常来讲refresh_token
的过时时间要大于access_token
的过时时间。只有这样,access_token
过时时,才可使用refresh_token
进行令牌延期(刷新)。
举个简单例子:
假设简书从QQ互联平台默认获取到的access_token
的有效期是1天,refresh_token
的有效期为一周。
用户今天使用QQ登陆受权后,过了两天再去逛简书,简书发现token失效,立马用refresh_token
刷新令牌,默默的完成了受权的延期。
假如用户隔了两周再去逛简书,简书一核对,access_token
、refresh_token
全都失效,就只能乖乖引导用户到受权页面从新受权,也就是回到OAuth2.0的第一步。
本文以简书经过QQ进行受权登陆为例,对OAuth2.0 的受权流程进行了梳理,但愿通读此文,对你有所帮助。
若是对OAuth2.0有所了解的话,你应该明白本文实际上是对OAuth2.0中受权码模式受权方式的讲解。
若是想了解OAuth2.0其余几种受权方式,建议参考理解OAuth 2.0 - 阮一峰的网络日志。