完整的部署教程,从前端到后端,一步步实现 Google 账号登录
Workers + D1 提供免费 Serverless 方案,无需服务器,全球 CDN 加速。
console.cloud.google.com
console.cloud.google.com/apis/consent
状态必须为 "In production",否则只有测试用户可以登录。
console.cloud.google.com/apis/credentials
https://your-app.pages.dev
http://localhost:5500
wrangler d1 create my-app-db
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
);
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"
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 });
}
wrangler deploy
# ✨ https://my-app-api.你的账户.workers.dev
<script src="https://accounts.google.com/gsi/client" async defer></script>
<button onclick="handleLogin()">🔐 Google 登录</button>
<div id="userInfo" style="display:none">
<img id="avatar"> <span id="name"></span>
</div>
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();
}
1. OAuth 同意屏幕是否已发布
2. 网站域名是否已添加授权
3. 打开 F12 控制台查看错误
Client ID 不匹配,确保代码中的 ID 与 Google Cloud Console 中完全一致。
Cloudflare API Token 需要 D1 和 Workers 的 Edit 权限。
console.cloud.google.com
dash.cloudflare.com
dash.cloudflare.com/profile/api-tokens