一什么是 OAuth 2.0
简单的说, OAuth 2.0 是个授权框架它定义了第三方应用如何通过用户授权, 来访问用户的受限资源
举个例子, 个人网站要支持微信账号登陆, 微信开放平台的授权登陆就用到了 OAuth 2.0
OAuth 2.0 涉及的关键参与方有:
Resource Owner: 资源所有者这里指微信用户
Third-party application: 第三方应用这里指个人网站
Authorization server: 授权服务器这里指微信开放平台的授权服务
Resource server: 资源服务器, 用来存储获取用户资源这里指的是微信开放平台的服务器
二 OAuth 2.0 基本流程
OAuth 2.0 主要包含两个关键步骤:
第三方应用取得用户授权
第三方应用访问用户资源
其中, 取得用户授权是流程重点, 最终取得的授权凭证叫做 access token 如下图所示:
如上图所示, access token 的获取分为两步:
获取授权码 code, 这是临时授权凭证: 步骤 ABCD
通过 code 交换 access token, 这是正式授权凭证: 步骤 EF
获取 access token 的细节是本文重点, 下一节会进行介绍
三如何获取 access token
有多种方式可以获取 access token, 这里主要介绍最常见授权码模式 (Authorization Code Grant)
授权码模式 流程如下:
跳过具体细节, 看下各步骤具体做了什么:
步骤 AB: 第三方应用取得用户授权
步骤 C: 第三方应用取得授权码 (authorization code)
步骤 D: 第三方应用请求授权凭证 (access token)
步骤 E: 第三方应用获得授权凭证 (access token)
User-Agent: 前端开发的同学应该不陌生, 大部分时候指的就是浏览器
接下来, 稍微详细点讲解各个步骤:
1 请求用户授权
第三方应用, 将资源所有者导向一个特定的地址, 并在地址里带上如下信息:
response_type: 必选, 请求类型这里固定为 "code"
client_id: 必选, 标识第三方应用的 id 很多地方也用 apppid 来代替
redirect_uri: 可选, 授权完成后重定向的地址当取得用户授权后, 授权服务会重定向到这个地址, 并在地址里带上授权码
scope: 可选, 第三方请求的资源范围比如是想获取基本信息敏感信息等
state: 推荐, 用于状态保持, 可以是任意字符串授权服务会原封不动地返回
对于 redirect_uri 是可选的, 大家可能会有疑惑在实际中, redirect_uri 一般在应用后台就完成了填写和验证, 因此可以是选填的
2 用户授权返回
资源所有者, 同意授权第三方应用访问受限资源后, 请求返回, 跳转到 redirect_uri 指定的地址
地址中带了如下信息:
code: 必选, 授权码后续步骤中, 用来交换 access token
state: 必选 (如果授权请求中, 带上了 state), 这里原封不动地回传
3 请求 access token
第三方应用, 向授权服务请求获取 access token 请求参数包括:
grant_type: 必选, 许可类型, 这里固定为 authorization_code
code: 必选, 授权码在用户授权步骤中, 授权服务返回的
redirect_uri: 必选, 如果在授权请求步骤中, 带上了 redirect_uri, 那么这里也必须带上, 且值相同
client_id: 必选, 第三方应用 id
4 返回 access token
请求合法且授权验证通过, 那么授权服务将 access token 返回给第三方应用
关键返回字段:
access token: 必选, 访问令牌, 第三方应用访问用户资源的凭证
expires_in: 推荐, access token 的有效时长
refresh token: 可选, 更新 access token 的凭证当 access token 过期, 可以 refresh token 为凭证, 获取新的 access token
例子如下:
- HTTP/1.1 200 OK
- Content-Type: application/json;charset=UTF-8
- Cache-Control: no-store
- Pragma: no-cache
- {
- "access_token":"2YotnFZFEjr1zCsicMWpAA",
- "token_type":"example",
- "expires_in":3600,
- "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
- "example_parameter":"example_value"
- }
四以微信授权为例
以微信开放平台统一登录为例, 更多细节可参考 官方文档
下图为微信统一登录的时序图:
步骤分解如下:
1 请求用户授权: 步骤 234
带上 appidredirect_uriresponse_typescopestate 其中:
appid: 应用 id, 就是前面提到的 client_id
redirect_uri: 授权回调的地址, 在微信管理后台填写
response_type: 响应类型, 固定为 "code"
scope: 授权许可范围, 固定为 "snsapi_login"
state: 可选, 授权服务回传
2 用户授权返回: 步骤 5
用户同意授权, 重定向到 redirect_uri, 并返回临时票据 code 如下所示:
redirect_uri?code=CODE&state=STATE
3 请求 access token
应用拿到临时票据后, 用临时票据去换取真实票据 access token 所需参数如下:
appid: 必选, 应用 id
secret: 必选, 应用秘钥, 在微信后台生成
code: 必选, 前面获取的授权码
grant_type: 必选, 值固定为 "authorization_code"
4 返回 access token
微信后台经过验证, 确认请求合法后, 将 access token 返回给第三方应用
返回例子如下:
- {
- "access_token":"ACCESS_TOKEN",
- "expires_in":7200,
- "refresh_token":"REFRESH_TOKEN",
- "openid":"OPENID",
- "scope":"SCOPE",
- "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
- }
除前面提到的 access_tokenrefresh_tokenexpires_in, 这里还返回了 openidunionid, 这两者是用户信息, 微信体系特有的, 不展开
五为什么不直接返回 access_token
在授权码模式下, 授权服务先返回授权码 code 给第三方应用, 第三方应用再利用授权码来换取 access token
为什么不直接返回 access token 呢?
主要是出于安全方面的考虑
假设第三方应用授权服务不直接通信, 中间隔了一层代理同时, 第三方应用采用 HTTP 协议, 那么, 恶意代理就可以窃取 access token
这就是所谓的中间人攻击
因此, 采用了通过 code 来交换 access token 的方式, 来增加安全性并且, 不能将 access token 直接给到用户侧
相对于用户侧网络环境的复杂性, 应用自身服务端的网络环境相对更可控些但这并不意味着就绝对安全
如果微信开放平台的接口是基于 HTTP 的, 那么不单 access token, 连 secret 也有被截获的的风险
六相关链接
OAuth 2.0 规范
https://tools.ietf.org/html/rfc6749
为什么需要 authorization_code
https://stackoverflow.com/questions/13387698/why-is-there-an-authorization-code-flow-in-oauth2-when-implicit-flow-works-s
微信网页授权
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
来源: https://juejin.im/entry/5a93506e6fb9a0634c268da8