功能说明
本文记录后台接入 Google、GitHub、Microsoft 和 Discord 第三方登录的配置与实现要点,包含平台应用创建、后端配置、回调接口、账号绑定表、用户信息映射和本地调试注意事项。
登录流程
图中的 provider 表示具体平台标识。
| 接口 | 方法 | 说明 |
|---|---|---|
/oauth/providers |
GET |
查询当前可展示的第三方登录平台。 |
/oauth/{provider}/authorize |
GET |
发起第三方授权,返回 302 跳转到对应平台。 |
/oauth/{provider}/callback |
GET |
处理第三方平台回调,成功后回跳前端并携带 ticket。 |
/oauth/token |
GET |
使用一次性 ticket 兑换系统 Token。 |
provider 当前支持:
googlegithubmicrosoftdiscord
回调成功时只携带 ticket,失败时携带错误信息。系统 Token 不直接放入 OAuth 回跳 URL。
数据表设计
第三方账号绑定表保存第三方平台与系统用户的绑定关系、平台资料快照、登录追踪和审计字段。
CREATE TABLE IF NOT EXISTS `sys_oauth_account`
(
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`user_id` bigint NOT NULL COMMENT '系统用户ID',
`provider` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'OAuth平台标识:google、github、microsoft、discord',
`provider_user_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '平台用户唯一ID',
`provider_username` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '平台用户名',
`provider_nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '平台昵称',
`email` varchar(180) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '平台邮箱',
`email_verified` tinyint(1) NULL DEFAULT 0 COMMENT '平台邮箱是否已验证:0否,1是',
`avatar_url` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '平台头像地址',
`profile_url` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '平台主页地址',
`last_login_time` datetime NULL DEFAULT NULL COMMENT '最后登录时间',
`last_login_ip` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '最后登录IP',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '更新人',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`is_deleted` tinyint(1) NULL DEFAULT 0 COMMENT '逻辑删除:0正常,1删除',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uk_provider_subject_deleted` (`provider` ASC, `provider_user_id` ASC, `is_deleted` ASC) USING BTREE,
UNIQUE INDEX `uk_user_provider_deleted` (`user_id` ASC, `provider` ASC, `is_deleted` ASC) USING BTREE,
INDEX `idx_email_provider` (`email` ASC, `provider` ASC) USING BTREE
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_0900_ai_ci
COMMENT = '第三方OAuth账号绑定表';
配置项
application.yml 中配置 OAuth 前端回跳地址、state 有效期、ticket 有效期、默认角色和各平台客户端信息。
oauth:
frontend-redirect-url: ${OAUTH_FRONTEND_REDIRECT_URL:http://localhost:3006/auth/oauth/callback}
state-expire-minutes: ${OAUTH_STATE_EXPIRE_MINUTES:10}
ticket-expire-seconds: ${OAUTH_TICKET_EXPIRE_SECONDS:120}
default-role-id: ${OAUTH_DEFAULT_ROLE_ID:5}
providers:
google:
enabled: ${OAUTH_GOOGLE_ENABLED:true}
display-name: Google
sort: 10
client-id: ${OAUTH_GOOGLE_CLIENT_ID:<GOOGLE_CLIENT_ID>}
client-secret: ${OAUTH_GOOGLE_CLIENT_SECRET:<GOOGLE_CLIENT_SECRET>}
redirect-uri: ${OAUTH_GOOGLE_REDIRECT_URI:http://localhost:10000/draft-auth/oauth/google/callback}
scope: openid email profile
github:
enabled: ${OAUTH_GITHUB_ENABLED:true}
display-name: GitHub
sort: 20
client-id: ${OAUTH_GITHUB_CLIENT_ID:<GITHUB_CLIENT_ID>}
client-secret: ${OAUTH_GITHUB_CLIENT_SECRET:<GITHUB_CLIENT_SECRET>}
redirect-uri: ${OAUTH_GITHUB_REDIRECT_URI:http://localhost:10000/draft-auth/oauth/github/callback}
scope: read:user user:email
microsoft:
enabled: ${OAUTH_MICROSOFT_ENABLED:true}
display-name: Microsoft
sort: 30
client-id: ${OAUTH_MICROSOFT_CLIENT_ID:<MICROSOFT_CLIENT_ID>}
client-secret: ${OAUTH_MICROSOFT_CLIENT_SECRET:<MICROSOFT_CLIENT_SECRET>}
redirect-uri: ${OAUTH_MICROSOFT_REDIRECT_URI:http://localhost:10000/draft-auth/oauth/microsoft/callback}
scope: openid email profile
discord:
enabled: ${OAUTH_DISCORD_ENABLED:true}
display-name: Discord
sort: 40
client-id: ${OAUTH_DISCORD_CLIENT_ID:<DISCORD_CLIENT_ID>}
client-secret: ${OAUTH_DISCORD_CLIENT_SECRET:<DISCORD_CLIENT_SECRET>}
redirect-uri: ${OAUTH_DISCORD_REDIRECT_URI:http://localhost:10000/draft-auth/oauth/discord/callback}
scope: identify email
平台控制台、授权请求和换取 Token 请求中的 redirect_uri 必须完全一致,包括协议、域名、端口、上下文路径和末尾斜杠。
Google OAuth 应用配置
进入 Google Cloud Console,创建或选择项目后配置 OAuth 客户端。

在左侧菜单进入 APIs & Services,选择 OAuth 相关配置入口。

进入 OAuth 概览页后,点击创建 OAuth 客户端。

创建客户端时,应用类型选择 Web application。

授权地址填写规则:
Authorized JavaScript origins:填写前端地址,例如http://localhost:3006或生产前端域名。Authorized redirect URIs:填写后端回调接口,例如http://localhost:10000/draft-auth/oauth/google/callback。

创建完成后,页面会显示 Client ID。将其配置到 OAUTH_GOOGLE_CLIENT_ID。

后续可在客户端列表中重新进入应用配置。

在客户端详情页查看或重置 Client secret,并配置到 OAUTH_GOOGLE_CLIENT_SECRET。

官方资料:
当前后端默认端点
| 用途 | 默认值 |
|---|---|
| 授权端点 | https://accounts.google.com/o/oauth2/v2/auth |
| Token 端点 | https://oauth2.googleapis.com/token |
| 用户资料端点 | https://openidconnect.googleapis.com/v1/userinfo |
| Scope | openid email profile |
这些地址未在配置中显式指定时,由 GoogleOAuthClient 使用默认常量兜底。授权请求额外携带 response_type=code、access_type=online 和用于防止 CSRF 的 state。
GitHub OAuth 应用配置
进入 GitHub Developer applications,在 OAuth Apps 中创建应用。

创建 OAuth App 时填写:
Homepage URL:填写前端地址。Authorization callback URL:填写后端回调接口,例如http://localhost:10000/draft-auth/oauth/github/callback。

创建后复制 Client ID,并配置到 OAUTH_GITHUB_CLIENT_ID。

点击 Generate a new client secret,完成验证后复制 Client secret,并配置到 OAUTH_GITHUB_CLIENT_SECRET。

官方资料:
当前后端默认端点
| 用途 | 默认值 |
|---|---|
| 授权端点 | https://github.com/login/oauth/authorize |
| Token 端点 | https://github.com/login/oauth/access_token |
| 用户资料端点 | https://api.github.com/user |
| 邮箱列表端点 | https://api.github.com/user/emails |
| Scope | read:user user:email |
这些地址未在配置中显式指定时,由 GithubOAuthClient 使用默认常量兜底。授权请求携带客户端 ID、回调地址、Scope 和用于防止 CSRF 的 state。
Microsoft OAuth 应用配置
进入 Microsoft Entra 管理中心,登录后打开 Entra ID,进入 应用注册。

在 所有应用程序 中点击 新注册。

填写应用名称。当前后端使用 Microsoft consumers 授权端点,只面向 Outlook、Hotmail 等个人 Microsoft 账号,因此受支持的账户类型选择 仅个人 Microsoft 帐户。

平台选择 Web,填写后端回调地址,例如 http://localhost:10000/draft-auth/oauth/microsoft/callback,然后完成注册。

在应用概览页复制 应用程序(客户端) ID,配置到 OAUTH_MICROSOFT_CLIENT_ID。不要误用 对象 ID 或 目录(租户) ID。

进入 证书和密码,在 客户端密码 页签点击 新客户端密码。

填写密码说明并选择有效期,然后点击添加。

创建成功后立即复制 值,并配置到 OAUTH_MICROSOFT_CLIENT_SECRET。后端需要的是密码值,不是 机密 ID;密码值离开当前页面后无法再次完整查看。

官方资料:
当前后端默认端点
| 用途 | 默认值 |
|---|---|
| 授权端点 | https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize |
| Token 端点 | https://login.microsoftonline.com/consumers/oauth2/v2.0/token |
| 用户资料端点 | https://graph.microsoft.com/oidc/userinfo |
| Scope | openid email profile |
这些地址未在配置中显式指定时,由 MicrosoftOAuthClient 使用默认常量兜底。授权请求额外携带 response_type=code、response_mode=query 和用于防止 CSRF 的 state。
Discord OAuth 应用配置
进入 Discord Developer Portal,点击 New Application 创建应用。

填写应用名称并确认开发者条款,然后点击创建。

如果创建时提示必须验证邮箱,先完成开发者账号的邮箱验证。

打开 Discord 发送的验证邮件,点击 Verify Email,验证完成后返回开发者后台重新创建应用。

进入应用的 OAuth2 页面,复制 Client ID 并配置到 OAUTH_DISCORD_CLIENT_ID。在 Redirects 区域点击添加回调地址。

填写后端回调接口,例如 http://localhost:10000/draft-auth/oauth/discord/callback,然后保存更改。

在 Client Secret 区域点击 Reset Secret 生成新密钥。重置操作会立即使旧密钥失效,已经接入的环境需要同步更新。

确认重置密钥。

账号启用多因素认证时,需要输入认证码完成验证。

复制新的 Client Secret,并配置到 OAUTH_DISCORD_CLIENT_SECRET。密钥只能放在后端配置或环境变量中。

官方资料:
当前后端默认端点
| 用途 | 默认值 |
|---|---|
| 授权端点 | https://discord.com/api/oauth2/authorize |
| Token 端点 | https://discord.com/api/oauth2/token |
| 用户资料端点 | https://discord.com/api/users/@me |
| Scope | identify email |
这些地址未在配置中显式指定时,由 DiscordOAuthClient 使用默认常量兜底。授权请求携带 response_type=code 和用于防止 CSRF 的 state。
Provider 用户信息映射
Google 使用 OpenID Connect userinfo 接口读取用户资料。
| 系统字段 | Google 字段 |
|---|---|
providerUserId |
sub |
providerUsername |
email |
providerNickname |
name |
email |
email |
emailVerified |
email_verified |
avatarUrl |
picture |
profileUrl |
profile |
GitHub
GitHub 先读取 /user,再读取 /user/emails,只使用 primary=true 且 verified=true 的邮箱。
| 系统字段 | GitHub 字段 |
|---|---|
providerUserId |
id |
providerUsername |
login |
providerNickname |
name,为空时使用 login |
email |
已验证主邮箱 |
emailVerified |
是否存在已验证主邮箱 |
avatarUrl |
avatar_url |
profileUrl |
html_url |
Microsoft
使用 Microsoft OIDC UserInfo 接口 GET https://graph.microsoft.com/oidc/userinfo 读取个人 Microsoft 账号资料,sub 是当前实现使用的平台稳定唯一标识。
| 系统字段 | Microsoft OIDC 字段或处理方式 |
|---|---|
providerUserId |
sub |
providerUsername |
preferred_username,为空时使用解析出的邮箱 |
providerNickname |
name,为空时使用 providerUsername |
email |
依次读取 email、符合邮箱格式的 preferred_username、符合邮箱格式的 unique_name |
emailVerified |
邮箱存在且 email_verified 为 true;字段缺失时按当前实现默认视为已验证 |
avatarUrl |
picture |
profileUrl |
无稳定公共主页地址,可保存为空 |
Discord
使用 GET https://discord.com/api/users/@me 读取用户资料,OAuth scope 必须包含 identify email。
| 系统字段 | Discord 字段或处理方式 |
|---|---|
providerUserId |
id |
providerUsername |
username |
providerNickname |
global_name,为空时使用 username |
email |
email |
emailVerified |
verified |
avatarUrl |
根据 id 和 avatar 生成 Discord CDN 地址;无头像时使用默认头像 |
profileUrl |
https://discord.com/users/<USER_ID> |
Discord 返回 avatar 时,头像地址可按官方 CDN 规则生成:
https://cdn.discordapp.com/avatars/<USER_ID>/<AVATAR_HASH>.<EXTENSION>?size=256
avatar以a_开头时,EXTENSION使用gif。- 其他自定义头像使用
png。
avatar 为空时使用默认头像:
- 新用户名体系中
discriminator为0:index = (userId >> 22) % 6。 - 旧用户名体系中
discriminator不为0:index = discriminator % 5。 - 用户 ID 或旧版
discriminator无法解析为数字时,回退使用索引0。
https://cdn.discordapp.com/embed/avatars/<INDEX>.png
常见问题
Google 授权成功但后端换 Token 失败
- 现象:回调接口收到
code,但后端请求 Google Token 或 userinfo 接口失败。 - 原因:本地 Java 进程没有走代理,或代理端口配置错误。
- 处理:按 IDEA 启动配置代理 配置 JVM 代理参数后重启服务。
redirect_uri_mismatch
- 现象:第三方授权页提示回调地址不匹配。
- 原因:平台控制台配置的回调地址和后端请求中的
redirect_uri不完全一致。 - 处理:确认协议、域名、端口、上下文路径、provider 路径和末尾斜杠完全一致。
Discord 用户没有头像
- 现象:Discord 用户资料中的
avatar为null。 - 原因:用户没有上传自定义头像。
- 处理:根据用户 ID 或旧版
discriminator计算默认头像索引,使用 Discord 默认头像 CDN 地址。
