原创

网站对接 Google 登录完整配置指南

本文介绍如何将 Google OAuth 2.0 登录集成到你的网站,涵盖 Google Cloud 控制台配置、前端接入、后端验证及安全最佳实践。


一、工作原理简述

Google 登录采用 OAuth 2.0 + OpenID Connect 协议。整体流程如下:

  1. 用户点击「Google 登录」按钮
  2. 浏览器跳转至 Google 授权页面
  3. 用户同意后,Google 将带有 codeid_token 的回调请求发送回你的网站
  4. 后端验证 Token,并创建/查找本地用户会话

二、Google Cloud Console 配置

2.1 创建项目

  1. 打开 Google Cloud Console
  2. 点击顶部导航栏的项目选择器 → 新建项目
  3. 填写项目名称,点击创建

2.2 开启 OAuth 同意屏幕

  1. 导航至 API 和服务OAuth 同意屏幕
  2. 选择用户类型:
    • 外部(External):适用于面向所有 Google 用户开放的应用
    • 内部(Internal):仅限 Google Workspace 组织内部用户
  3. 填写必要信息:
字段 说明
应用名称 显示在授权弹窗中的名称
用户支持邮箱 用户授权问题的联系邮箱
开发者联系邮箱 Google 通知开发者的邮箱
授权域名 你的生产环境域名,如 example.com
  1. 如果需要获取用户邮箱和基本信息,添加以下 Scope
    • openid
    • email
    • profile

2.3 创建 OAuth 客户端 ID

  1. 导航至 API 和服务凭据创建凭据OAuth 客户端 ID
  2. 应用类型选择 Web 应用
  3. 配置 URI:

已授权的 JavaScript 来源(用于前端 One Tap 或 OAuth):

http://localhost:3000          ← 本地开发
https://example.com            ← 生产环境

已授权的重定向 URI(用于授权码模式回调):

http://localhost:3000/auth/google/callback
https://example.com/auth/google/callback
  1. 保存后,复制 Client IDClient Secret,妥善保存。

三、环境变量配置

在项目根目录创建 .env 文件(切勿提交至 Git):

GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-client-secret
GOOGLE_CALLBACK_URL=https://example.com/auth/google/callback
SESSION_SECRET=your-random-session-secret

四、前端接入

方案 A:Google One Tap(推荐,最简)

Google One Tap 是最现代的接入方式,弹出式授权,无需页面跳转。

在 HTML <head> 中引入脚本:

<script src="https://accounts.google.com/gsi/client" async></script>

在页面中添加:

<!-- 初始化配置(隐藏元素) -->
<div id="g_id_onload"
     data-client_id="YOUR_GOOGLE_CLIENT_ID"
     data-callback="handleCredentialResponse"
     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>

处理回调的 JavaScript:

function handleCredentialResponse(response) {
  // response.credential 是一个 JWT id_token
  fetch('/auth/google/verify', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ credential: response.credential }),
  })
  .then(res => res.json())
  .then(data => {
    if (data.success) {
      window.location.href = '/dashboard';
    }
  });
}

方案 B:OAuth 2.0 授权码流程(传统跳转)

适合需要请求额外权限(如 Google Drive、Gmail)的场景。

前端只需一个按钮,点击跳转后端路由:

<a href="/auth/google" class="btn-google-login">
  使用 Google 账号登录
</a>

五、后端实现

Node.js + Express + Passport.js

安装依赖

npm install passport passport-google-oauth20 express-session

配置 Passport

// config/passport.js
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;

passport.use(new GoogleStrategy({
  clientID:     process.env.GOOGLE_CLIENT_ID,
  clientSecret: process.env.GOOGLE_CLIENT_SECRET,
  callbackURL:  process.env.GOOGLE_CALLBACK_URL,
}, async (accessToken, refreshToken, profile, done) => {
  try {
    // 在数据库中查找或创建用户
    let user = await User.findOne({ googleId: profile.id });

    if (!user) {
      user = await User.create({
        googleId:    profile.id,
        email:       profile.emails[0].value,
        name:        profile.displayName,
        avatar:      profile.photos[0].value,
      });
    }

    return done(null, user);
  } catch (err) {
    return done(err);
  }
}));

passport.serializeUser((user, done) => done(null, user.id));
passport.deserializeUser(async (id, done) => {
  const user = await User.findById(id);
  done(null, user);
});

配置路由

// routes/auth.js
const express  = require('express');
const passport = require('passport');
const router   = express.Router();

// 发起 Google 授权
router.get('/auth/google',
  passport.authenticate('google', {
    scope: ['openid', 'email', 'profile'],
  })
);

// Google 回调处理
router.get('/auth/google/callback',
  passport.authenticate('google', { failureRedirect: '/login?error=1' }),
  (req, res) => {
    res.redirect('/dashboard');
  }
);

// 登出
router.post('/auth/logout', (req, res) => {
  req.logout(() => res.redirect('/'));
});

module.exports = router;

验证 One Tap 的 id_token(方案 A 后端)

const { OAuth2Client } = require('google-auth-library');
const client = new OAuth2Client(process.env.GOOGLE_CLIENT_ID);

router.post('/auth/google/verify', async (req, res) => {
  const { credential } = req.body;

  try {
    const ticket = await client.verifyIdToken({
      idToken:  credential,
      audience: process.env.GOOGLE_CLIENT_ID,
    });

    const payload = ticket.getPayload();
    const { sub: googleId, email, name, picture } = payload;

    // 查找或创建用户
    let user = await User.findOne({ googleId });
    if (!user) {
      user = await User.create({ googleId, email, name, avatar: picture });
    }

    // 建立会话
    req.session.userId = user.id;
    res.json({ success: true });
  } catch (err) {
    res.status(401).json({ success: false, error: '无效凭据' });
  }
});

六、Python(Django / Flask)接入示例

Django(使用 social-auth-app-django)

pip install social-auth-app-django

settings.py 中配置:

INSTALLED_APPS += ['social_django']

AUTHENTICATION_BACKENDS = [
    'social_core.backends.google.GoogleOAuth2',
    'django.contrib.auth.backends.ModelBackend',
]

SOCIAL_AUTH_GOOGLE_OAUTH2_KEY    = 'YOUR_CLIENT_ID'
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = 'YOUR_CLIENT_SECRET'
SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE  = ['openid', 'email', 'profile']

LOGIN_URL          = '/auth/login/'
LOGIN_REDIRECT_URL = '/dashboard/'

urls.py 中:

urlpatterns = [
    path('auth/', include('social_django.urls', namespace='social')),
    # ...
]

前端登录链接:

<a href="{% url 'social:begin' 'google-oauth2' %}">使用 Google 登录</a>

七、安全注意事项

必须验证的内容

验证项 说明
aud(受众) 必须等于你的 Client ID,防止 Token 被其他应用伪造
iss(颁发者) 必须是 accounts.google.comhttps://accounts.google.com
exp(过期时间) Token 不能已过期
HTTPS 所有通信必须走 HTTPS,绝不在 HTTP 下传输 Token

防护 CSRF

在重定向流程中,使用 state 参数防止 CSRF 攻击:

// 生成随机 state 存入 session
const state = crypto.randomBytes(16).toString('hex');
req.session.oauthState = state;

// 发起授权时带上 state
passport.authenticate('google', { scope: [...], state })

回调时验证:

if (req.query.state !== req.session.oauthState) {
  return res.status(403).send('CSRF 验证失败');
}

其他要点

  • Client Secret 绝不能暴露到前端,只能存在于服务端环境变量
  • 定期轮换 SESSION_SECRET
  • 数据库中不要存储 access_token 的明文,如需长期访问应使用 refresh_token 并加密
  • 在 Google Cloud Console 中配置 已授权域名白名单,防止回调地址被篡改
  • 应用上线前通过 Google 的审查,避免用户看到「此应用未经验证」的警告

八、常见错误排查

错误信息 原因 解决方法
redirect_uri_mismatch 回调 URI 与控制台配置不符 检查控制台配置的重定向 URI 是否与代码完全一致(含协议、端口、路径)
invalid_client Client ID 或 Secret 错误 重新复制凭据,确认环境变量正确加载
access_denied 用户拒绝授权或应用未通过审查 外部应用需完成 Google 审查流程
Token 验证失败 aud 不匹配或 Token 过期 检查 Client ID 是否正确,系统时间是否同步
This app isn't verified 应用未完成 Google 审查 开发阶段可将测试账号加入测试用户列表;生产环境需提交审查

九、完整流程回顾

用户点击登录
     ↓
浏览器跳转 Google 授权页(携带 client_id、scope、redirect_uri、state)
     ↓
用户同意授权
     ↓
Google 重定向回 callback URL(携带 code 或 id_token)
     ↓
后端用 code 换取 access_token + id_token(授权码模式)
  或 直接验证 id_token(One Tap 模式)
     ↓
解析 id_token,获取用户信息
     ↓
数据库查找或创建用户 → 建立本地会话
     ↓
重定向至应用主页 ✓

参考资源

正文到此结束
Loading...