Vue 项目接入Google第三方登录的详细流程🚀🚀

707 阅读3分钟

前言

现在很多的项目都需要接入第三方登录,比如 Google / Apple / Facebook 等登录方式,尤其是出海项目,因此很有必要把接入第三方登录的完整流程研究一遍。

接入第三方登录,核心就两个步骤:1. 按照官方提供的文档进行一些必要的配置(文档一般都会提供比较详细的接入步骤) 2. 开发人员编写代码实现效果;这里以 Google 登录为例进行讲解。

开发者平台配置

首先在 Google 开发者平台 进行配置,如果没有项目的话需要新建,这块的流程搞定之后,接下来进入 凭证 页面,生成客户端 ID 用于后续的登录流程,主要的操作步骤如下:

5ea28e99cf18553cd69aefe529169bc.png

大家可以参考文档进行操作,这里不再详细展开。实现 Google 登录主要有两种模式,分别为弹出式窗口模式、重定向模式。

弹出式窗口模式

顾名思义,在原页面弹出一个小窗口进行登录,如下图所示:

c5df586f58b33d599bac26dfb25816b.png

这种模式下我们需要有一个按钮,当点击的时候唤起弹窗,Google 官方允许开发者自定义登录按钮,要显示 使用 Google 账号登录 按钮,可以选择 HTML 或者 JavaScript 的方式去实现,具体可以参考该链接:developers.google.com/identity/gs…

1.使用 HTML 呈现登录按钮,并将 JWT 返回给平台的登录端点:

<html>
  <body>
    <script src="https://accounts.google.com/gsi/client" async></script>
    <div id="g_id_onload"
        data-client_id="YOUR_GOOGLE_CLIENT_ID"
        data-login_uri="https://your.domain/your_login_endpoint"
        data-auto_prompt="false">
    </div>
    <div class="g_id_signin"
        data-type="standard"
        data-size="large"
        data-theme="outline"
        data-text="sign_in_with"
        data-shape="rectangular"
        data-logo_alignment="left">
    </div>
  <body>
</html>

2.使用 JavaScript 呈现登录按钮,并将 JWT 返回给 浏览器的 JavaScript 回调处理程序:

<html>
  <body>
    <script src="https://accounts.google.com/gsi/client" async></script>
    <script>
      function handleCredentialResponse(response) {
        console.log("Encoded JWT ID token: " + response.credential);
      }
      window.onload = function () {
        google.accounts.id.initialize({
          client_id: "YOUR_GOOGLE_CLIENT_ID"
          callback: handleCredentialResponse
        });
        google.accounts.id.renderButton(
          document.getElementById("buttonDiv"),
          { theme: "outline", size: "large" }  // customization attributes
        );
        google.accounts.id.prompt(); // also display the One Tap dialog
      }
    </script>
    <div id="buttonDiv"></div>
  </body>
</html>

这两段代码是在 Google 开发者平台中生成的,位于 生成集成代码 页面:

d40f198309f0fb0bdeaf2a5a4bd4765.png

自定义按钮的样式以及模式:

96643af336250e71145fcba9bd315d4.png

我们只需要将生成的代码复制到项目中使用即可:

7a37f71d10ff90f26e4de8e5100ecb9.png

具体的实现代码:

<!-- 登录按钮 -->
<div class="oauth-item" style="display: none">
  <div id="g_id_onload" :data-client_id="clientId" data-callback="googleCallback"</div>
  <div
    class="g_id_signin"
    data-theme="outline"
    :data-width="300"
    :data-locale="en"
  ></div>
</div>

动态引入 JS 并绑定 Google 回调事件:

const clientId = ref('');

const createGoogleClient = () => {
  const cb = (data: any) => {
    const params = {
      accessToken: data.credential,
      ...
    };
    loginByGoogleIdentity(params).then(res => {
      if (res) {
        // 登录成功后做一些处理
      }
    });
  };
  (window as any).googleCallback = cb;
  const s = document.createElement('script');
  s.src = 'https://accounts.google.com/gsi/client';
  document.body.appendChild(s);
};

onMounted(() => {
  createGoogleClient();
});

架构图

这里着重讲解重定向模式,官方文档也有提到,这种方式是最安全的,在该模式下,集成 Google 第三方登录的完整架构图如下:

image.png

重定向模式

在页面上准备一个按钮,点击跳转至 Google 授权登录页,跳转链接需要 client_id 以及 redirect_uri 两个字段的值。

// 重定向地址
const redirectUrl = `${location.origin}/loading`;

// Google授权登录页
const targetUrl = `https://accounts.google.com/o/oauth2/v2/auth?client_id=
${clientId}&scope=https://www.googleapis.com/auth/userinfo.email
&response_type=token&redirect_uri=${redirectUrl}`;

const signUpByGoogle = () => {
  // 缓存当前页面的URL并进行跳转
  sessionStorage.setItem('ThirdLoginUrl', location.href);
  location.href = targetUrl
};

授权登录页如下:

a94b5c50b54a8b284ecf36722fa0e68.png

在 Google 授权页面完成登录之后,接下来需要携带 token 返回重定向页面。

对于重定向页面,可以考虑把所有的第三方登录都重定向到同一个页面,便于管理,比如重定向至 loading 页面:

<template>
  <div>Loading Page</div>
</template>

<script setup lang="ts">
import { handleThirdLogin } from '@/utils/third-login';

handleThirdLogin()
</script>

重定向页面 handleThirdLogin 方法的主要逻辑:从 URL 中获取 access_token,接着调用一遍我们系统内部的登陆接口,携带凭证 accessToken 传给后端,后端需要向 Google 服务器验证 token 的有效性,验证通过后将用户信息返回给前端:

function handleThirdLogin(cb?: () => void) {
  const accessToken = takeAccessToken();
  if (accessToken) {
    return loginByGoogleAccount({
      accessToken: accessToken,
      ...
    })
    .then(res => {
      return loginCallback(res, cb);
    })
  }
  return Promise.reslove(undefined)
}

function takeAccessToken() {
  let accessToken = getValueFormHash(location?.hash?.split('&') || [], '#access_token=');
  if (!accessToken) {
    accessToken = getValueFormHash(location?.hash?.split('&') || [], '#/access_token=');
  }
  return accessToken
}

export const getValueFormHash = (hashArr: string[], key: string) => {
  return hashArr.find((e: string) => e.startsWith(key))?.split(key)?.[1];
};

此时整个登录流程已经完成,执行 loginCallback 方法,前端可以跳转至原来的页面或者其他页面,还可以根据自己项目的需求增加一些处理逻辑:

const loginCallback = (res: any, cb?: () => void) => {
  if (res) {
    cb && cb();
    const targetUrl = sessionStorage.getItem('ThirdLoginUrl');
    sessionStorage.removeItem('ThirdLoginUrl')
    location.href = targetUrl;
  }
};