package user import ( "context" "yunyan/pb" "yunyan/sys/ipinfo" "encoding/json" "fmt" "io/ioutil" "net/http" "google.golang.org/api/oauth2/v2" "google.golang.org/api/option" ) type ( FacebookTokenDebugResponse struct { Data struct { AppID string `json:"app_id"` IsValid bool `json:"is_valid"` UserID string `json:"user_id"` ExpiresAt int64 `json:"expires_at"` Scopes []string `json:"scopes"` } `json:"data"` } WeChatTokenResponse struct { AccessToken string `json:"access_token"` ExpiresIn int `json:"expires_in"` RefreshToken string `json:"refresh_token"` OpenID string `json:"openid"` Scope string `json:"scope"` } WeChatUserInfoResponse struct { OpenID string `json:"openid"` Nickname string `json:"nickname"` HeadImgURL string `json:"headimgurl"` } ) func verifyGoogleIDToken(idToken string) (*oauth2.Tokeninfo, error) { ctx := context.Background() // 创建 OAuth2 服务 oauth2Service, err := oauth2.NewService(ctx, option.WithHTTPClient(http.DefaultClient)) if err != nil { return nil, fmt.Errorf("failed to create OAuth2 service: %v", err) } // 调用 Google 的 Tokeninfo 接口验证 ID Token tokenInfo, err := oauth2Service.Tokeninfo().IdToken(idToken).Do() if err != nil { return nil, fmt.Errorf("invalid ID Token: %v", err) } // 检查 Token 是否有效 if tokenInfo.VerifiedEmail != true { return nil, fmt.Errorf("email not verified") } return tokenInfo, nil } func verifyFacebookAccessToken(accessToken, facebookAppID, facebookAppSecret string) (string, error) { // 调用 Facebook 的 Debug Token 接口 url := fmt.Sprintf( "https://graph.facebook.com/debug_token?input_token=%s&access_token=%s|%s", accessToken, facebookAppID, facebookAppSecret, ) resp, err := http.Get(url) if err != nil { return "", fmt.Errorf("failed to call Facebook API: %v", err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return "", fmt.Errorf("failed to read response body: %v", err) } // 解析 Facebook 的响应 var tokenResponse FacebookTokenDebugResponse if err := json.Unmarshal(body, &tokenResponse); err != nil { return "", fmt.Errorf("failed to parse Facebook response: %v", err) } // 检查 Token 是否有效 if !tokenResponse.Data.IsValid { return "", fmt.Errorf("token is invalid") } // 检查 App ID 是否匹配 if tokenResponse.Data.AppID != facebookAppID { return "", fmt.Errorf("invalid app ID") } // 返回用户 ID return tokenResponse.Data.UserID, nil } func getWeChatAccessToken(code, wechatAppID, wechatAppSecret string) (*WeChatTokenResponse, error) { // 调用微信 API 获取 Access Token url := fmt.Sprintf( "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code", wechatAppID, wechatAppSecret, code, ) resp, err := http.Get(url) if err != nil { return nil, fmt.Errorf("failed to call WeChat API: %v", err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response body: %v", err) } // 解析微信的响应 var tokenResponse WeChatTokenResponse if err := json.Unmarshal(body, &tokenResponse); err != nil { return nil, fmt.Errorf("failed to parse WeChat response: %v", err) } return &tokenResponse, nil } func getWeChatUserInfo(accessToken, openID string) (*WeChatUserInfoResponse, error) { // 调用微信 API 获取用户信息 url := fmt.Sprintf( "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s", accessToken, openID, ) resp, err := http.Get(url) if err != nil { return nil, fmt.Errorf("failed to call WeChat API: %v", err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response body: %v", err) } // 解析微信的响应 var userInfo WeChatUserInfoResponse if err := json.Unmarshal(body, &userInfo); err != nil { return nil, fmt.Errorf("failed to parse WeChat response: %v", err) } return &userInfo, nil } // ToRegion 将 IPData 转换为区域枚举 // 优先按特大国家匹配,再按洲级别归类 func ToRegion(d *ipinfo.IPData) pb.Region { if d == nil { return pb.Region_RegionUnknown } // 优先匹配特大国家 switch d.CountryCode { case "CN", "HK", "MO": return pb.Region_RegionChina case "US": return pb.Region_RegionUSA case "RU": return pb.Region_RegionRussia case "BR": return pb.Region_RegionBrazil case "IN": return pb.Region_RegionIndia } // 亚洲内部细分 if d.ContinentCode == "AS" { switch d.CountryCode { // 东亚(日韩蒙等) case "JP", "KR", "MN", "TW": return pb.Region_RegionEastAsia // 东南亚 case "VN", "TH", "MY", "SG", "ID", "PH", "MM", "KH", "LA", "BN", "TL": return pb.Region_RegionSoutheastAsia // 南亚 case "PK", "BD", "LK", "NP", "BT", "MV", "AF": return pb.Region_RegionSouthAsia // 西亚/中东 case "SA", "AE", "IR", "IQ", "TR", "IL", "JO", "KW", "QA", "BH", "OM", "YE", "SY", "LB", "PS", "CY", "GE", "AM", "AZ", "UZ", "KZ", "TM", "TJ", "KG": return pb.Region_RegionMiddleEast default: return pb.Region_RegionEastAsia } } // 按洲归类 switch d.ContinentCode { case "EU": return pb.Region_RegionEurope case "NA": return pb.Region_RegionNorthAmerica case "SA": return pb.Region_RegionSouthAmerica case "AF": return pb.Region_RegionAfrica case "OC": return pb.Region_RegionOceania } return pb.Region_RegionUnknown }