🔐 Google OAuth 2.0 登录

完整的部署教程,从前端到后端,一步步实现 Google 账号登录

Google OAuth 2.0 Cloudflare Workers D1 数据库 Serverless

1 准备工作

需要准备的资源

💡
为什么选择 Cloudflare?

Workers + D1 提供免费 Serverless 方案,无需服务器,全球 CDN 加速。

2 Google Cloud 配置

2.1 创建项目

Google Cloud Console

console.cloud.google.com

  1. 点击顶部项目选择器 → "新建项目"
  2. 填写项目名称 → 点击 "创建"

2.2 配置 OAuth 同意屏幕

OAuth 同意屏幕

console.cloud.google.com/apis/consent

  1. 选择 "外部" → 创建
  2. 填写应用名称、邮箱等信息
  3. 作用域页面:直接 "保存并继续"
  4. 点击 "发布应用"(重要!)
⚠️
必须发布应用

状态必须为 "In production",否则只有测试用户可以登录。

2.3 创建 OAuth 凭证

API 凭据页面

console.cloud.google.com/apis/credentials

  1. 点击 "创建凭证" → "OAuth 客户端 ID"
  2. 应用类型:选择 "Web 应用"
  3. 添加已授权的 JavaScript 来源:
授权来源
https://your-app.pages.dev
http://localhost:5500
  1. 添加已授权的重定向 URI(同上)
  2. 点击 "创建"
  3. ⚠️ 复制 Client ID

3 创建后端 API

3.1 创建 D1 数据库

bash
wrangler d1 create my-app-db

3.2 创建用户表

SQL
CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    google_id TEXT UNIQUE NOT NULL,
    email TEXT,
    name TEXT,
    picture TEXT,
    high_score INTEGER DEFAULT 0,
    total_score INTEGER DEFAULT 0,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

3.3 wrangler.toml

toml
name = "my-app-api"
main = "src/index.js"
compatibility_date = "2026-03-22"

[[d1_databases]]
binding = "DB"
database_name = "my-app-db"
database_id = "你的数据库ID"

[vars]
GOOGLE_CLIENT_ID = "你的Client ID"

3.4 src/index.js

javascript
export default {
  async fetch(request, env, ctx) {
    const corsHeaders = {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type',
    };
    if (request.method === 'OPTIONS') return new Response(null, { headers: corsHeaders });
    
    const url = new URL(request.url);
    if (url.pathname === '/api/verify') return handleVerify(request, env, corsHeaders);
    if (url.pathname === '/api/leaderboard') return handleLeaderboard(env, corsHeaders);
    return new Response('Not found', { status: 404 });
  }
};

async function handleVerify(request, env, corsHeaders) {
  const { id_token } = await request.json();
  const res = await fetch(`https://oauth2.googleapis.com/tokeninfo?id_token=${id_token}`);
  if (!res.ok) return Response.json({ error: 'Invalid token' }, { status: 401, headers: corsHeaders });
  
  const user = await res.json();
  if (user.aud !== env.GOOGLE_CLIENT_ID) return Response.json({ error: 'Invalid client' }, { status: 401, headers: corsHeaders });
  
  let dbUser = await env.DB.prepare('SELECT * FROM users WHERE google_id = ?').bind(user.sub).first();
  if (!dbUser) {
    await env.DB.prepare('INSERT INTO users (google_id, email, name, picture) VALUES (?, ?, ?, ?)')
      .bind(user.sub, user.email, user.name, user.picture).run();
    dbUser = await env.DB.prepare('SELECT * FROM users WHERE google_id = ?').bind(user.sub).first();
  }
  return Response.json({ success: true, user: dbUser }, { headers: corsHeaders });
}

async function handleLeaderboard(env, corsHeaders) {
  const result = await env.DB.prepare('SELECT name, picture, high_score FROM users ORDER BY high_score DESC LIMIT 100').all();
  return Response.json({ success: true, leaderboard: result.results }, { headers: corsHeaders });
}

3.5 部署

bash
wrangler deploy
# ✨ https://my-app-api.你的账户.workers.dev

4 前端集成

4.1 添加 Google SDK

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

4.2 登录按钮

HTML
<button onclick="handleLogin()">🔐 Google 登录</button>
<div id="userInfo" style="display:none">
  <img id="avatar"> <span id="name"></span>
</div>

4.3 JavaScript

JavaScript
const API = 'https://your-api.workers.dev';
let user = null;

document.addEventListener('DOMContentLoaded', () => {
  google.accounts.id.initialize({
    client_id: '你的Client ID',
    callback: async (res) => {
      const data = await fetch(`${API}/api/verify`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ id_token: res.credential })
      }).then(r => r.json());
      if (data.success) {
        user = data.user;
        document.getElementById('avatar').src = user.picture;
        document.getElementById('name').textContent = user.name;
      }
    }
  });
});

function handleLogin() {
  google.accounts.id.prompt();
}

5 常见问题

点击登录没反应?

1. OAuth 同意屏幕是否已发布
2. 网站域名是否已添加授权
3. 打开 F12 控制台查看错误

显示 "Invalid client" 错误?

Client ID 不匹配,确保代码中的 ID 与 Google Cloud Console 中完全一致。

API 返回权限错误?

Cloudflare API Token 需要 D1 和 Workers 的 Edit 权限。

6 相关链接

Google Cloud Console

console.cloud.google.com

Cloudflare Dashboard

dash.cloudflare.com

API Token

dash.cloudflare.com/profile/api-tokens