OAuth 是一份关于允许用户授权第三方应用访问其存储在其他网站上资源,而无需将用户名密码提供给第三方网站的开放标准。OAuth2 是 OAuth 的最新版本,同时也是被广泛应用的一个版本。
OAuth2 标准定义了一个 “用户授权 -> 数据获取” 的流程,理解了这个流程,也就理解了 OAuth2 的整体思路。
流程即不同角色之间的交互,在进入具体的流程描述之前,我们需要了解流程中涉及的角色有哪些。OAuth2 中涉及的角色包括:
资源所有者就是用户,为了便于理解,以下简称为用户。
资源服务器就是存储用户资源的服务器。
客户端也被称为第三方应用,即需要得到用户授权,让它可以访问用户资源的应用。在 Web 环境中,客户端由 “服务器” 和 “运行于浏览器中的网页” 组成,而在手机环境中,客户端由 “服务器” 和 “App” 组成。
授权服务器即为客户端颁发访问令牌(Access Token)的服务器。访问令牌是客户端访问资源服务器中存放的用户资源所需要出示的凭据,访问令牌一般会有资源访问权限(如,读、写、读写)、访问范围(如,所有数据、部分数据)、访问时间(如,一天、一小时)的限制。只有得到用户的授权,授权服务器才会为客户端颁发访问令牌。
基于 OAuth2 的数据获取流程如上图所示,整个流程可以归纳为以下三个部分:
授权码顾名思义即用户授权的凭据是一个“授权码”。大部分基于 OAuth2 的用户数据获取流程都使用授权码形式的授权凭据。授权码类型的流程如下:
隐式授权即不产生授权码的授权码模式,在隐式模式中,整个流程不存在授权码,用户在授权服务器授权通过后,授权服务器会直接生成访问令牌继续执行后面的操作,隐式模式适用于存在 “运行于浏览器中的网页” 的客户端,它的流程如下:
密码凭据即客户端主动向用户申请访问资源所需的账号密码,然后使用账号密码向授权服务器发起请求,获取访问令牌。密码凭据适用于用户高度相信客户端的情况。
在客户端凭据类型下,客户端即用户。在这种类型下,客户端直接向授权服务器发起请求获取访问令牌,不需要其他额外的证明。
以下使用 Node.js 演示授权码类型下获取 GitHub 的 OAuth2 授权,涉及的库包括:
OAuth2 是一个获取用户存储在其他网站上数据的标准,我们想要获取用户数据,就得先到用户数据所在的网站进行注册应用,GitHub 的 OAuth2 应用注册地址是 https://github.com/settings/applications/new,具体界面如下图所示。
上图中,Homepage URL 指的是你应用的地址,由于我们是本地测试应用,所以填写测试地址即可。Authorization callback URL 一项中填写的是用户授权后,授权服务器的回调地址。
点击 Register application 注册成功后,GitHub 会生成客户端 ID(Client ID)和客户端密钥(Client Secret ),这两个数据在后续的请求需要用到,需要保存到服务器应用
对于浏览器应用而言,只需要在浏览器界面展示一个跳转 GitHub 的授权链接,用户点击了即可去 GitHub 授权。
// view/index.pug
// html 设置授权链接,并拼接 client_id
doctype html
html
head
body
a(href='https://github.com/login/oauth/authorize?client_id=' + clientId) 获取授权
服务端定义 GitHub 授权回调路由,并使用回调参数交换访问令牌,再使用访问令牌获取用户信息。
// router.js
router.get('/redirect', async (ctx) => {
const code = ctx.query.code // Code 为 GitHub 拼接在重定向链接后的参数
if (!code) {
ctx.throw(400, '回调 URL 无 code 字段')
return
}
// GitHub OAuth 要求参数
const param = {
client_id: config.clientId,
client_secret: config.clientSecret,
code,
}
// GitHub 交换访问令牌默认返回字符串,通过设置 accept 来控制结果返回 json 字符串
const opt = {
headers: {
accept: 'application/json',
},
}
// 使用 code 交换访问令牌
const { data } = await axios.post('https://github.com/login/oauth/access_token', param, opt)
const accessToken = data.access_token
if (!accessToken) {
ctx.throw(500, '交换访问令牌失败')
return
}
const { data: user } = await axios.get('https://api.github.com/user', {
headers: {
Authorization: `token ${accessToken}`,
},
})
if (!user) {
ctx.throw(500, '用户数据获取失败')
return
}
ctx.body = user
})
查看完整代码请前往 GitHub 搜索用户 yo-squirrel
觉得写得不错可以关注下微信公众号「松鼠专栏」
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。