---
title: "Google 登录"
description: "为您的 Nuxt 应用添加 Google 登录,支持 One Tap 和个性化按钮。"
canonical_url: "https://nuxt-scripts.zhcndoc.com/scripts/google-sign-in"
last_updated: "2026-05-22T14:11:53.681Z"
---
[Google 登录](https://developers.google.com/identity/gsi/web) 提供了一种安全且便捷的方式,让用户使用他们的 Google 账号通过 One Tap、个性化按钮及自动登录进入您的应用。
Nuxt Scripts 提供了一个注册表脚本组合式函数 [`useScriptGoogleSignIn()`](/scripts/google-sign-in),可轻松集成 Google 登录到您的 Nuxt 应用中,并具备最佳性能。
## 实时演示
## Composable API
`useScriptGoogleSignIn()` 返回标准脚本上下文(`status`、`proxy`、`onLoaded` 等)以及三个帮助函数,用于封装最常见的流程。每次调用帮助函数时,都会与传递给 composable 的 schema 选项合并,因此您无需重复传入 `clientId`、`loginUri`、`uxMode` 等参数。
```ts
const { initialize, renderButton, prompt, status, onLoaded, proxy } = useScriptGoogleSignIn({
clientId: 'YOUR_CLIENT_ID',
context: 'signin',
useFedcmForPrompt: true,
})
```
### `initialize(config?)`
使用与 `config` 合并后的 schema 选项调用 `google.accounts.id.initialize()`。内部已做防护,因此多次调用(例如跨导航和组件重新挂载)是安全的;如果 `initialize()` 在页面渲染按钮后再次运行,Google 的 API 会记录错误,因此该帮助函数只会转发第一次调用。
```ts
initialize({
callback: (response) => {
// 在服务器端验证 response.credential
}
})
```
### `renderButton(parent, config?)`
渲染个性化按钮。如有需要会自动初始化,并且在语言切换或导航时可安全重新渲染。
```vue
```
### `prompt(listener?)`
显示 One Tap 提示。如有需要会自动初始化。
```ts
prompt((notification) => {
if (notification.isNotDisplayed())
console.log('未显示:', notification.getNotDisplayedReason())
})
```
### 切换语言环境
按钮的语言环境是 `renderButton` 的选项,而不是 `initialize` 的选项。要更改语言,请清空容器并重新渲染:
```ts
watch([locale, buttonRef], ([newLocale, el]) => {
if (!el)
return
el.innerHTML = ''
renderButton(el, { locale: newLocale, use_fedcm: true })
}, { immediate: true })
```
即使设置了此选项,Google 仍可能回退到用户的 Google 账户语言。这是 Google 端的行为,无法配置。
### 重定向 UX 模式
在 `uxMode: 'redirect'` 下,Google 会将凭证通过 **POST** 以 `application/x-www-form-urlencoded` 的形式发送到您的 `loginUri` 服务器端点(字段包括:`credential`、`g_csrf_token`、`select_by` 等)。重定向后,凭证不会作为 URL fragment 出现;它会随 POST 请求体传输,由您的服务器在将浏览器重定向之前进行处理。
如果您需要在客户端获取凭证(例如使用独立 API 的 SPA),请改用带有 `callback` 的 `uxMode: 'popup'`。
```ts
const { initialize, renderButton } = useScriptGoogleSignIn({
uxMode: 'redirect',
loginUri: 'https://your-server.com/auth/google',
})
initialize() // 重定向模式下不需要 callback
```
## Moment 通知
跟踪 One Tap 的显示状态:
```ts
const { onLoaded } = useScriptGoogleSignIn()
onLoaded(({ accounts }) => {
accounts.id.prompt((notification) => {
if (notification.isDisplayMoment()) {
if (notification.isDisplayed()) {
console.log('One Tap 已显示')
} else {
console.log('未显示:', notification.getNotDisplayedReason())
}
}
if (notification.isSkippedMoment()) {
console.log('已跳过:', notification.getSkippedReason())
}
if (notification.isDismissedMoment()) {
console.log('已关闭:', notification.getDismissedReason())
}
})
})
```
## 服务器端验证
始终在服务器端验证凭证令牌:
```ts [server/api/auth/google.post.ts]
export default defineEventHandler(async (event) => {
const { credential } = await readBody(event)
// 向 Google 验证令牌
const response = await $fetch(`https://oauth2.googleapis.com/tokeninfo`, {
params: { id_token: credential }
})
// 验证客户端 ID 是否匹配
if (response.aud !== 'YOUR_CLIENT_ID') {
throw createError({ statusCode: 401, message: '无效的令牌' })
}
// 使用用户信息创建会话
const user = {
email: response.email,
name: response.name,
picture: response.picture,
sub: response.sub
}
return { user }
})
```
## Cross-Origin-Opener-Policy
在 `popup` UX 模式(默认)下,Google 会打开一个弹窗,并通过 `window.postMessage` 将凭证返回到您的页面。如果您的页面发送了严格的 `Cross-Origin-Opener-Policy` 标头,浏览器会阻止该消息,登录看起来就像没有反应:弹窗会关闭,但不会触发回调。
如果您设置了 COOP,请使用 `same-origin-allow-popups`:
```ts [nuxt.config.ts]
export default defineNuxtConfig({
routeRules: {
'/login/**': {
headers: { 'Cross-Origin-Opener-Policy': 'same-origin-allow-popups' },
},
},
})
```
未显式设置 COOP 标头的页面默认可正常工作。重定向模式(`uxMode: 'redirect'`)和仅 FedCM 流程不受影响。
## FedCM API 支持
启用隐私沙箱 [FedCM API](https://developers.google.com/identity/gsi/web/guides/fedcm-migration) 支持以增强隐私保护。**2025 年 8 月起将强制采用 FedCM。**
```ts
export default defineNuxtConfig({
scripts: {
registry: {
googleSignIn: {
clientId: 'YOUR_CLIENT_ID',
useFedcmForPrompt: true
}
}
}
})
```
### 跨域 iframe
在使用 FedCM 的 One Tap 或登录按钮于跨域 iframe 中时,应为所有父 iframe 添加 `allow` 属性:
```html
```
启用 FedCM 后,无法通过 `prompt_parent_id` 定制 One Tap 提示的位置。
## 撤销访问权限
允许用户撤销对其 Google 账户的访问权限:
```ts
const { onLoaded } = useScriptGoogleSignIn()
function revokeAccess(userId: string) {
onLoaded(({ accounts }) => {
accounts.id.revoke(userId, (response) => {
if (response.successful) {
console.log('访问权限已撤销')
} else {
console.error('撤销失败:', response.error)
}
})
})
}
```
## 最佳实践
### 登出处理
用户登出时务必调用 `disableAutoSelect()`,防止自动重新认证:
```ts
function signOut() {
// 清除应用会话
user.value = null
// 阻止 One Tap 自动选择此账户
onLoaded(({ accounts }) => {
accounts.id.disableAutoSelect()
})
}
```
### 限制托管域
限制登录仅允许来自特定 Google Workspace 域的用户:
```ts
accounts.id.initialize({
client_id: 'YOUR_CLIENT_ID',
callback: handleCredentialResponse,
hd: 'your-company.com' // 仅允许该域用户登录
})
```
## 本地开发配置
要在本地测试 Google 登录:
1. 打开 [Google Cloud 控制台 → 凭据](https://console.cloud.google.com/apis/credentials)
2. 创建或选择一个 OAuth 2.0 客户端 ID(Web 应用类型)
3. 在 **授权的 JavaScript 来源** 中添加:
- `http://localhost`
- `http://localhost:3000`(或您的开发服务器端口)
4. 保存并复制客户端 ID
Google 要求本地开发使用 `http://localhost`(而非 `127.0.0.1`)。使用弹出模式时无需重定向 URI。
然后配置环境变量:
```bash
NUXT_PUBLIC_SCRIPTS_GOOGLE_SIGN_IN_CLIENT_ID=your-client-id.apps.googleusercontent.com
```
## 指南
有关如何获取 Google 客户端 ID 及配置 OAuth 同意屏幕的详细信息,请参阅官方 [Google 身份服务文档](https://developers.google.com/identity/gsi/web/guides/get-google-api-clientid)。
## 示例
### One Tap 登录
One Tap 提示提供了一种简化的登录体验:
```vue
```
### 个性化按钮
渲染 Google 的个性化“使用 Google 登录”按钮:
```vue
```