本文详解如何在 golang 中通过 google oauth2 安全、可靠地获取用户邮箱地址,涵盖正确构造授权 url、交换令牌、调用 userinfo 端点、解析响应体等关键步骤,并澄清 `oauth2.config` 的线程安全与复用机制。
在 Go 中集成 Google OAuth2 获取用户邮箱时,一个常见误区是忽略了 HTTP 响应体(response.Body)需要显式读取并关闭——直接打印 response 结构体只会看到空的 Body: {},而真实数据实际存在于流式响应体中。此外,Google 已全面弃用 Google+ API(如 /plus/v1/people/me),推荐使用标准 OpenID Connect 流程,即通过 https://www.googleapis.com/oauth2/v3/userinfo 或更规范的动态发现端点获取用户信息。
✅ 正确流程:OpenID Connect 方式(推荐)
首先确保 oauth2.Config 使用标准 OIDC 作用域:
provider := oauth2.Config{
ClientID: "your-client-id.apps.googleusercontent.com",
ClientSecret: "your-client-secret",
RedirectURL: "https://yourdomain.com/callback",
Endpoint: google.Endpoint,
Scopes: []string{
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile", // 可选,用于 name/picture
"openid", // 必须启用 OpenID Connect
},
}
⚠️ 注意:openid 是必需 scope,否则 userinfo 端点可能拒绝请求或返回不完整数据。
生成授权 URL 并处理回调后,完成令牌交换:
tok, err := provider.Exchange(ctx, authCode) // ✅ 使用 context(替代已废弃的 oauth2.NoContext)
if err != nil {
http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
return
}
client := provider.Client(ctx, tok)
关键一步:调用 UserInfo 端点并读取响应体
// ✅ 正确端点(无需手动拼接,直接使用标准 OIDC userinfo endpoint)
userInfoURL := "https://www.googleapis.com/oauth2/v3/userinfo"
resp, err := client.Get(userInfoURL)
if err != nil {
http.Error(w, "Failed to fetch user info: "+err.Error(), http.StatusInternalServerError)
return
}
defer resp.Body.Close() // ✅ 必须关闭 Body
body, err := io.ReadAll(resp.Body) // ✅ 显式读取全部响应内容
if err != nil {
http.Error(w, "Failed to read response body: "+err.Error(), http.StatusInternalServerError)
return
}
// 解析 JSON
var userInfo struct {
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
Name string `json:"name"`
Picture string `json:"picture"`
}
if err := json.Unmarshal(body, &userInfo); err != nil {
http.Error(w, "Failed to parse user info: "+err.Error(), http.StatusInternalServerError)
return
}
log.Printf("User email: %s (verified: %t)", userInfo.Email, userInfo.EmailVerified)
? 进阶:使用 OpenID Connect 发现文档(更健壮)
为提升兼容性与未来可维护性,可先获取 Google 的 OpenID 配置(.well-known/openid-configuration),再从中提取 userinfo_endpoint: