1.前言
前段时间公司取消了Android岗,在面临失业没有饭碗天天吃土的压迫下,我转岗变成JAVA工程师。虽然JAVA并不陌生,但在陌生技术的面前,还是向大佬们低下了头。
也有人会问为啥不换份工作。自己也问过自己为什么?这份工作挣得少,变化多,不稳定。但是却能每次让我面临新的挑战,不论是转岗JAVA还是前端,就是这种挑战的感觉让我无法停止,如果有一天公司不会再让我吸收到养分,那应该就是我离开的一天。
不多废话了,因为最近要对接另一个公司的接口,面临权限的问题,领导让我调研了下,跟大家也分享下。
2.概念
2.1 OAuth2.0是什么?
OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版。
OAuth2.0是OAuth协议的下一版本,但不向后兼容OAuth 1.0即完全废止了OAuth1.0。 OAuth 2.0关注客户端开发者的简易性。
那我们该怎么使用协议?协议给我们定义了流程和参数,只要我们双方都遵循这个协议,那就算使用实现了该协议。
2.2 OAuth2.0做什么?
目前很多开放平台如新浪微博开放平台都在使用提供开放API接口供开发者使用,随之带来了第三方应用要到开放平台进行授权的问题,OAuth就是干这个的。
举个栗子,咱们浏览百度的时候需要登陆,可以用微信账号登录,那这时候微信给百度的账号信息就可以通过授权(OAuth2.0)来实现了。
2.3 关键字
这部分大家需要记牢,在后面的流程中多次涉及。
- client:第三方应用程序,客户端(百度)
- HTTP service:HTTP服务提供商,资源信息提供方(微信)
- Resource Owner:资源所有者,用户(有微信账号的百度使用者)
- User Agent:用户代理,浏览器
- Authorization server:认证服务器,即服务提供商(微信)专门用来处理认证的服务器。
- Resource server:资源服务器,即服务提供商(微信)存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同的服务器。
2.4 运行流程
(A)用户打开客户端以后,客户端要求用户给予授权。
(B)用户同意给予客户端授权。
(C)客户端使用上一步获得的授权,向认证服务器申请令牌。
(D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
(E)客户端使用令牌,向资源服务器申请获取资源。
(F)资源服务器确认令牌无误,同意向客户端开放资源。
以上是OAuth2.0的运行流程,其中ABCD如何授权是咱们关注的主要流程。
3.客户端的授权模式
在上节我们明白了OAuth2.0的运行流程,那咱们这节主要看一下如何获取授权的方法。
客户端必须得到用户的授权(authorization grant),才能获得令牌(access token)。OAuth 2.0定义了四种授权方式。
- 授权码模式(authorization code)
- 简化模式(implicit)
- 密码模式(resource owner password credentials)
- 客户端模式(client credentials)
上面的四种方法只有授权码模式应用广泛,所以本文只讲授权码模式,其余三个会在下面给大家链接,有兴趣的可以查看。
3.1 授权码模式运行流程
以下我们还是采用用微信账号登录百度的栗子讲解。(括号中均为举例)
(A)用户访问客户端,后者将前者导向认证服务器。(当我们在百度客户端点击微信登录,会在百度客户端中跳转到微信授权页面,询问用户是否同意授权。显示的授权URI是微信提供的,其中带有部分参数,包括授权成功后的重定向URI,该重定向URI是百度提供的。)
(B)用户选择是否给予客户端授权。
(C)假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。(在A步骤中,我们传递了重定向URI,那么微信授权页面就知道授权成功后,应该跳转到该重定向URI,并向百度传递授权码。该步骤在浏览器中可见。)
(D)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。(百度拿着授权码和一些参数,去微信申请访问令牌。)
(E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。(微信返回给百度访问令牌和更新令牌,百度就能拿着访问令牌去访问微信的信息了。)
3.2 授权码模式所需参数
该步骤对应3.1的步骤
(A)访问授权页面URI
- response_type:表示授权类型,此处的值固定为"code",必选项
- client_id:表示客户端的ID,在授权方的开放平台申请的appid,是双方的一个身份证明,必选项
- redirect_uri:表示重定向URI。当用户同意授权,服务商回应客户端的uri,可选项建议写。
- scope:表示申请的权限范围,可选项
- state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。
例子:
/authorize ? response_type=code & client_id=s6BhdRkqt3 & state=xyz & redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
(C)服务商给重定向URI传回Code
C步骤中,假设用户同意授权,服务商会访问重定向URI,并包含以下参数:(用户同意用微信账号登录百度,微信访问百度给的重定向URI,并拼上参数)
- code:表示授权码,必选项。该码的有效期应该很短,通常设为10分钟,客户端只能使用该码一次,否则会被授权服务器拒绝。该码与客户端ID和重定向URI,是一一对应关系。(微信给百度的授权码)
- state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。
例子:
https://client.example.com/cb ? code=SplxlOBeZQQYbYS6WxSbIA & state=xyz
(D)用Code换Token
在C步骤中拿到Code之后,客户端(百度)向认证服务器(微信)申请令牌的HTTP请求,包含以下参数:
- grant_type:表示使用的授权模式,必选项,此处的值固定为"authorization_code"。
- code:表示上一步获得的授权码,必选项。
- redirect_uri:表示重定向URI,必选项,且必须与A步骤中的该参数值保持一致。(这个参数会做一个验证)
- client_id:表示客户端ID,在授权方开放平台申请的appid,必选项。(百度在微信开放平台申请的appid)
- client_secret:表示客户端Secret,在授权方开放平台申请的secret,必选项。(百度在微信开放平台申请的secret)
注意: 该步中的信息一定要保证正确,且只有该步骤可以暴露secret,因为不会暴露给外网。
(E)返回Token格式
E步骤中,认证服务器发送的HTTP回复,包含以下参数:
- access_token:表示访问令牌,必选项。
- token_type:表示令牌类型,该值大小写不敏感,必选项,可以是bearer类型或mac类型。
- expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
- refresh_token:表示更新令牌,用来获取下一次的访问令牌,可选项。
- scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。
例子:返回值采用JSON格式
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
3.3 appid和secret的存在意义
这两个参数都是从授权方的开放平台申请获取的。
如果 appid 是为了告诉身份认证服务器,『我是 百度』;那么 secret 是为了告诉身份认证服务器,『我真的是 百度』。A步骤中,为了告诉身份认证服务你是哪家第三方服务,你总是需要暴露一些信息(appid)给身份认证服务器的(暴露是指用户能获取到,比如会在地址栏里出现)。
获取 code 的时候,那能暴露的只能是 appid,但如果数据交互可以不暴露给用户,比如获取 access token 那一步,D步骤,由第三方服务器内部直接发起,用户并不可见,那会带上 secret。secret 为什么叫 secret,就因为他绝对不能暴露给外网。secret其实就是第三方网站与 OAuth 服务网站之间的信物。此信物是一定一定不能被第三者知道的。如果知道了一定要第一时间重新生成。
3.4 Code的存在意义
可能看到这,大家会问,Code有必要存在吗?我为什么不能直接申请token呢?
OAuth 2.0 当初设计的一个目标之一是,让不支持 https 的网站也能安全使用。既然提到了 https,必然跟中间人攻击有关系。
我们先举个栗子:
我们先假设 OAuth 不需要整什么 code,就直接获取 access token,那么流程就是(还是拿 baidu.com 和微信举例):
用户浏览器访问 baidu.com,baidu.com 服务器发现用户处于未登录状态,返回 302,让浏览器跳转到微信 OAuth 服务获取 access token,(假设为 https://wx.qq.com/token?appid=xxx&redirect_uri=http://chrisyue.com&scope=...)
用户在微信的网页上完成了账号密码的输入并登录成功(或者已经登录授权成功),微信服务器也返回 302,让浏览器跳转到 redirect_uri 指定的地址并且带上 access token 参数。
用户浏览器访问带 access token 的链接,完成整个登录。
此流程没大毛病,就是最后一步,如果 chrisyue.com 是不支持 https 访问的,那么 access token 就等于是暴露在浏览器和 chrisyue.com 服务器之间的线路中。
当然,从另外一方面来说,如果第三方网站强行要求必须支持 https,理论上来说,code 这一步也是可以省的。
可能也有人会问,那多一步获取 code 有什么用呢?如果 baidu.com 不支持 https,code 不也是会被暴露吗?攻击者拿到 code,类似与之前讨论 redirect_uri 是否可以不检查域名时所做的一样,直接用获取到的 code,访问 baidu.com 获取 access token 的地址来登录受害人的账号。针对这个问题,OAuth2.0 协议其实对此是有处理方式的:
- code 只能被使用一次。
- 若是攻击者比正常用户先用了 code 也没事,因为如果同一个 code 被用了两次,之前通过此 code 获取的 access token 将被撤回,而因为普通用户本来就是要访问拿 code 换 access token 的地址,code 是一定会被用的
也就是说,攻击者最多让正常用户有点困扰,可能会出现登录意外失败,或者明明看起来登录成功但还是获取不到用户信息的情况(access token 已经失效),但攻击者依旧拿不到数据。
4.资料
4.1 大佬的教学文章
以下文章没有先后,会让大家更深入了解OAuth2.0,如果大家不喜欢我这篇,以下几篇足够用了:
理解OAuth 2.0
OAuth2集成——《跟我学Shiro》
关于 OAuth2.0 安全性你应该要知道的一些事
4.2 微博开放平台API
现在微博,微信,支付宝等用的都是OAuth2.0协议,给大家拿微博的API看下,可以确认自己学的不是假的。(/ω\)
- 微博开放平台授权API
- A步骤:授权页面API
- D步骤:获取tokenAPI
5.总结
本篇文章我们围绕着:
- OAuth2.0是什么?做什么?
- 关键字与运行流程
- 四种授权模式(授权码模式)
- Code的作用
以上四个方面对OAuth2.0进行了学习,希望大家读完这篇文章,会对OAuth2.0有一个更深入的了解。如果我的文章能给大家带来一点点的福利,那在下就足够开心了。
下次再见!