在Go里面实现Google和Apple OAuth登录

本文由claude code编写,仅供参考。 在现代应用开发中,提供第三方登录已经成为标配功能。本文将详细介绍如何在Go语言中实现Google和Apple的OAuth登录,以及如何处理iOS和Android的原生登录。

一、OAuth 2.0 基础概念

OAuth 2.0是一个授权框架,允许应用在用户授权的情况下访问用户在第三方服务上的资源,而无需获取用户的密码。主要流程包括:

  1. 授权请求:引导用户到OAuth提供商的授权页面
  2. 授权确认:用户同意授权
  3. 获取授权码:重定向回应用并携带授权码
  4. 交换令牌:使用授权码换取访问令牌
  5. 访问资源:使用访问令牌获取用户信息

二、Google OAuth登录实现

2.1 前期准备

首先需要在Google Cloud Console创建项目并配置OAuth 2.0凭据:

  1. 访问 Google Cloud Console
  2. 创建新项目或选择现有项目
  3. 启用Google+ API或Google Identity服务
  4. 创建OAuth 2.0客户端ID(Web应用、iOS、Android分别创建)
  5. 配置授权重定向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,然后发送给后端验证:

添加评论
点赞收藏
点踩分享查看原文
评论
?
参与讨论