什么是 OAuth2 协议?
互联网上的一款安全协议
OAuth协议为用户资源的授权提供了一个安全的、开放而又简易的标准。
OAuth主要有OAuth1.0a和OAuth2.0两个版本,并且二者完全不同,且不兼容。OAuth2.0是目前是市面上最广泛使用的版本
如下图所示,第三方登录就是 Oauth2 的一种应用场景
为什么要有 OAuth2?
随着时代的发展,对于企业而言,可能会遇到下面这些问题
- 不同服务之间的分别部署,用户的登录问题,如果让用户在不同服务上注册账号密码,过于麻烦
- 第三方程序接入提供信息的问题,如何确定授权范围(授权的范围)
- 如果选择将用户账号密码暴露给第三方或不同服务,如何解决用户修改账号密码授权失效的问题
……
为了解决上面的问题,OAuth2 诞生了
OAuth2 的四种授权模式
授权码模式
授权码模式(authorization code)是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与” 服务提供商” 的认证服务器进行互动。分为下述步骤:
(A)用户访问客户端,后者将前者导向认证服务器。
(B)用户选择是否给予客户端授权。
(C)假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。
(D)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。
(E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。
授权码模式是最繁琐的模式,也是安全系数最高的模式,因此市面上最常见的就是该模式 `
简化模式(隐式授权)
简化模式(implicit grant type)不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了 “授权码” 这个步骤,因此得名。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。分为下述步骤:
(A)客户端将用户导向认证服务器。
(B)用户决定是否给于客户端授权。
(C)假设用户给予授权,认证服务器将用户导向客户端指定的"重定向URI",并在URI的Hash部分包含了访问令牌。
(D)浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值。
(E)资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。
(F)浏览器执行上一步获得的脚本,提取出令牌。
(G)浏览器将令牌发给客户端。
简化模式一般用于没有后端应用的时候,常见于第三方单页面应用。例如调查问卷等
密码模式(资源所有者密码凭证)
密码模式(Resource Owner Password Credentials Grant)中,用户向客户端提供自己的用户名和密码。客户端使用这些信息,向 “服务商提供商” 索要授权。分为下述步骤:
(A)用户向客户端提供用户名和密码。
(B)客户端将用户名和密码发给认证服务器,向后者请求令牌。
(C)认证服务器确认无误后,向客户端提供访问令牌。
客户端模式(客户端凭证)
客户端模式(Client Credentials Grant)指客户端以自己的名义,而不是以用户的名义,向 “服务提供商” 进行认证。严格地说,客户端模式并不属于 OAuth 框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以自己的名义要求 “服务提供商” 提供服务,其实不存在授权问题。分为下述步骤:
(A)客户端向认证服务器进行身份认证,并要求一个访问令牌。
(B)认证服务器确认无误后,向客户端提供访问令牌。
授权模式类比
授权模式 | 优点 | 缺点 | 应用场景 |
---|---|---|---|
授权码模式 | 安全性较高 功能最完整 流程最严密 | 需要额外的交互步骤 客户端需要存储和管理授权码 | 适用于有自己的服务器的应用 例如公网的开放平台 |
简化模式 | 流程简单 | 安全性较低 访问令牌容易泄露且不可刷新 | 适用于无后端服务的纯前端应用 例如纯静态页面应用 |
密码模式 | 简单易用 无额外的交互步骤 | 安全性较低 用户的账号和密码直接暴露给客户端 | 资源所有者与客户端具有良好信任关系的场景 例如客户端是设备的操作系统或具备高权限的应用 |
客户端模式 | 简单易用 | 安全性较低 不涉及用户的身份验证 | 适用于客户端需要访问自己拥有的资源的场景 例如移动应用访问自己的后台服务器 ,或者在微服务架构中服务之间的调用 |
OAuth2 实际应用:如何实现微信第三方登录
授权码模式
1. 注册应用
在 https://open.weixin.qq.com/ 网站注册应用,以获取 app_id 以及 app_secret
2. 前端接收 code
流程
1)用户点击页面上的第三方登录,拼接好对应的路由,并在浏览器中打开
拼接的路由结构如下
参数说明
参数 | 是否必须 | 说明 |
---|---|---|
appid | 是 | 应用唯一标识(前面认证网页应用中获得) |
redirect_uri | 是 | 重定向地址,需要进行 UrlEncode(前面认证网页应用中获得) |
response_type | 是 | 填 code |
scope | 是 | 应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写 snsapi_login 即可 |
state | 否 | 用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止 csrf 攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加 session 进行校验 |
用户进行扫描,微信会进行验证,并返回到回调的 url 上,所以前端要监听对应的回调地址,判断微信是否返回
接受微信返回的 code,并发送给后端
3. 后端接受凭证并通过 code 获取 access_token
流程
1)后端拼接路由并发送请求
路由结构如下
参数说明
参数 | 是否必须 | 说明 |
---|---|---|
appid | 是 | 应用唯一标识,在微信开放平台提交应用审核通过后获得 |
secret | 是 | 应用密钥 AppSecret,在微信开放平台提交应用审核通过后获得 |
code | 是 | 填写第一步获取的 code 参数 |
grant_type | 是 | 填 authorization_code |
2)接收微信返回的信息
ex:
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE",
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
参数说明
参数 | 说明 |
---|---|
access_token | 接口调用凭证 |
expires_in | access_token 接口调用凭证超时时间,单位(秒) |
refresh_token | 用户刷新 access_token |
openid | 授权用户唯一标识 |
scope | 用户授权的作用域,使用逗号(,)分隔 |
unionid | 当且仅当该网站应用已获得该用户的 userinfo 授权时,才会出现该字段。 |
4. 通过 access_token 获取用户信息
流程
1)拼接路由并发送请求
https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID
参数说明
参数 | 是否必须 | 说明 |
---|---|---|
access_token | 是 | 调用凭证(上一个请求中获得) |
openid | 是 | 普通用户的标识,对当前开发者帐号唯一(上一个请求中获得) |
lang | 否 | 国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语,默认为 zh-CN |
2)接收返回信息
{
"openid":"OPENID",
"nickname":"NICKNAME",
"sex":1,
"province":"PROVINCE",
"city":"CITY",
"country":"COUNTRY",
"headimgurl": "http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
"privilege":[
"PRIVILEGE1",
"PRIVILEGE2"
],
"unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
参数说明
参数 | 说明 |
---|---|
openid | 普通用户的标识,对当前开发者帐号唯一 |
nickname | 普通用户昵称 |
sex | 普通用户性别,1 为男性,2 为女性 |
province | 普通用户个人资料填写的省份 |
city | 普通用户个人资料填写的城市 |
country | 国家,如中国为 CN |
headimgurl | 用户头像,最后一个数值代表正方形头像大小(有 0、46、64、96、132 数值可选,0 代表 640*640 正方形头像),用户没有头像时该项为空 |
privilege | 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom) |
unionid | 用户统一标识。针对一个微信开放平台帐号下的应用,同一用户的 unionid 是唯一的。 |