From 4ea300f522efbc61e292996383f461a33393e61a Mon Sep 17 00:00:00 2001 From: junleea <354425203@qq.com> Date: Wed, 30 Apr 2025 14:37:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0google=E7=AC=AC=E4=B8=89?= =?UTF-8?q?=E6=96=B9=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dao/user.go | 9 ++-- handler/tool.go | 6 +-- proto/tool.go | 27 +++++++++++ service/toolService.go | 107 +++++++++++++++++++++++++++++++++-------- worker/thirdParty.go | 27 +++++++++-- 5 files changed, 143 insertions(+), 33 deletions(-) diff --git a/dao/user.go b/dao/user.go index ef1a917..1b3054a 100644 --- a/dao/user.go +++ b/dao/user.go @@ -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() diff --git a/handler/tool.go b/handler/tool.go index b200643..332cca6 100644 --- a/handler/tool.go +++ b/handler/tool.go @@ -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 diff --git a/proto/tool.go b/proto/tool.go index dcd8ca1..b6c6b1b 100644 --- a/proto/tool.go +++ b/proto/tool.go @@ -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 +} diff --git a/service/toolService.go b/service/toolService.go index 9658565..b75dd24 100644 --- a/service/toolService.go +++ b/service/toolService.go @@ -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) } diff --git a/worker/thirdParty.go b/worker/thirdParty.go index ce45714..6283b79 100644 --- a/worker/thirdParty.go +++ b/worker/thirdParty.go @@ -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