diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 8211174..ec3693b 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -5,6 +5,7 @@ + diff --git a/handler/tool.go b/handler/tool.go index 2b80747..7914d53 100644 --- a/handler/tool.go +++ b/handler/tool.go @@ -825,6 +825,13 @@ func GetThirdPartyAuthUrl(c *gin.Context) { params.Add("response_type", "code") //直接返回token params.Add("state", stateBase64Str) respUrl = fmt.Sprintf("%s?%s", proto.FacebookAuthorizeBaseUrl, params.Encode()) + case "stackoverflow": + params := url.Values{} + params.Add("client_id", worker.StackOverflowClientID) + params.Add("redirect_uri", "https://pm.ljsea.top/tool/third_party_callback") + //params.Add("scope", "") + params.Add("state", stateBase64Str) + respUrl = fmt.Sprintf("%s?%s", proto.StackOverflowAuthorizeBaseUrl, params.Encode()) default: log.Println("platform not support:", platform) } diff --git a/proto/status.go b/proto/status.go index 9a5eb89..e120d9f 100644 --- a/proto/status.go +++ b/proto/status.go @@ -189,9 +189,10 @@ const ( // 第三方登录设计url const ( - GitHuAuthorizeBaseUrl = "https://github.com/login/oauth/authorize" - QQAuthorizeBaseUrl = "https://graph.qq.com/oauth2.0/authorize" - GiteeAuthorizeBaseUrl = "https://gitee.com/oauth/authorize" - GoogleAuthorizeBaseUrl = "https://accounts.google.com/o/oauth2/v2/auth" - FacebookAuthorizeBaseUrl = "https://www.facebook.com/v22.0/dialog/oauth" + GitHuAuthorizeBaseUrl = "https://github.com/login/oauth/authorize" + QQAuthorizeBaseUrl = "https://graph.qq.com/oauth2.0/authorize" + GiteeAuthorizeBaseUrl = "https://gitee.com/oauth/authorize" + GoogleAuthorizeBaseUrl = "https://accounts.google.com/o/oauth2/v2/auth" + FacebookAuthorizeBaseUrl = "https://www.facebook.com/v22.0/dialog/oauth" + StackOverflowAuthorizeBaseUrl = "https://stackoverflow.com/oauth" ) diff --git a/proto/tool.go b/proto/tool.go index c76e73e..2617061 100644 --- a/proto/tool.go +++ b/proto/tool.go @@ -234,3 +234,42 @@ type FaceBookUserInfoPictureData struct { IsSilhouette bool `json:"is_silhouette"` Url string `json:"url"` } + +/***************************Stackoverflow****************************/ +type StackoverflowOAuthResponse struct { + AccessToken string `json:"access_token"` + Expires int `json:"expires"` +} + +type StackoverflowUserInfoBadgeCounts struct { + Bronze int `json:"bronze"` + Silver int `json:"silver"` + Gold int `json:"gold"` +} + +type StackoverflowUserInfo struct { + BadgeCounts StackoverflowUserInfoBadgeCounts `json:"badge_counts"` + AccountID int `json:"account_id"` + IsEmployee bool `json:"is_employee"` + LastAccessDate int `json:"last_access_date"` + ReputationChangeYear int `json:"reputation_change_year"` + ReputationChangeQuarter int `json:"reputation_change_quarter"` + ReputationChangeMonth int `json:"reputation_change_month"` + ReputationChangeWeek int `json:"reputation_change_week"` + ReputationChangeDay int `json:"reputation_change_day"` + Reputation int `json:"reputation"` + CreationDate int `json:"creation_date"` + UserType string `json:"user_type"` + UserID int `json:"user_id"` + Link string `json:"link"` + ProfileImage string `json:"profile_image"` + DisplayName string `json:"display_name"` +} + +type StackoverflowUserInfoResponse struct { + Items []StackoverflowUserInfo `json:"items"` + HasMore bool `json:"has_more"` + Backoff int `json:"backoff"` + QuotaMax int `json:"quota_max"` + QuotaRemaining int `json:"quota_remaining"` +} diff --git a/service/toolService.go b/service/toolService.go index 80fd713..ad78c2e 100644 --- a/service/toolService.go +++ b/service/toolService.go @@ -487,6 +487,8 @@ func DoThirdPartyCallBack(state *proto.ThirdPartyLoginState, code string) { DoGoogleCallBack(state, code) case "facebook": DoFaceBookCallBack(state, code) + case "stackoverflow": + // TODO default: log.Println("DoThirdPartyCallBack platform error:", state.Platform) } @@ -544,6 +546,39 @@ func DoFaceBookCallBack(state *proto.ThirdPartyLoginState, code string) { worker.SetRedisWithExpire(state.UUID, string(thirdPartyLoginStatusStr), time.Minute*10) } +func DoStackoverflowCallBack(state *proto.ThirdPartyLoginState, code string) { + //根据code获取Access Token + tokenResp, err := worker.GetStackoverflowAccessTokenByCode(code, "https://pm.ljsea.top/tool/third_party_callback", worker.StackOverflowClientID, worker.StackOverflowClientSecret) + + if tokenResp.AccessToken == "" { + log.Println("get Stackoverflow access token is empty") + return + } + log.Println("get Stackoverflow access token:", tokenResp) + //获取用户信息 + userInfoResp, err := worker.GetStackoverflowUserInfo(tokenResp.AccessToken) + if err != nil { + log.Println("get Stackoverflow user info error:", err) + return + } + log.Println("get Stackoverflow user info:", userInfoResp) + var userInfo proto.StackoverflowUserInfo + if userInfoResp.Items != nil && len(userInfoResp.Items) > 0 { + userInfo = userInfoResp.Items[0] + } else { + log.Println("get Stackoverflow user info is empty") + return + } + var thirdPartyLoginStatus proto.ThirdPartyLoginStatus + thirdPartyLoginStatus.Type = state.Platform + thirdPartyUserInfo := proto.ThirdPartyUserInfo{UserID: strconv.Itoa(userInfo.UserID), Name: userInfo.DisplayName, Avatar: userInfo.ProfileImage, Email: ""} + HandleThirdPartyLoginStatusV2(state, &thirdPartyLoginStatus, &thirdPartyUserInfo) + //更新redis中的第三方登录状态 + thirdPartyLoginStatusStr, _ := json.Marshal(thirdPartyLoginStatus) + log.Println("do handle Stackoverflow callback success, third party login status:", string(thirdPartyLoginStatusStr)) + worker.SetRedisWithExpire(state.UUID, string(thirdPartyLoginStatusStr), time.Minute*10) +} + // 国外服务器处理国内服务器要请求外网的请求 func DoRequestToForeignServer(req *proto.OnlineServerReq) (proto.OutlineServerResp, error) { var resp proto.OutlineServerResp diff --git a/worker/thirdParty.go b/worker/thirdParty.go index aff2004..279b884 100644 --- a/worker/thirdParty.go +++ b/worker/thirdParty.go @@ -5,6 +5,7 @@ import ( "bytes" "encoding/json" "errors" + "fmt" "io" "log" "net/http" @@ -373,3 +374,82 @@ func GetFaceBookUserInfo(accessToken string) (proto.FaceBookUserInfoResp, error) } return resp, nil } + +const ( + StackOverflowClientID = "32093" + StackOverflowClientSecret = "cgHOI0emSLU7VB7jpAY9NQ((" + StackOverflowKey = "rl_s6vbPNPhbFbMcyWp7YbaTeg18" +) + +func GetStackoverflowAccessTokenByCode(code string, redirectURI string, clientID string, clientSecret string) (proto.StackoverflowOAuthResponse, error) { + var resp proto.StackoverflowOAuthResponse + + url := "https://stackoverflow.com/oauth/access_token/json" + req := proto.FaceBookOAuthRequest{ + ClientID: clientID, + ClientSecret: clientSecret, + Code: code, + RedirectURI: redirectURI, + } + log.Println("GetFacebookAccessTokenByCode url:", url) + var onlineReq proto.OnlineServerReq + onlineReq.Type = "post" + onlineReq.PostType = "form" + onlineReq.Url = url + onlineReq.Data = req + superTokens := GetRedisSetMembers("super_permission_tokens") + //onlineReq.Data = req + onlineReqBytes, _ := json.Marshal(onlineReq) + headers := map[string]string{ + "token": superTokens[0], + "super_id": "1", + } + + log.Println("GetStackoverflowAccessTokenByCode onlineReqBytes:", string(onlineReqBytes)) + err, respBytes := DoPostRequestJSON("https://vis.ljsea.top/tool/online_server_request?super_id=1", onlineReqBytes, headers) + log.Println("GetStackoverflowAccessTokenByCode 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) + if err != nil { + return resp, err + } + return resp, nil + +} + +func GetStackoverflowUserInfo(accessToken string) (proto.StackoverflowUserInfoResponse, error) { + var resp proto.StackoverflowUserInfoResponse + url := "https://api.stackexchange.com/2.3/me?site=stackoverflow&order=desc&sort=reputation" + url = fmt.Sprintf("%s&access_token=%s&key=%s", url, accessToken, StackOverflowKey) + 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("GetStackoverflowUserInfo onlineReqBytes:", string(onlineReqBytes)) + err, respBytes := DoPostRequestJSON("https://vis.ljsea.top/tool/online_server_request?super_id=1", onlineReqBytes, headers) + log.Println("GetStackoverflowUserInfo 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 + } + return resp, nil +}