标题:Go 语言使用 Google OAuth2 获取用户信息的完整教程

13次阅读

标题:Go 语言使用 Google OAuth2 获取用户信息的完整教程

本文详细讲解如何在 go 中正确使用 `golang.org/x/oauth2`(含 `google` 子包)完成 google oauth2 授权流程,并成功获取用户基本信息(如邮箱、头像、姓名等),解决常见响应体为空、未解析 json、scope 不足等问题。

在 Go 中集成 google OAuth2 登录时,许多开发者能顺利获取授权码(code)和访问令牌(Token),却卡在最后一步——调用 /userinfo/v2/me 接口获取用户资料。问题往往并非代码逻辑错误,而是http 响应未被正确读取与解析,或权限范围(Scopes)配置不全导致返回数据受限。

✅ 正确做法:完整可运行示例

以下是一个结构清晰、生产可用的 Google OAuth2 用户信息获取流程(基于 golang.org/x/oauth2/google):

package main  import (     "fmt"     "io"     "io/ioutil"     "log"     "net/http"     "net/url"      "golang.org/x/oauth2"     "golang.org/x/oauth2/google" )  var googleconf = &oauth2.Config{     ClientID:     "YOUR_CLIENT_ID",     ClientSecret: "YOUR_CLIENT_SECRET",     redirectURL:  "http://localhost:3000/googlelogin",     Scopes: []string{         "https://www.googleapis.com/auth/userinfo.profile",         "https://www.googleapis.com/auth/userinfo.email", // ⚠️ 必须显式添加此 Scope 才能获取 email     },     Endpoint: google.Endpoint, }  func main() {     http.HandleFunc("/googleloginrequest", func(w http.ResponseWriter, r *http.Request) {         url := googleconf.AuthCodeURL("state", oauth2.accessTypeOffline)         http.Redirect(w, r, url, http.StatusFound)     })      http.HandleFunc("/googlelogin", func(w http.ResponseWriter, r *http.Request) {         code := r.FormValue("code")         if code == "" {             http.Error(w, "missing code", http.StatusbadRequest)             return         }          tok, err := googleconf.Exchange(r.Context(), code) // ✅ 推荐使用 r.Context() 替代 oauth2.NoContext(已弃用)         if err != nil {             log.Printf("OAuth2 exchange error: %v", err)             http.Error(w, "failed to exchange code for token", http.StatusInternalServerError)             return         }          // ✅ 方式一:使用 conf.Client() 构建带 Token 的 HTTP Client(推荐)         client := googleconf.Client(r.Context(), tok)         resp, err := client.Get("https://www.googleapis.com/oauth2/v2/userinfo")         if err != nil {             log.Printf("API request error: %v", err)             http.Error(w, "failed to fetch user info", http.StatusInternalServerError)             return         }         defer resp.Body.Close()          body, err := io.ReadAll(resp.Body)         if err != nil {             log.Printf("Read body error: %v", err)             http.Error(w, "failed to read response", http.StatusInternalServerError)             return         }          // ✅ 输出结构化 jsON(实际项目中建议定义 struct 解析)         w.Header().Set("Content-Type", "application/json")         w.WriteHeader(http.StatusOK)         w.Write(body) // 如 {"id":"123","email":"user@gmail.com","verified_email":true,"name":"John Doe",...}     })      log.Println("Server starting on :3000")     log.Fatal(http.ListenAndServe(":3000", nil)) }

? 关键注意事项

  • Scopes 必须完整:仅 userinfo.profile 只返回 id, name, picture, locale;要获取 email、verified_email,必须额外声明 userinfo.email Scope
  • 避免直接拼接 access_token:虽然 https://www.googleapis.com/oauth2/v2/userinfo?access_token=xxx 在调试时可行,但存在安全风险(如日志泄露、URL 长度限制)。应始终使用 conf.Client(ctx, token) 创建自动携带 Authorization: Bearer … 头的客户端。
  • oauth2.NoContext 已弃用:Go 1.7+ 应使用 r.Context()(如 googleconf.Exchange(r.Context(), code)),确保上下文取消传播与超时控制。
  • 务必读取并关闭 resp.Body:否则连接会泄漏,且你看到的“大响应体”只是 *http.Response 结构体打印(含 Header、Request 等元信息),不是真正的用户数据。真实数据在 resp.Body 流中,需 io.ReadAll() 或 json.NewDecoder().Decode() 解析。
  • API Endpoint 推荐 oauth2/v2/userinfo:相比 userinfo/v2/me,该路径更稳定,且明确支持 email 字段(Google 官方文档推荐)。

? 补充:结构化解析用户信息(最佳实践)

为提升类型安全与可维护性,建议定义结构体并解码:

type GoogleUser struct {     ID            string `json:"id"`     Email         string `json:"email"`     VerifiedEmail bool   `json:"verified_email"`     Name          string `json:"name"`     Picture       string `json:"picture"`     GivenName     string `json:"given_name"`     FamilyName    string `json:"family_name"`     Locale        string `json:"locale"` }  // 替换上面的 io.ReadAll(...) 部分: var user GoogleUser if err := json.Unmarshal(body, &user); err != nil {     log.Printf("JSON decode error: %v", err)     http.Error(w, "invalid user data", http.StatusInternalServerError)     return } log.Printf("Logged in as: %s (%s)", user.Name, user.Email)

通过以上配置与实践,即可稳定、安全、可维护地完成 Google OAuth2 用户信息获取。无需引入第三方库——官方 golang.org/x/oauth2 经过充分测试,完全满足生产需求。

text=ZqhQzanResources