给你的网站接入 github 提供的第三方登录

1,462 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第27天,点击查看活动详情

什么年代了还在用传统账号密码登录?没钱买手机号验证码组合?直接把鉴权和用户系统全盘托出依赖第三方(又不是不能用),省去鉴权系列 SQL 攻击、密码加密、CSRF 攻击、XSS 攻击,老板再也不用担心黑产盗号了(我们的系统根本没有号)

要实现上面的功能就得接入第三方登录,接下来就随着文章一起试试吧!

github

本章节将使用 github 作为第三方登录服务提供商

github 不愧是阿美力卡之光,极其简便的操作即可开启你的第三方登录之旅,经济又实惠,你可以通过快捷链接进入创建 OAuth 应用界面,也可以按照下面的顺序

202212251712220.png

然后填写相应的信息

生成你的密钥(Client secrets),就可以去试试第三方登录了

组合 URL

您可以在线查看本章节源代码

这里我使用的是 express-generator 去生成项目,并且前后端分离,在选项上不需要 HTML 渲染器

npx express --no-view your-project-path && cd your-project-path

实现第三方登录的第一步就是需要跳转到 github 自己的页面去做验证,它的 URI 格式如下

GET https://github.com/login/oauth/authorize

前端部分简单设置一下跳转验证

<html>
  <body>
    <div>
      第三方登录
      <br />
      <button onclick="handleGithubLoginClick()">github</button>
    </div>
  </body>
  <script>
    const handleGithubLoginClick = () => {
      const state = Math.floor(Math.random() * Math.pow(10, 8));
      localStorage.setItem("state", state);
      window.open(
        `https://github.com/login/oauth/authorize?client_id=b351931efd1203b2230e&redirect_uri=http://localhost:8080&state=${state}`,
        "_blank"
      );
    };
  </script>
</html>

其中有三个比较重要的 params

  1. client_id - string - “必需”。 注册时从 GitHub 收到的客户端 ID。
  2. redirect_uri - string - 用户获得授权后被发送到的应用程序中的 URL。 请参阅以下有关重定向 URL 的详细信息。
  3. state - string - 不可猜测的随机字符串。 它用于防止跨站请求伪造攻击。

redirect_uri 默认是注册 OAuth 应用(Register a new OAuth application)是填写的授权回调 URLAuthorization callback URL

而对于 state 就在前端用随机字符串模拟,通常此类加密的敏感数据会再后端生成,而这里为了方便演示就采用了前端生成

详细参数请参考文档

鉴权验证

登录之后就可以进行相对应的验证,比如输入账号密码、授权、Github 客户端验证

202212252308300.png

成功鉴权后会再新弹出的页面重定向redirect_uri

注意要在属于用户操作的范畴下,比如点击按钮的操作,去使用 window.open(strUrl, strWindowName, [strWindowFeatures]) 这种方式去跳转鉴权,否则像 window.open("https://github.com...", "_blank") 这种常见的写法,会报错

202212251820219.png

浏览器会以为是弹窗式广告,所以我推荐使用直接在当前窗口跳转的方法,而不是选择新开窗口或者浮动窗口

window.location.href = "https://github.com/login/oauth/authorize?client_id=b351931efd1203b2230e&redirect_uri=http://localhost:8080";

处理回调

通过用户授权时,Github 的响应如下

GET redirect_uri

参数

名称类型说明
codestring鉴权通过的响应代码
statestring请求第三方登录时防 csrf 凭证

state 参数负责安全非常重要,想要快速通关的选手可以跳过这部分

对于这里的 state 处理可以分为前端处理和后端处理

前端处理

redirect_uri 是前端路由时,可以将之前提交的 statelocalStorage 或者 sessionStorage 中取出,验证是否一致,再去向后端请求并带上 statecode

如下

// 跳转登录前
const timestamp = new Date().getTime();
const state = md5(timestamp);
localStorage.setItem("state", state);
// ...
// 验证成功返回后(即请求 redirect_uri)
function getLocalState() {
  return localStorage.getItem("state");
}
const code = getQueryString("code");
const state = getQueryString("state");
if (code && state === getLocalState()) {
  // 处理 github 返回的 code
  callback(code);
}

优点

  1. 无需缓存 state

缺点

  1. 需要防止 XSSDOM 型攻击

时序图如下

后端处理

redirect_uri 是后端时,后端需要持有 state 的缓存,具体做法可以在前端处理第三方登录时同步随机生成的 state,并在后端缓存

优点

  1. 不需要防止 XSSDOM 型攻击

缺点

  1. 需要缓存 state

科普:早期 token 其实就是这里的 state

获取 token

第三方登录从本质上来讲就是获取到 token,在安全的拿到 codestate 之后,需要向 github 发送获取 token 请求,其文档如下

POST https://github.com/login/oauth/access_token

参数

名称类型说明
client_idstring必填。 从 GitHub 收到的 OAuth App 的客户端 ID。
client_secretstring必填。 从 GitHub 收到的 OAuth App 的客户端密码。
codestring必填。 收到的作为对步骤 1 的响应的代码。
redirect_uristring用户获得授权后被发送到的应用程序中的 URL。

响应

名称类型说明
access_tokenstringgithubtoken
scopestring参考文档
token_typestringtoken 类型

注意因为 client_secret 属于私钥,所以该请求必须放在后端,不能在前端请求!否则会失去登录的意义

const { default: axios } = require("axios");
const express = require("express");
const router = express.Router();

router.post("/redirect", function (req, res, next) {
  const { code } = req.body;
  axios({
    method: "POST",
    url: "https://github.com/login/oauth/access_token",
    headers: {
      "Accept": "application/json",
    },
    timeout: 60 * 1000,
    data: {
      client_id: "your_client_id",
      client_secret: "your_client_secret",
      code,
    },
  })
    .then((response) => {
      res.send(response.data);
    })
    .catch((e) => {
      res.status(404);
    });
});

module.exports = router;

注意,由于 github 的服务器在国外,所以这个请求非常容易超时或者失效,建议做好对应的处理(或者设置一个比较长的时间)

最后拿到对应的 token

总结

如果还没有了解过第三方登录的同学可以试试,毕竟不需要审核,有对应的 github 账号就行,截至写完文章的现在,我仍然没有通过微博第三方登录的审核/(ㄒoㄒ)/~~

下面是 OAuth 应用的完整时序图

202301171746697.png

参考资料

  1. 授权 OAuth 应用 - Github Docs
  2. 给你的网站添加第三方登录以及短信验证功能
  3. 前后端分离项目OAuth登录总结 - 墨天轮 - 崔亮的博客