You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

213 lines
5.4 KiB

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
}