在Go里面实现Google和Apple OAuth登录
本文由claude code编写,仅供参考。 在现代应用开发中,提供第三方登录已经成为标配功能。本文将详细介绍如何在Go语言中实现Google和Apple的OAuth登录,以及如何处理iOS和Android的原生登录。
一、OAuth 2.0 基础概念
OAuth 2.0是一个授权框架,允许应用在用户授权的情况下访问用户在第三方服务上的资源,而无需获取用户的密码。主要流程包括:
- 授权请求:引导用户到OAuth提供商的授权页面
- 授权确认:用户同意授权
- 获取授权码:重定向回应用并携带授权码
- 交换令牌:使用授权码换取访问令牌
- 访问资源:使用访问令牌获取用户信息
二、Google OAuth登录实现
2.1 前期准备
首先需要在Google Cloud Console创建项目并配置OAuth 2.0凭据:
- 访问 Google Cloud Console
- 创建新项目或选择现有项目
- 启用Google+ API或Google Identity服务
- 创建OAuth 2.0客户端ID(Web应用、iOS、Android分别创建)
- 配置授权重定向URI
2.2 安装依赖
go get golang.org/x/oauth2
go get golang.org/x/oauth2/google
go get google.golang.org/api/oauth2/v2
2.3 Web端Google登录实现
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
oauth2api "google.golang.org/api/oauth2/v2"
"google.golang.org/api/option"
)
var googleOauthConfig *oauth2.Config
func init() {
googleOauthConfig = &oauth2.Config{
ClientID: os.Getenv("GOOGLE_CLIENT_ID"),
ClientSecret: os.Getenv("GOOGLE_CLIENT_SECRET"),
RedirectURL: "http://localhost:8080/auth/google/callback",
Scopes: []string{
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile",
},
Endpoint: google.Endpoint,
}
}
// 处理Google登录请求
func handleGoogleLogin(w http.ResponseWriter, r *http.Request) {
// 生成随机state参数,防止CSRF攻击
state := generateRandomState()
// 将state存储到session中
saveStateToSession(r, w, state)
// 生成授权URL
url := googleOauthConfig.AuthCodeURL(state, oauth2.AccessTypeOffline)
http.Redirect(w, r, url, http.StatusTemporaryRedirect)
}
// 处理Google回调
func handleGoogleCallback(w http.ResponseWriter, r *http.Request) {
// 验证state参数
state := r.FormValue("state")
savedState := getStateFromSession(r)
if state != savedState {
http.Error(w, "Invalid state parameter", http.StatusBadRequest)
return
}
// 获取授权码
code := r.FormValue("code")
if code == "" {
http.Error(w, "Code not found", http.StatusBadRequest)
return
}
// 交换令牌
token, err := googleOauthConfig.Exchange(context.Background(), code)
if err != nil {
http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
return
}
// 获取用户信息
userInfo, err := getGoogleUserInfo(token)
if err != nil {
http.Error(w, "Failed to get user info: "+err.Error(), http.StatusInternalServerError)
return
}
// 处理用户信息(创建或更新用户)
handleUserLogin(w, r, userInfo)
}
// 获取Google用户信息
func getGoogleUserInfo(token *oauth2.Token) (*GoogleUserInfo, error) {
ctx := context.Background()
// 创建OAuth2服务
oauth2Service, err := oauth2api.NewService(ctx,
option.WithTokenSource(googleOauthConfig.TokenSource(ctx, token)))
if err != nil {
return nil, err
}
// 获取用户信息
userInfo, err := oauth2Service.Userinfo.Get().Do()
if err != nil {
return nil, err
}
return &GoogleUserInfo{
ID: userInfo.Id,
Email: userInfo.Email,
VerifiedEmail: userInfo.VerifiedEmail,
Name: userInfo.Name,
GivenName: userInfo.GivenName,
FamilyName: userInfo.FamilyName,
Picture: userInfo.Picture,
}, nil
}
type GoogleUserInfo struct {
ID string `json:"id"`
Email string `json:"email"`
VerifiedEmail bool `json:"verified_email"`
Name string `json:"name"`
GivenName string `json:"given_name"`
FamilyName string `json:"family_name"`
Picture string `json:"picture"`
}
2.4 Android/iOS原生Google登录处理
对于移动端原生登录,客户端会使用Google SDK获取ID Token,然后发送给后端验证:
评论
?
参与讨论