OAuth 2.0一词中的"Auth"表示"授权Authorization",字母"O"表示"开放Open",连在一起就表示"开放授权"。这也是为什么我们使用OAuth的场景,通常发生在开放平台的环境下。
OAuth 2.0提供了4种模式:
资源拥有者凭据许可(Resource Owner Password Credentials)
隐式许可(Implicit)
客户端凭据许可(Client Credentials)
授权码许可(Authorization Code)
本文将介绍OAuth2.0授权码许可的模式,授权码许可是应用最为广泛的开放授权模式,比如微信开放平台、阿里开放平台等都是基于这个模式做开放授权。
开放授权出现的意义
我们首先需要区分下认证和授权:
认证(Authentication):用来验证某个用户是否具有访问系统的权限。如果认证通过,该用户就可以访问系统,从而创建、修改、删除、查询平台支持的资源。认证的方式就是我们登录,比如账密登录、手机验证码登录等。
授权(Authorization):用来验证某个用户是否具有访问某个资源的权限,如果授权通过,该用户就能对资源做增删改查等操作。
简单而言,认证就是证明访问者的身份,决定他是否能进入系统;授权则是决定访问者能做哪些内容。一般来说,都是先认证后检查授权。
OAuth 诞生之初是为了解决 Web 浏览器场景下的授权问题。比如有一个网站A,在新用户注册时是支持QQ账号注册的,如果网站A能拿到QQ昵称和头像作为网站A用户注册的基本资料,这样新注册用户流程就简单多了,很多信息都不需要用户自己填写,直接套用QQ号的信息就可以了。这样的注册模式就对用户很友好,自然很容易提升用户注册的热情。
这个【访问用户QQ头像和昵称】就涉及到授权问题了。我的QQ信息为什么可以给网站A使用?如果我同意网站A获取我的QQ昵称,那我的QQ空间的信息网站A是否也可以访问呢?这就涉及到授权范围的内容了。开放授权是个很复杂的模式,需要一个安全且友好的机制来支持。
注册时的开放授权和长期访问QQ空间信息的开发授权的复杂度时不一样的。
注册时授权获取QQ昵称等信息是一次性的,需要考虑的事情可能没那么多,也许一个请求回复就能拿到这些信息了,以后不会再需要二次获取。但如果网站A还有个业务,支持管理QQ空间里的照片,此时需要向用户申请授权:请允许网站A可以访问你的QQ空间的照片。这种资源的访问就不是一次性了,是反复多次的访问,这个就更为复杂,首先我们不可能网站A每一次访问我们都点一次【同意授权】,如果我们当时没有登陆QQ,还得先账密登录QQ才可以进行授权。这样对用户体验很不友好。因此需要一种安全便捷的方式去处理这种频繁访问受限资源的场景。这个就是开放授权出现的意义:把私有资源有限地开放给第三方访问和使用,而且需要做到安全高效。OAuth 2.0能很好地解决了这个问题。
OAuth2.0 定义的角色类型
根据 OAuth 2.0 协议规范,OAuth 2.0体系下有四个角色类型:
授权服务器,负责颁发 Access Token,比如微信开放平台授权服务器。
资源所有者,你的应用的用户是资源的所有者,授权其他人访问他的资源。比如微信用户是资源所有者。
调用方,调用方请求获取 Access Token,经过用户授权后,微信开放平台为其颁发 Access Token。调用方可以携带 Access Token 到资源服务器访问用户的资源。比如调用方是上文说的网站A。
资源服务器,接受 Access Token,然后验证它的被赋予的权限项目,最后返回资源。比如微信开放平台资源服务器。
接下来,我们将以微信用户授权第三方网站A的案例,介绍OAuth2.0授权码模式下的授权流程。
OAuth2.0授权码模式流程
我们先看下业界标杆微信开放平台是怎么做开放授权的。网站应用微信登录是基于OAuth2.0协议标准构建的微信OAuth2.0授权登录系统,微信OAuth2.0授权登录目前支持authorization_code模式。
先上序列图:
流程步骤:
网站A需要在微信开放平台注册,填写回调地址,并获得相应的 AppID 和AppSecret。
用户登录了网站A,获得了网站A的登录态,这里用JWT来维护登录态。
此时网站A需要访问用户的微信相关资源,这里需要获得用户的微信授权。网站A弹出一个页面引导用户去微信开放平台的网站来授权。
用户点击【去授权】,浏览器跳转到微信二维码界面。
此时微信用户尚未在web端登录,因此需要微信用户【扫码登录】,登录成功后浏览器带着AppID请求微信开放平台,请求授权给网站A。
微信开放平台同意后返回页面让微信用户确认:网站A将获取以下权限:1. ... 2. ...,是否同意授权。
点击【同意授权】,用户浏览器就带着AppID和重定向URI两个参数去请求微信开放平台的授权服务器。重定向URI是网站A的地址。
微信开放平台的授权服务器收到请求,通过AppID知道了这个是要微信用户要给网站A授权,因此生成了一个授权码CODE,以重定向的方式返回给微信用户,重定向的地址填的是第7步请求带上来的URI,并参数带上这个CODE。
微信用户收到重定向的回复后,拿出重定向URI和CODE,重定向跳转到这个URI,并参数带上这个CODE。因为跳转的网址是网站A的域名地址,因为用户已经在该网站登录了,因此这个请求会带上登录态的JWT。
网站A收到微信用户CODE后,从JWT中解析出这个请求的来源用户的uid,这样网站用户uid就跟CODE有了绑定关系。接着拿出自己的AppID 和AppSecret,跟CODE一起请求微信开放平台授权服务器。
微信开放平台授权服务器收到AppID + AppSecre + CODE 三个参数后,生成access_token、refresh_token,返回这些token给网站A。
网站A拿到access_token后就存储起来,有了uid->access_token的映射关系。后续带着这个access_token就可以访问微信用户的指定资源了,无需每次要微信用户登录和授权。
access_token和refresh_token的关系
access_token是调用授权关系接口的调用凭证,由于access_token有效期(目前为2个小时)较短,当access_token超时后,可以使用refresh_token进行刷新,access_token刷新结果有两种:
若access_token已超时,那么进行refresh_token会获取一个新的access_token,新的超时时间;
若access_token未超时,那么进行refresh_token不会改变access_token,但超时时间会刷新,相当于续期access_token(注意,微信开放平台是在原来的access_token上做延时,有的开放平台是直接换一个新的access_token)。
refresh_token拥有较长的有效期(30天),当refresh_token失效后,需要用户重新授权。
因为access_token是需要频繁在网络上传输的,因此被窃取的风险大,所access_token的有效时间设置较短;refresh_token仅仅在续签access_token时会在网络上传输,被窃取的风险相对小点,因此refresh_token拥有较长的有效期。这样一长一短的双token机制会比较安全。
双Token机制更为安全,而且可以很好的处理在线、离线续签的问题,可以参考这个回答深入理解双Token机制。
jwt续签为什么要使用双token,没看明白啊,感觉单个token也可以啊? https://www.zhihu.com/question/506320859/answer/2913798899
47 赞同 · 19 评论回答
OAuth2.0授权码模式困惑点
为什么OAuth2里面在获取access token之前一定要先获取code,然后再用code去获取access token?
为什么OAuth2里面在获取access token之前一定要先获取code,然后再用code去获取access token? https://www.zhihu.com/question/27446826/answer/2916005175
15 赞同 · 2 评论回答
Token的续签是系统自动完成的吗?
续签是客户端写代码来实现自动更换的,比如客户端本地周期检查access_token是否即将过期或者已经过期,是的话就拿refresh_token去换新的access_token。
授权码被盗取后,人家不能也模拟服务器请求获取access_token吗?
首先授权码有以下基本的安全措施:授权码存在有效期,一般很短只有几分钟,过期了就没法用了;授权码只有一次使用机会,如果该授权码已经被人兑换过了,后面就没法兑换了。
如果是无appSecret的模式,授权码被盗取后是有机会被模拟盗窃access_token的。
在appSecret这种模式下,兑换access token,如果只有授权码是没法兑换access token,还得有appSecret+授权码来兑换。所以appSecret这种模式更为安全。
redirect_uri既然在注册的时候已经填了,为什么请求授权码时还要再传一遍这个参数?
因为注册时可能会填了多个uri,因此带参数redirect_uri请求时,授权服务器会校验是否该uri是否处于注册的uri列表中。
参考链接:https://zhuanlan.zhihu.com/p/610490265?s_r=0
标签:微信,用户,开放平台,access,token,详解,授权,OAuth2.0 From: https://www.cnblogs.com/leifonlyone/p/18391399