引言
在开始之前我们先模拟以下场景:
- 用户
张三
在GitHub
注册了账号, 并存储了个人的信息(头像、昵称、性别、邮箱、手机号……) - 这时
张三
访问不知名应用A
,A
想要张三
可以免注册、登录, 可以直接从GitHub
上获取到张三
的个人信息, 然后进行注册、登录 - 那么问题来了, 要想实现上面 👆🏻 这个需求,
GitHub
作为服务商要做哪些事情呢?A
应用又要做哪些事件呢? 作为用户张三
他的交互流程又该是怎样的呢?
一、OAuth2 是什么?
在这里我先不直接抛出概念, 请允许我从引言中的模拟场景出发, 来发表下我个人对 OAuth2
的理解:
- 在
OAuth2
就是一个标准、约定 - 它规定
一个应用
要想获取用户在另一个应用
里的个人信息, 需要怎样的一个流程 - 以及各个角色(用户、应用端、授权服务器、资源服务器)之间需要进行怎样的通信、交互的, 最后又是如何完成用户授权功能
- 所谓
用户授权
简单来讲就是, 用户允许将资源服务器
的个人信息授权
给应用端
, 让应用端
能够获取到自己在资源服务器
的个人信息
现在我们看下 维基 上的解释:
-
OAuth
全称是Open Authentication
(开放授权), 是一个开放标准, 它允许用户
让第三方应用
具有访问该用户在某一网站上存储的个人信息的权限, 而无需将用户名和密码提供给第三方应用 -
OAuth
允许用户提供一个令牌, 而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的网站 (例如, 视频编辑网站) 在特定的时段 (例如, 接下来的 2 小时内) 内访问特定的资源 (例如仅仅是某一相册中的视频)。这样, OAuth 让用户可以授权第三方网站访问他们存储在另外服务提供者的某些特定信息, 而非所有内容
二、交互流程
首选在开始介绍 交互流程
之前我们先来了解下整个过程需要涉及到的几个角色:
- 资源所有者 (Resource Owner): 顾名思义, 资源的所有者, 就是上文提到的用户
- 应用程序 (Client): 准备访问用户资源的应用程序, 可以是任何应用端
- 授权服务器 (Authorization Server): 授权服务器, 在获取用户的同意授权后, 颁发访问令牌给应用程序
- 资源服务器 (Resource Server): 保存用户资源的服务
下面我们以 GitHub
为例来讲解下 OAuth
授权的交互流程, 当一个应用接入了 GitHub
的 OAuth
后, 用户从授权到登录整个交互流程应该是怎样的:
- 用户访问
A
网站, 并选择GitHub
登录成功 A
网站将跳转到GitHub
让用户进行授权- 这时
GitHub
将会先验证用户的身份, 用户如果没有登录则需要先进行登录, 然后询问用户是否授权给A
网站 - 用户同意授权后
GitHub
将会重定向回A
网站, 同时会在URL
上携带一个授权码
A
网站就可以使用授权码
, 向GitHub
授权服务器
获取访问资源服务器
的token
(权限)GitHub
授权服务器
返回资源服务器 token
A
网站使用资源服务器 token
, 向GitHub
的资源服务器
请求用户数据
补充: 第三方登录, 实质就是 OAuth
授权, 用户想要登录 A
网站, 但又不想注册登录想直接使用 第三方系统
的 身份数据
进行登录
三、「GitHub 授权 OAuth 应用」实现
理论简单过下, 接下来我们就按照上文给出的流程图, 来完成 GitHub
第三方登录授权登录功能, 当然更多的细节可以查阅 官方文档
3.1 创建应用
首先如果我们想要使用 GitHub
第三方网站 OAuth
授权, 需要先去 GitHub
上进行登记, 这里可以直接访问 applications/new 进行登记
登记成功后, 将进入应用详情页面, 在详情页我们可以看到 GitHub
为我们登记的应该生成了一个唯一 ID
, 这里我们可以简单记下, 后面我们进行 GitHub
授权等操作都需要用到
同时这里我们还需要创建一个 client_secret(客户端秘钥)
在后面向 授权服务
获取访问 资源服务器
token
需要该秘钥
创建 client_secret(客户端秘钥)
成功后记得保存下生成的秘钥, 刷新后将无法再次查看秘钥内容了
到此应用登记也就结束咯, 同时我们可以通过访问 settings/developers 来查看所有已经登记的应用
3.2 用户授权
改造登录页, 新增跳转到 GitHub
授权入口, 用户点击下列 👇 授权按钮
将跳转到 https://github.com/login/oauth/authorize? client_id=6aa8689e618b1e87e483
进行授权, 其中 6aa8689e618b1e87e483
是我们上面创建的 应用 ID
用户进行登录、授权
3.3 后端「授权登录」实现
上面 👆🏻 授权成功后, 将会重定向到我们登记应用时所填写的路由, 这里我填写的是
后端的一个 api
, 该接口逻辑如下:
- 拿到接口携带的
授权码
获取资源服务器
token
import qs from 'qs';
const { code } = ctx.request.query;
const { data: { access_token } } = await axios({
method: 'post',
url: `https://github.com/login/oauth/access_token?${qs.stringify({
code,
client_id: clientID,
client_secret: clientSecret,
})}`,
headers: {
accept: 'application/json',
},
});
- 通过
资源服务器
token
获取用户信息
const { data } = await axios({
method: 'get',
url: 'https://api.github.com/user',
headers: {
accept: 'application/json',
Authorization: `token ${access_token}`,
},
});
- 根据
GitHub
用户ID
查找当前用户是否登录过, 如果没有则创建新用户
// 根据 githubId 查找用户
let { data: user } = await findOne({
model: 'User',
search: { githubId: githubInfo.id },
});
// 查不到用户, 则进行注册
if (!user) {
const { change: [newUser] } = await create({
model: 'User',
body: [{
name: githubInfo.name,
githubId: githubInfo.id,
account: githubInfo.login,
}],
});
user = newUser;
}
- 登录(设置
cookie
)
// 拿到 id、name、account 创建 jwt token
const token = await signJwt({
id: user.id,
name: user.name,
account: user.account,
});
// 设置 cookie
ctx.cookies.set('jwt_token', token, { maxAge: 1000 * 60 * 60 * 24 * 7 };
- 最后重定向回首页
ctx.status = 302;
ctx.redirect('http://www.dev.kunlunxu.cc');
到此整个大致流程基本也就结束咯, 下面 👇🏻 是最终的一个效果
这是最后新建的用户表
四、总结
本文到此也就结束咯, 文中内容相对比较简单, 只是简单记录了下 昆仑虚 接入 GitHub
第三方登录的一个流程, 以及过程中遇到的一些疑惑, 当然如果你对 昆仑虚 感兴趣欢迎 👏🏻 点个 star
五、参考
原文链接:https://juejin.cn/post/7230469903123185719 作者:墨渊君