OAuth是一个行业标准的授权协议, 包含一系列流程和标准。即第三方应用如何从平台上获取一些操作数据的授权方案
OAuth2标准参考RFC_6749文件说明
本文中基本都是文字说明,后续补充相关交互时序图。 本文只是整理下相关概念及规范,如想要通俗一点的可以看看阮一峰老师的文章:www.ruanyifeng.com/blog/2019/0…
场景示例
假设有这么个场景,你开发了一款游戏小程序,需要获取微信好友进行微信好友分数排名。获取好友信息,那么势必只能从微信平台获取,如何获取呢?
想当然的,用户将账号密码告诉游戏应用,应用使用账号密码登录获取token,通过token调用接口获取游戏好友列表,这一系列功能不就完成了!
会存在什么问题呢?
1.安全问题,用户需要完全信任这个应用,应用能拿到用户的微信账号密码信息,这是很危险的!应用滥用、信息泄露等各种问题就来了;
2.如果用户不想让这个应用继续获取好友列表怎么做呢?改密码!改密码又有一个问题,密码改了那么其他也是这么实现的应用也用不了了,所有应用都得重新登录;
3.权限管控问题,应用有了用户的微信账号密码,就很难限制应用能够用什么功能了;
4.对于微信平台来说,审核这些小程序的难度大,应用都有账号密码了就无法区分是小程序的操作还是用户的操作。出了问题也无法及时感知。
OAuth介绍
基于以上出现的问题,就出现了oauth;oauth就是针对如何安全地授权问题提供了授权标准流程规范。
RFC_6749文件摘要说明:
The OAuth 2.0 authorization framework enables a third-party application to obtain limited access to an HTTP service, either on behalf of a resource owner by orchestrating an approval interaction between the resource owner and the HTTP service, or by allowing the third-party application to obtain access on its own behalf.
OAuth 2.0授权框架允许第三方应用程序获得对HTTP服务的有限访问,可以代表资源所有者编排资源所有者与HTTP服务之间的审批交互,也可以允许第三方应用程序代表自己获得访问。
OAuth 引入了一个授权层,用来分离两种不同的角色:客户端和资源所有者。只有资源所有者同意以后,资源服务器才可以向客户端颁发令牌。客户端通过令牌,去请求数据。
它提供了四种授权方式,以适应互联网业务的多种场景
授权方式
名词说明:
第三方应用:类比为示例中的游戏小程序
应用认证(授权)平台:类比为示例中的微信/微信服务端
1.授权码(authorization code)方式
先让第三方应用想应用认证平台申请一个授权码,应用再通过授权码获取对应平台相关的数据访问令牌,拿到令牌后就可以使用令牌找应用平台获取数据了。 通俗一点说就是,游戏小程序再微信平台中上架了,通过微信颁发给自己的应用id和密钥申请一个授权码,用户同意后,微信将授权码返回给游戏小程序。至此游戏小程序就可以通过这个授权码获取这个用户的微信好友信息啦。
使用场景: 有后端的web应用,授权码可以通过前端透传给后端,后端通过这个授权码找认证平台获取访问应用平台数据的令牌,后端通过令牌与应用平台交互。
令牌存储在后端,安全性还是比较高的,在一定程度上避免了令牌的泄露。
1) 获取授权码请求示例:
HTTP/1.1 GET
/authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz&redirect_uri=http://abc.com/callback&scope=read
response_type:必填,参数表示需要返回数据类型,code为授权码类型
client_id:必填,客户端标识,即第三方应用id
redirect_uri:可选,告诉应用认证平台的服务端认证通过\失败后需要跳转的地址
state:可选,应用认证平台认证后重定向回客户端时包含此值(原封不动地返回),该参数可用于防止跨站点请求伪造;
scope:可选,允许第三方应用访问的请求范围。如此处read可以认为此第三方应用只有只读权限,不可写。
2) 认证平台授权通过后,跳回redirect_uri指定的地址:
HTTP/1.1 GET
http://abc.com/callback?code=123456&state=xyz
code即第三方应用所需的授权码。
3)第三方应用拿到授权码后发起获取令牌请求
HTTP/1.1 GET
/authorize?client_id=s6BhdRkqt3&client_secret=baF4ciWQbk6ldc23f&grant_type=authorization_code&code=123456&redirect_uri=CALLBACK_URL
client_id:客户端标识,即第三方应用id
client_secret:第三方应用secret
grant_type:说明是使用哪种授权方式来申请令牌的,authorization_code为code方式
code:上一步获取到的授权码
redirect_uri:接收令牌的回调地址,可选
response示例:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"access_token":"eyJ0XAiOiJKLCJhbGciOiJSUzI1NiJ9.eyJhcmbnRyeSI6IkNOIiwicG5hIjoiYWRtaW4iLCJqdCb2tlbI6I", //访问令牌
"refresh_token":"eyJ0XAiOiJKLCJhbGciOiJSUzI1NiJ9.RtaW4ihbnRyeAiOiJKLCJhbGciOiJSUzI1NiJ9bGciOiJSUzuJ9", //刷新token
"token_type":"bearer",
"expires_in":3600,
"scope":"read"
}
access_token:访问令牌
refresh_token:更新令牌有效期的刷新操作令牌
token_type:令牌认证类型
expires_in:访问令牌有效期
scope:权限范围
2.隐藏式授权(Implicit Grant)方式
RFC_6749指定的可以直接给前端的授权方式,主要给纯前端应用使用。以上面为例进入网页小程序时需要微信授权获取用户好友信息,网页小程序会跳转到微信授权界面,待用户点击同意授权后,微信授权服务器返回访问令牌并重定向到网页小程序中;此时网页小程序就可以根据当前页面的url上拿到对应的访问令牌。
1)发起授权请求
HTTP/1.1 GET
/authorize?response_type=token&client_id=s6BhdRkqt3&state=xyz&redirect_uri=http://abc.com/callback&scope=read
response_type:必填,参数表示需要返回数据类型,此处必须为token,意为直接返回令牌(access_token)
client_id:必填,客户端标识,即第三方应用id
redirect_uri:可选,告诉应用认证平台的服务端认证通过\失败后需要跳转的地址
state:可选,应用认证平台认证后重定向回客户端时包含此值(原封不动地返回),该参数可用于防止跨站点请求伪造;
scope:可选,允许第三方应用访问的请求范围。如此处read可以认为此第三方应用只有只读权限,不可写。
2)此时在授权服务器页面,点击同意授权;
统一后授权服务器就会将access_token拼接到redirect_uri上并重定向到redirect_uri指定的网址。
HTTP/1.1 302 Found
http://abc.com/callback#access_token=YotnFZFEjr1zCsicMWpAA&state=xyz&token_type=bearer&expires_in=3600
access_token:访问令牌
state:原请求授权时的state值
token_type:令牌认证类型
expires_in:访问令牌有效期
ps: 此方式不支持刷新token,所以没有refresh_token
可以发现此方式返回的信息是通过锚点(hash模式:#)的方式给的,也就是说‘access_token=YotnFZFEjr1zCsicMWpAA&state=xyz&token_type=bearer&expires_in=3600’这一串字符不会当成请求参数传给服务器,这就降低了中间人
攻击的从而泄露访问令牌的风险。
将令牌直接传给前端的方式是不安全的,所以隐藏式授权,只适用于安全性不高的场景,且访问令牌必须设置有效期较短。
3.密码式(Resource Owner Password Credentials Grant)授权
就是通过使用用户的账号密码去做认证,用户账号密码认证通过后返回访问令牌。此方式必须是针对高度信任的应用使用!
1)应用直接携带用户账号密码和应用自己的标识请求认证服务器授权
HTTP/1.1 GET
https://authorization.server.com/authorize?grant_type=password&username=USERNAME&password=PASSWORD&client_id=CLIENT_ID
2)认证服务器认证通过后直接http响应相关信息
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"access_token": "2YotnFZFEjr1zCsicMWpAA",
"token_type": "example ",
"expires_in": 3600,
"refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA"
}
4.凭证式授权(Client Credentials Grant)
通过签发给应用的相关标识(clientId、clientSecret)认证,适合纯后端或后端使用的应用。
1)请求认证服务器授权
HTTP/1.1 GET
/authorize?grant_type=client_credentials&client_id=CLIENT_ID&client_secret=CLIENT_SECRET
grant_type:说明是使用哪种授权方式来申请令牌的,client_credentials为凭证授权方式
client_id:客户端标识,即当前第三方应用id
client_secret:当前第三方应用秘钥
还有其他比较常见的做法是将client_id和client_secret通过一定算法算成一个签名或者token放在请求头中如:
HTTP/1.1 POST /authorize
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
timestamp: 1610544682145
nonce: aac64aa63454457294d440d8be191254
Body:grant_type=client_credentials
Authorization:将client_id和client_secret等相关信息通过认证系统提供的算法计算出来的认证信息,这种方式也适用于本文中请求参数存在client_id和client_secret的地方
拓展:
timestamp和nonce的作用:
防止请求重放。
什么是请求重放?如何防止?
请求重放是:重放攻击,属于中间人攻击的一种低级别版本;重放攻击就是中间人捕获了客户端的发送包,然后一直使用这个包重复地向服务端发起请求。
防止:满足以下两个条件
增加timestamp字段,表明客户端是什么时候发起的请求,服务端接收到后解析时间;只要请求时间和当前服务器收到包的时间在一定的误差范围内则认为请求合法
增加nonce标识,作为每次请求的唯一标识,如果服务器中没有发现此标识使用过,则认为请求合法;反之,则认为重复请求。
2)应用认证服务器认证通过后返回授权信息
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"access_token": "2YotnFZFEjr1zCsicMWpAA",
"token_type": "example ",
"expires_in": 3600,
"refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA"
}
refreshToken的使用
当访问令牌即将过期时,应用可以通过refresh_token发起更新令牌操作。有了refresh_token的操作,就没有必要再走一遍用户授权流程了,使用上更方便。
1)应用发起更新令牌请求
HTTP/1.1 GET
/authorize?grant_type=refresh_token&client_id=CLIENT_ID&client_secret=CLIENT_SECRET&refresh_token=REFRESH_TOKEN
grant_type:说明是使用哪种授权方式来申请令牌的,refresh_token为使用refresh_token获取新的认证令牌
OAuth 1.0与OAuth 2.0 差别
- OAuth 2.0是OAuth协议的下一版本,但不向下兼容OAuth 1.0(如果您今天创建新应用程序,请使用OAuth 2.0)。
- OAuth 2.0关注客户端开发者的简易性,同时为Web应用、桌面应用、手机和智能设备提供专门的认证流程,更快,更容易实现。
- OAuth 2.0有六个流程用于不同类型的应用程序和要求(梳理下来有四类Access Token授权方式),OAuth令牌不再需要在2.0中的端点上加密,因为它们在传输过程中已加密(通过HTTPS启用签名机制)。
参考文章:
www.ruanyifeng.com/blog/2019/0…
www.ruanyifeng.com/blog/2014/0…
www.ruanyifeng.com/blog/2019/0…