返回功能模块设计实现
功能模块设计实现 / 发布 2026-06-23 22:12 / 更新 2026-07-13 13:05

OAuth2.0 第三方登录接入方案

整理 Google、GitHub、Microsoft 和 Discord 第三方登录的后端接口、数据表、配置项、平台控制台配置和用户信息映射。

OAuth2.0第三方登录GoogleGitHubMicrosoftDiscord
OAuth2.0 第三方登录接入方案

功能说明

本文记录后台接入 Google、GitHub、Microsoft 和 Discord 第三方登录的配置与实现要点,包含平台应用创建、后端配置、回调接口、账号绑定表、用户信息映射和本地调试注意事项。

登录流程

flowchart TD A["前端获取可用登录平台<br>GET /oauth/providers"] --> B["用户选择第三方登录平台"] B --> C["浏览器请求授权入口<br>GET /oauth/provider/authorize"] C --> D["后端生成第三方授权地址<br>返回 302"] D --> E["跳转到第三方授权页"] E --> F["用户授权成功"] F --> G["第三方回调后端<br>GET /oauth/provider/callback"] G --> H["后端校验 state"] H --> I["使用授权码 code 换取第三方 access_token"] I --> J["读取第三方用户资料"] J --> K["登录、注册或绑定系统账号"] K --> L["生成短期一次性 ticket"] L --> M["302 回跳前端 OAuth 回调页<br>携带 ticket"] M --> N["前端使用 ticket 兑换系统 Token<br>GET /oauth/token"] N --> O["登录完成"]

图中的 provider 表示具体平台标识。

接口 方法 说明
/oauth/providers GET 查询当前可展示的第三方登录平台。
/oauth/{provider}/authorize GET 发起第三方授权,返回 302 跳转到对应平台。
/oauth/{provider}/callback GET 处理第三方平台回调,成功后回跳前端并携带 ticket
/oauth/token GET 使用一次性 ticket 兑换系统 Token。

provider 当前支持:

  • google
  • github
  • microsoft
  • discord

回调成功时只携带 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 客户端。

Google Cloud 控制台首页

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

Google Cloud APIs & Services 菜单

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

Google OAuth 概览页

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

创建 Google OAuth Web 客户端

授权地址填写规则:

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

填写 Google OAuth 授权来源和回调地址

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

Google OAuth 客户端创建成功

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

Google OAuth 客户端列表

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

Google OAuth 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=codeaccess_type=online 和用于防止 CSRF 的 state

GitHub OAuth 应用配置

进入 GitHub Developer applications,在 OAuth Apps 中创建应用。

GitHub OAuth Apps 列表

创建 OAuth App 时填写:

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

注册 GitHub OAuth App

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

GitHub OAuth App Client ID 和生成密钥入口

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

GitHub OAuth App 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 Entra 首页的应用注册入口

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

Microsoft Entra 新建应用注册入口

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

填写 Microsoft 应用名称和账户类型

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

配置 Microsoft Web 回调地址

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

复制 Microsoft 应用程序客户端 ID

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

Microsoft 客户端密码管理页面

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

创建 Microsoft 客户端密码

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

复制 Microsoft 客户端密码值

官方资料:

当前后端默认端点

用途 默认值
授权端点 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=coderesponse_mode=query 和用于防止 CSRF 的 state

Discord OAuth 应用配置

进入 Discord Developer Portal,点击 New Application 创建应用。

Discord Developer Portal 新建应用入口

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

创建 Discord Application

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

Discord 提示开发者账号需要验证邮箱

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

Discord 邮箱验证邮件

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

Discord OAuth2 Client ID 和回调地址入口

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

配置 Discord OAuth2 回调地址

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

Discord 重置 Client Secret 入口

确认重置密钥。

确认重置 Discord Client Secret

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

Discord 重置密钥的多因素认证

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

复制 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

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=trueverified=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_verifiedtrue;字段缺失时按当前实现默认视为已验证
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 根据 idavatar 生成 Discord CDN 地址;无头像时使用默认头像
profileUrl https://discord.com/users/<USER_ID>

Discord 返回 avatar 时,头像地址可按官方 CDN 规则生成:

https://cdn.discordapp.com/avatars/<USER_ID>/<AVATAR_HASH>.<EXTENSION>?size=256
  • avatara_ 开头时,EXTENSION 使用 gif
  • 其他自定义头像使用 png

avatar 为空时使用默认头像:

  • 新用户名体系中 discriminator0index = (userId >> 22) % 6
  • 旧用户名体系中 discriminator 不为 0index = 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 用户资料中的 avatarnull
  • 原因:用户没有上传自定义头像。
  • 处理:根据用户 ID 或旧版 discriminator 计算默认头像索引,使用 Discord 默认头像 CDN 地址。