添加google第三方登录

This commit is contained in:
junleea 2025-04-30 14:37:36 +08:00
parent 97ce265dab
commit 4ea300f522
5 changed files with 143 additions and 33 deletions

View File

@ -31,7 +31,8 @@ type User struct {
type ThirdPartyUserInfo struct {
gorm.Model
UserID int `json:"user_id"` // 用户ID,本系统的用户id
ThirdPartyID int `json:"third_party_id"` // 第三方用户ID
ThirdPartyID string `json:"third_party_id"` // 第三方用户ID
ThirdPartyEmail string `json:"third_party_email"` // 第三方平台用户邮箱
ThirdPartyPlatform string `json:"third_party_platform"` // 第三方平台名称,qq,github
ThirdPartyUserName string `json:"third_party_user_name"` // 第三方用户名
ThirdPartyUserAvatar string `json:"third_party_user_avatar"` // 第三方用户头像
@ -217,7 +218,7 @@ func FindThirdPartyUserInfoByUserID(userID int) []ThirdPartyUserInfo {
}
// 根据平台用户id获取信息
func FindThirdPartyUserInfoByThirdPartyID(thirdPartyID int) []ThirdPartyUserInfo {
func FindThirdPartyUserInfoByThirdPartyID(thirdPartyID string) []ThirdPartyUserInfo {
var thirdPartyUserInfo []ThirdPartyUserInfo
DB.Where("third_party_id = ?", thirdPartyID).First(&thirdPartyUserInfo)
return thirdPartyUserInfo
@ -230,7 +231,7 @@ func FindThirdPartyUserInfoByPlatformAndUserID(thirdPartyPlatform string, userID
return thirdPartyUserInfo
}
func CreateThirdPartyUserInfo(userID, thirdPartyID int, thirdPartyPlatform, thirdPartyUserName, thirdPartyUserAvatar, thirdPartyUserUrl string) uint {
func CreateThirdPartyUserInfo(userID int, thirdPartyID, thirdPartyPlatform, thirdPartyUserName, thirdPartyUserAvatar, thirdPartyUserUrl string) uint {
thirdPartyUserInfo := ThirdPartyUserInfo{UserID: userID, ThirdPartyID: thirdPartyID, ThirdPartyPlatform: thirdPartyPlatform, ThirdPartyUserName: thirdPartyUserName, ThirdPartyUserAvatar: thirdPartyUserAvatar, ThirdPartyUserUrl: thirdPartyUserUrl}
res := DB.Create(&thirdPartyUserInfo)
if res.Error != nil {
@ -268,7 +269,7 @@ func DeleteThirdPartyLoginByID(id int, userID int) error {
}
// 更新第三方登录用户信息
func UpdateThirdPartyUserInfoByThirdPartyID(thirdPartyID int, thirdPartyPlatform, thirdPartyUserName, thirdPartyUserAvatar, thirdPartyUserUrl string) error {
func UpdateThirdPartyUserInfoByThirdPartyID(thirdPartyID, thirdPartyPlatform, thirdPartyUserName, thirdPartyUserAvatar, thirdPartyUserUrl string) error {
db2 := DB
if proto.Config.SERVER_SQL_LOG {
db2 = DB.Debug()

View File

@ -809,9 +809,9 @@ func GetThirdPartyAuthUrl(c *gin.Context) {
case "google":
params := url.Values{}
params.Add("client_id", worker.GoogleClientID)
params.Add("response_type", "code")
params.Add("response_type", "token") //直接返回token
params.Add("redirect_uri", "https://pm.ljsea.top/tool/third_party_callback")
params.Add("scope", "https://www.googleapis.com/auth/drive.metadata.readonly+https://www.googleapis.com/auth/calendar.readonly")
params.Add("scope", "https://www.googleapis.com/auth/userinfo.email+https://www.googleapis.com/auth/userinfo.profile+openid")
params.Add("state", stateBase64Str)
}
resp.Message = "success"
@ -844,7 +844,7 @@ func handleThirdPartyCallback(c *gin.Context) {
if err != nil {
log.Println("json unmarshal error:", err)
} else {
service.DoThirdPartyCallBack(&state, code)
service.DoThirdPartyCallBack(c, &state, code)
}
}
resp.Code = 0

View File

@ -133,6 +133,15 @@ type GiteeOAuthRequest struct {
GrantType string `json:"grant_type"`
}
type ThirdPartyUserInfo struct {
UserID string `json:"user_id"` // 第三方用户ID
Email string `json:"email"` // 第三方平台用户邮箱
Avatar string `json:"avatar"` // 第三方平台用户头像
Name string `json:"name"` // 第三方平台用户名
Url string `json:"url"` // 第三方平台用户主页,可选
// 其他信息
}
// github返回用户信息
type GitHubUserInfo struct {
LoginUserName string `json:"login"` // 用户名
@ -141,6 +150,16 @@ type GitHubUserInfo struct {
Url string `json:"url"` // 用户主页
}
type GoogleUserInfoResp 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"`
}
// 国外服务器负责请求的请求
type OnlineServerReq struct {
Type string `json:"type" form:"type"` // 请求类型,get,post
@ -168,3 +187,11 @@ type OutlineServerReqResp struct {
Message string `json:"message"` // 响应信息
Data OutlineServerResp `json:"data"` // 响应数据
}
// google回调信息
type GoogleOAuthCallback struct {
Code string `json:"code"` // code
AccessToken string `json:"access_token"` // access_token
Scope string `json:"scope"` // scope
State string `json:"state"` // state
}

View File

@ -7,6 +7,7 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt"
"log"
"regexp"
@ -335,6 +336,75 @@ func DoGogsCallBack(state *proto.ThirdPartyLoginState, code string) {
// 处理第三方登录状态
func HandleThirdPartyLoginStatus(state *proto.ThirdPartyLoginState, thirdPartyLoginStatus *proto.ThirdPartyLoginStatus, userInfo *proto.GitHubUserInfo) {
if state.Type == "login" {
//根据第三方平台查找用户
thirdPartyUserInfoList := dao.FindThirdPartyUserInfoByThirdPartyID(strconv.Itoa(userInfo.UserID))
if thirdPartyUserInfoList == nil || len(thirdPartyUserInfoList) == 0 {
thirdPartyLoginStatus.Status = proto.ThirdPartyUserNotBinded //未绑定用户
} else {
thirdPartyUserInfo := thirdPartyUserInfoList[0]
//获取用户信息
user := GetUserByIDWithCache(thirdPartyUserInfo.UserID)
if user.ID == 0 {
thirdPartyLoginStatus.Status = proto.ThirdPartyUserNotBinded
log.Println("get user by id error")
} else {
//成功
thirdPartyLoginStatus.Status = proto.SuccessCode
thirdPartyLoginStatus.UserInfo.UserID = int(user.ID)
thirdPartyLoginStatus.UserInfo.Username = user.Name
thirdPartyLoginStatus.UserInfo.Email = user.Email
thirdPartyLoginStatus.UserInfo.Token, _ = GenerateJWTToken(int(user.ID), user.Name)
}
}
} else if state.Type == "add" {
//根据第三方平台查找用户
thirdPartyUserInfoList := dao.FindThirdPartyUserInfoByThirdPartyID(strconv.Itoa(userInfo.UserID))
if thirdPartyUserInfoList != nil && len(thirdPartyUserInfoList) > 0 {
thirdPartyLoginStatus.Status = 3 //已绑定用户
} else {
userIDStr := worker.GetRedis("user_add_platform_" + state.UUID)
if userIDStr == "" {
log.Println("user id is empty")
thirdPartyLoginStatus.Status = 2 //未绑定用户
} else {
//字符串转int
userID, _ := strconv.Atoi(userIDStr)
//根据用户ID获取用户信息
user := GetUserByIDWithCache(userID)
if user.ID == 0 {
thirdPartyLoginStatus.Status = 4 //添加用户信息错误
log.Println("get user by id error")
} else {
//需要创建数据库记录
data := dao.ThirdPartyUserInfo{UserID: userID, ThirdPartyID: strconv.Itoa(userInfo.UserID), ThirdPartyPlatform: state.Platform, ThirdPartyUserAvatar: userInfo.AvatarUrl, ThirdPartyUserName: userInfo.LoginUserName, ThirdPartyUserUrl: userInfo.Url}
uid := dao.CreateThirdPartyUserInfoV2(&data)
if uid == 0 {
log.Println("create third party user info error")
thirdPartyLoginStatus.Status = proto.OperationFailed //操作错误
} else {
//成功
thirdPartyLoginStatus.Status = proto.SuccessCode
thirdPartyLoginStatus.UserInfo.UserID = int(user.ID)
thirdPartyLoginStatus.UserInfo.Username = user.Name
thirdPartyLoginStatus.UserInfo.Email = user.Email
thirdPartyLoginStatus.UserInfo.Token, _ = GenerateJWTToken(int(user.ID), user.Name)
}
}
}
}
} else {
log.Println("DoGithubCallBack state type error:", state.Type)
thirdPartyLoginStatus.Status = proto.ParameterError //参数错误
}
//更新userInfo到数据库
err := dao.UpdateThirdPartyUserInfoByThirdPartyID(strconv.Itoa(userInfo.UserID), state.Platform, userInfo.LoginUserName, userInfo.AvatarUrl, userInfo.Url)
if err != nil {
log.Println("update third party user info error:", err)
}
}
func HandleThirdPartyLoginStatusV2(state *proto.ThirdPartyLoginState, thirdPartyLoginStatus *proto.ThirdPartyLoginStatus, userInfo *proto.ThirdPartyUserInfo) {
if state.Type == "login" {
//根据第三方平台查找用户
thirdPartyUserInfoList := dao.FindThirdPartyUserInfoByThirdPartyID(userInfo.UserID)
@ -376,7 +446,7 @@ func HandleThirdPartyLoginStatus(state *proto.ThirdPartyLoginState, thirdPartyLo
log.Println("get user by id error")
} else {
//需要创建数据库记录
data := dao.ThirdPartyUserInfo{UserID: userID, ThirdPartyID: userInfo.UserID, ThirdPartyPlatform: state.Platform, ThirdPartyUserAvatar: userInfo.AvatarUrl, ThirdPartyUserName: userInfo.LoginUserName, ThirdPartyUserUrl: userInfo.Url}
data := dao.ThirdPartyUserInfo{UserID: userID, ThirdPartyID: userInfo.UserID, ThirdPartyPlatform: state.Platform, ThirdPartyUserAvatar: userInfo.Avatar, ThirdPartyUserName: userInfo.Name, ThirdPartyUserUrl: userInfo.Url, ThirdPartyEmail: userInfo.Email}
uid := dao.CreateThirdPartyUserInfoV2(&data)
if uid == 0 {
log.Println("create third party user info error")
@ -397,13 +467,13 @@ func HandleThirdPartyLoginStatus(state *proto.ThirdPartyLoginState, thirdPartyLo
thirdPartyLoginStatus.Status = proto.ParameterError //参数错误
}
//更新userInfo到数据库
err := dao.UpdateThirdPartyUserInfoByThirdPartyID(userInfo.UserID, state.Platform, userInfo.LoginUserName, userInfo.AvatarUrl, userInfo.Url)
err := dao.UpdateThirdPartyUserInfoByThirdPartyID(userInfo.UserID, state.Platform, userInfo.Name, userInfo.Avatar, userInfo.Url)
if err != nil {
log.Println("update third party user info error:", err)
}
}
func DoThirdPartyCallBack(state *proto.ThirdPartyLoginState, code string) {
func DoThirdPartyCallBack(c *gin.Context, state *proto.ThirdPartyLoginState, code string) {
switch state.Platform {
case "github":
DoGithubCallBack(state, code)
@ -415,39 +485,34 @@ func DoThirdPartyCallBack(state *proto.ThirdPartyLoginState, code string) {
// TODO
log.Println("DoThirdPartyCallBack gogs error:", state.Platform)
case "google":
DoGoogleCallBack(state, code)
accessToken := c.Query("access_token")
DoGoogleCallBack(state, accessToken)
default:
log.Println("DoThirdPartyCallBack platform error:", state.Platform)
}
}
func DoGoogleCallBack(state *proto.ThirdPartyLoginState, code string) {
//获取Access Token
resp, err := worker.GetGiteeAccessTokenByCode(code, "https://pm.ljsea.top/tool/gitee_callback", proto.Config.GITEE_CLIENT_ID, proto.Config.GITEE_CLIENT_SECRET)
if err != nil {
log.Println("get google access token error:", err)
func DoGoogleCallBack(state *proto.ThirdPartyLoginState, accessToken string) {
if accessToken == "" {
log.Println("get google access token is empty")
return
}
if resp.AccessToken == "" {
log.Println("get gitee access token is empty")
log.Println("get gitee access token error:", resp)
return
}
log.Println("get gitee access token:", resp.AccessToken)
log.Println("get google access token:", accessToken)
//获取用户信息
userInfo, err := worker.GetGiteeUserInfo(resp.AccessToken)
userInfo, err := worker.GetGoogleUserInfo(accessToken)
if err != nil {
log.Println("get gitee user info error:", err)
log.Println("get google user info error:", err)
return
}
log.Println("get gitee user info:", userInfo)
log.Println("get github user info:", userInfo)
var thirdPartyLoginStatus proto.ThirdPartyLoginStatus
thirdPartyLoginStatus.Type = state.Platform
HandleThirdPartyLoginStatus(state, &thirdPartyLoginStatus, &userInfo)
thirdPartyUserInfo := proto.ThirdPartyUserInfo{UserID: userInfo.ID, Name: userInfo.Name, Avatar: userInfo.Picture, Email: userInfo.Email}
HandleThirdPartyLoginStatusV2(state, &thirdPartyLoginStatus, &thirdPartyUserInfo)
//更新redis中的第三方登录状态
thirdPartyLoginStatusStr, _ := json.Marshal(thirdPartyLoginStatus)
log.Println("do handle gitee callback success, third party login status:", string(thirdPartyLoginStatusStr))
log.Println("do handle google callback success, third party login status:", string(thirdPartyLoginStatusStr))
worker.SetRedisWithExpire(state.UUID, string(thirdPartyLoginStatusStr), time.Minute*10)
}

View File

@ -245,14 +245,31 @@ func GetGoogleAccessTokenByCode(code string, redirectURI string, clientID string
}
func GetGoogleUserInfo(accessToken string) (proto.GitHubUserInfo, error) {
var resp proto.GitHubUserInfo
url := "https://www.googleapis.com/oauth2/v1/userinfo?access_token=" + accessToken
err, respBytes := DoGetRequest(url, nil)
func GetGoogleUserInfo(accessToken string) (proto.GoogleUserInfoResp, error) {
var resp proto.GoogleUserInfoResp
url := "https://www.googleapis.com/oauth2/v2/userinfo?access_token=" + accessToken
var onlineReq proto.OnlineServerReq
onlineReq.Type = "get"
onlineReq.Url = url
superTokens := GetRedisSetMembers("super_permission_tokens")
onlineReqBytes, _ := json.Marshal(onlineReq)
headers := map[string]string{
"token": superTokens[0],
"super_id": "1",
}
log.Println("GetGoogleUserInfo onlineReqBytes:", string(onlineReqBytes))
err, respBytes := DoPostRequestJSON("https://vis.ljsea.top/tool/online_server_request?super_id=1", onlineReqBytes, headers)
log.Println("GetGoogleUserInfo respBytes:", string(respBytes))
var onlineResp proto.OutlineServerReqResp
err = json.Unmarshal(respBytes, &onlineResp)
if err != nil {
return resp, err
}
err = json.Unmarshal([]byte(onlineResp.Data.Response.Response), &resp)
//err, respBytes := DoGetRequest(url, nil)
//if err != nil {
// return resp, err
//}
err = json.Unmarshal(respBytes, &resp)
if err != nil {
return resp, err