Merge remote-tracking branch 'origin/master'
This commit is contained in:
@ -64,6 +64,10 @@ type GetUserBoundInfoReq struct {
|
|||||||
}
|
}
|
||||||
type GetUserBoundInfoRes struct {
|
type GetUserBoundInfoRes struct {
|
||||||
IsBound bool `json:"isBound" dc:"是否已绑定"`
|
IsBound bool `json:"isBound" dc:"是否已绑定"`
|
||||||
|
Nick string `json:"nick" dc:"昵称"`
|
||||||
|
AppNames []string `json:"appNames" dc:"已绑定游戏名称"`
|
||||||
|
Utype int8 `json:"utype" dc:"用户类型,1: qq,2: 微信"`
|
||||||
|
Ctime uint32 `json:"ctime" dc:"绑定时间"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetBoundUrlReq struct {
|
type GetBoundUrlReq struct {
|
||||||
|
|||||||
@ -13,5 +13,5 @@ func (c *ControllerV1) GetUserBoundInfo(ctx context.Context, req *v1.GetUserBoun
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &v1.GetUserBoundInfoRes{IsBound: out.IsBound}, nil
|
return &v1.GetUserBoundInfoRes{IsBound: out.IsBound, Nick: out.Nick, AppNames: out.AppNames, Utype: out.Utype, Ctime: out.Ctime}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,6 @@ package internal
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/go-resty/resty/v2"
|
|
||||||
"github.com/gogf/gf/v2/frame/g"
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
"server/internal/consts"
|
"server/internal/consts"
|
||||||
"server/internal/dao"
|
"server/internal/dao"
|
||||||
@ -268,20 +267,16 @@ func (s *sUser) UnBoundUrl(ctx context.Context, in *model.UserBoundUrlIn) (out *
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
func (s *sUser) BoundInfo(ctx context.Context, in *model.UserBoundInfoIn) (out *model.UserBoundInfoOut, err error) {
|
func (s *sUser) BoundInfo(ctx context.Context, in *model.UserBoundInfoIn) (out *model.UserBoundInfoOut, err error) {
|
||||||
url, err := gamelife.GetGamelifeClient(ctx).GetBound(ctx, in.PopenId)
|
result, err := gamelife.GetGamelifeClient(ctx).GetBound(ctx, in.PopenId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ecode.Fail.Sub("获取查询用户绑定信息 url 失败")
|
return nil, err
|
||||||
}
|
|
||||||
var result model.UserBoundResult
|
|
||||||
resp, err := resty.New().R().SetResult(&result).Post(url)
|
|
||||||
if err != nil {
|
|
||||||
return nil, ecode.Fail.Sub("调用游戏人生接口出现异常")
|
|
||||||
}
|
|
||||||
if resp.StatusCode() != 200 {
|
|
||||||
return nil, ecode.Fail.Sub("调用游戏人生接口失败")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &model.UserBoundInfoOut{
|
return &model.UserBoundInfoOut{
|
||||||
IsBound: result.Result,
|
IsBound: result.Result,
|
||||||
|
AppNames: result.AppNames,
|
||||||
|
Ctime: result.Ctime,
|
||||||
|
Nick: result.Nick,
|
||||||
|
Utype: result.Utype,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -138,6 +138,7 @@ type UserGamelifeCache struct {
|
|||||||
Aes string `json:"aes"`
|
Aes string `json:"aes"`
|
||||||
IV string `json:"iv"`
|
IV string `json:"iv"`
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
|
Params string `json:"params"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserBoundResult struct {
|
type UserBoundResult struct {
|
||||||
@ -145,7 +146,7 @@ type UserBoundResult struct {
|
|||||||
Nick string `json:"nick"`
|
Nick string `json:"nick"`
|
||||||
Ctime uint32 `json:"ctime"`
|
Ctime uint32 `json:"ctime"`
|
||||||
Utype int8 `json:"utype"`
|
Utype int8 `json:"utype"`
|
||||||
AppName []string `json:"app_name"`
|
AppNames []string `json:"app_names"`
|
||||||
}
|
}
|
||||||
type UserBoundUrlIn struct {
|
type UserBoundUrlIn struct {
|
||||||
PopenId string
|
PopenId string
|
||||||
@ -167,4 +168,8 @@ type UserBoundInfoIn struct {
|
|||||||
}
|
}
|
||||||
type UserBoundInfoOut struct {
|
type UserBoundInfoOut struct {
|
||||||
IsBound bool
|
IsBound bool
|
||||||
|
AppNames []string
|
||||||
|
Ctime uint32
|
||||||
|
Nick string
|
||||||
|
Utype int8
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,58 +1,31 @@
|
|||||||
package encrypt
|
package encrypt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// AesEncrypt 使用 AES-CBC 模式对数据进行加密。
|
// PKCS5 填充
|
||||||
//
|
func pkcs5Padding(src []byte, blockSize int) []byte {
|
||||||
// 参数:
|
padding := blockSize - len(src)%blockSize
|
||||||
// - plainText: 原始明文数据(必须是任意长度)
|
padText := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||||
// - key: 加密密钥(长度必须是 16、24 或 32 字节)
|
return append(src, padText...)
|
||||||
// - iv: 初始化向量(必须是 16 字节)
|
}
|
||||||
//
|
|
||||||
// 返回值:
|
func AesEncryptCBCPKCS5(plainText, key, iv []byte) ([]byte, error) {
|
||||||
// - 加密后的密文
|
|
||||||
// - 错误信息(如果加密失败)
|
|
||||||
func AesEncrypt(plainText, key, iv []byte) ([]byte, error) {
|
|
||||||
block, err := aes.NewCipher(key)
|
block, err := aes.NewCipher(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(iv) != aes.BlockSize {
|
|
||||||
return nil, errors.New("IV 长度必须为 16 字节")
|
|
||||||
}
|
|
||||||
|
|
||||||
plainText = pkcs7Padding(plainText, aes.BlockSize)
|
// PKCS5 填充(与 PKCS7 相同,只是 block size 为 8)
|
||||||
cipherText := make([]byte, len(plainText))
|
paddedText := pkcs5Padding(plainText, block.BlockSize())
|
||||||
|
|
||||||
|
cipherText := make([]byte, len(paddedText))
|
||||||
mode := cipher.NewCBCEncrypter(block, iv)
|
mode := cipher.NewCBCEncrypter(block, iv)
|
||||||
mode.CryptBlocks(cipherText, plainText)
|
mode.CryptBlocks(cipherText, paddedText)
|
||||||
|
|
||||||
return cipherText, nil
|
return cipherText, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// pkcs7Padding 对数据进行 PKCS7 填充。
|
|
||||||
//
|
|
||||||
// 参数:
|
|
||||||
// - data: 原始数据
|
|
||||||
// - blockSize: 块大小(通常为 16)
|
|
||||||
//
|
|
||||||
// 返回值:
|
|
||||||
// - 填充后的数据
|
|
||||||
func pkcs7Padding(data []byte, blockSize int) []byte {
|
|
||||||
padding := blockSize - len(data)%blockSize
|
|
||||||
padText := bytesRepeat(byte(padding), padding)
|
|
||||||
return append(data, padText...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// bytesRepeat 返回一个重复 count 次的字节切片。
|
|
||||||
func bytesRepeat(b byte, count int) []byte {
|
|
||||||
buf := make([]byte, count)
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
buf[i] = b
|
|
||||||
}
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|||||||
@ -48,8 +48,8 @@ func newgamelifeClient(ctx context.Context) *gamelifeClient {
|
|||||||
"prod": "https://h5.cafe.qq.com/pmd-mobile.cafe.bind-account.pc/#/bind-manage",
|
"prod": "https://h5.cafe.qq.com/pmd-mobile.cafe.bind-account.pc/#/bind-manage",
|
||||||
},
|
},
|
||||||
getBoundUrl: map[string]string{
|
getBoundUrl: map[string]string{
|
||||||
"test": "https://api-test。cafe.qq.com/tipmp.user.authinfoo_cgi.authinfo_cgi/GetPlatUserInfo",
|
"test": "https://api-test.cafe.qq.com/tipmp.user.authinfo_cgi.authinfo_cgi/GetPlatUserInfo",
|
||||||
"prod": "https://api.cafe.qq.com/tipmp.user.authinfoo_cgi.authinfo_cgi/GetPlatUserInfo",
|
"prod": "https://api.cafe.qq.com/tipmp.user.authinfo_cgi.authinfo_cgi/GetPlatUserInfo",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
glog.Infof(ctx, "初始化 gamelifeClient 成功")
|
glog.Infof(ctx, "初始化 gamelifeClient 成功")
|
||||||
@ -167,10 +167,6 @@ func (s *gamelifeClient) GetUrl(ctx context.Context, popenid, appname, nickname
|
|||||||
if !isBound {
|
if !isBound {
|
||||||
rooturl = s.unBoundUrlMap[s.Mode]
|
rooturl = s.unBoundUrlMap[s.Mode]
|
||||||
}
|
}
|
||||||
baseUrl, err := url.Parse(rooturl)
|
|
||||||
if err != nil {
|
|
||||||
return "", ecode.Fail.Sub("解析基础 URL 失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
cacheData, err := g.Redis().Get(ctx, fmt.Sprintf(consts.GameLifeUserKey, popenid))
|
cacheData, err := g.Redis().Get(ctx, fmt.Sprintf(consts.GameLifeUserKey, popenid))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -203,48 +199,48 @@ func (s *gamelifeClient) GetUrl(ctx context.Context, popenid, appname, nickname
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 加密用户信息
|
// 加密用户信息
|
||||||
aesEncrypt, err := encrypt.AesEncrypt(marshal, []byte(gamelifeCache.Aes), []byte(gamelifeCache.IV))
|
aesEncrypt, err := encrypt.AesEncryptCBCPKCS5(marshal, []byte(gamelifeCache.Aes), []byte(gamelifeCache.IV))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", ecode.Fail.Sub("加密用户信息失败")
|
return "", ecode.Fail.Sub("加密用户信息失败")
|
||||||
}
|
}
|
||||||
platUserInfoStr := encrypt.Base64Encode(aesEncrypt)
|
platUserInfoStr := encrypt.Base64Encode(aesEncrypt)
|
||||||
explatData := g.MapStrStr{"Token": gamelifeCache.Token, "PlatUserInfoStr": platUserInfoStr}
|
explatData := g.MapStrStr{"Token": gamelifeCache.Token, "PlatUserInfoStr": platUserInfoStr}
|
||||||
|
queryParams := url.Values{}
|
||||||
// 构建查询参数
|
|
||||||
queryParams := baseUrl.Query()
|
|
||||||
queryParams.Add("app_name", appname)
|
|
||||||
queryParams.Add("mini_program_band", consts.GamelifeMiniProgramBand)
|
|
||||||
queryParams.Add("extplat_plat", s.PlatId)
|
queryParams.Add("extplat_plat", s.PlatId)
|
||||||
queryParams.Add("extplat_type", consts.GamelifeExtplatType)
|
queryParams.Add("app_name", appname)
|
||||||
queryParams.Add("extplat_extra", consts.GamelifeExtplatExtraPc)
|
|
||||||
queryParams.Add("extplat_data", url.QueryEscape(gconv.String(explatData)))
|
|
||||||
|
|
||||||
// 根据 isBound 设置 bind_type 和 nickname
|
|
||||||
if bindType == 1 {
|
if bindType == 1 {
|
||||||
queryParams.Add("bind_type", consts.GamelifeExtplatBoundTypeQQ)
|
queryParams.Add("bind_type", consts.GamelifeExtplatBoundTypeQQ)
|
||||||
} else {
|
} else {
|
||||||
queryParams.Add("bind_type", consts.GamelifeExtplatBoundTypeWX)
|
queryParams.Add("bind_type", consts.GamelifeExtplatBoundTypeWX)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 仅在解绑时添加 nickname
|
queryParams.Add("extplat_type", consts.GamelifeExtplatType)
|
||||||
|
queryParams.Add("mini_program_band", consts.GamelifeMiniProgramBand)
|
||||||
|
queryParams.Add("extplat_extra", consts.GamelifeExtplatExtraPc)
|
||||||
|
queryParams.Add("extplat_data", gconv.String(explatData))
|
||||||
|
|
||||||
|
// 解绑时加 nickname
|
||||||
if !isBound {
|
if !isBound {
|
||||||
queryParams.Add("nickname", nickname)
|
queryParams.Add("nickname", nickname)
|
||||||
}
|
}
|
||||||
|
// 将请求参数更新到缓存中
|
||||||
|
gamelifeCache.Params = queryParams.Encode()
|
||||||
|
|
||||||
baseUrl.RawQuery = queryParams.Encode()
|
// 拼接最终 URL
|
||||||
return baseUrl.String(), nil
|
url := fmt.Sprintf("%s?%s", rooturl, queryParams.Encode())
|
||||||
|
return url, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBound 获取用户绑定情况
|
// GetBound 获取用户绑定情况
|
||||||
func (s *gamelifeClient) GetBound(ctx context.Context, popenid string) (string, error) {
|
func (s *gamelifeClient) GetBound(ctx context.Context, popenid string) (*model.UserBoundResult, error) {
|
||||||
baseUrl, err := url.Parse(s.getBoundUrl[s.Mode])
|
// 获取基础 URL
|
||||||
if err != nil {
|
rooturl := s.getBoundUrl[s.Mode]
|
||||||
return "", ecode.Fail.Sub("解析基础 URL 失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 获取缓存
|
||||||
cacheData, err := g.Redis().Get(ctx, fmt.Sprintf(consts.GameLifeUserKey, popenid))
|
cacheData, err := g.Redis().Get(ctx, fmt.Sprintf(consts.GameLifeUserKey, popenid))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", ecode.Fail.Sub("从缓存中获取用户信息失败")
|
return nil, ecode.Fail.Sub("从缓存中获取用户信息失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
var gamelifeCache model.UserGamelifeCache
|
var gamelifeCache model.UserGamelifeCache
|
||||||
@ -252,38 +248,44 @@ func (s *gamelifeClient) GetBound(ctx context.Context, popenid string) (string,
|
|||||||
// 如果缓存不存在或已过期,重新调用 GetUserKeyIV
|
// 如果缓存不存在或已过期,重新调用 GetUserKeyIV
|
||||||
data, err := s.GetUserKeyIV(ctx, popenid)
|
data, err := s.GetUserKeyIV(ctx, popenid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", ecode.Fail.Sub("获取用户信息失败")
|
return nil, ecode.Fail.Sub("获取用户信息失败")
|
||||||
}
|
}
|
||||||
gamelifeCache = model.UserGamelifeCache{Aes: data.Aes, IV: data.IV, Token: data.Token}
|
gamelifeCache = model.UserGamelifeCache{Aes: data.Aes, IV: data.IV, Token: data.Token}
|
||||||
} else {
|
} else {
|
||||||
// 缓存存在,直接解析
|
|
||||||
if err = json.Unmarshal(cacheData.Bytes(), &gamelifeCache); err != nil {
|
if err = json.Unmarshal(cacheData.Bytes(), &gamelifeCache); err != nil {
|
||||||
return "", ecode.Fail.Sub("解析用户信息失败")
|
return nil, ecode.Fail.Sub("解析用户信息失败")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 序列化原始数据
|
|
||||||
|
// 加密原始数据
|
||||||
oriData := g.Map{
|
oriData := g.Map{
|
||||||
"PopenId": popenid,
|
"PopenId": popenid,
|
||||||
"TimeStamp": time.Now().Unix(),
|
"TimeStamp": time.Now().Unix(),
|
||||||
}
|
}
|
||||||
marshal, err := json.Marshal(oriData)
|
marshal, err := json.Marshal(oriData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", ecode.Fail.Sub("序列化用户信息失败")
|
return nil, ecode.Fail.Sub("序列化用户信息失败")
|
||||||
}
|
}
|
||||||
|
aesEncrypt, err := encrypt.AesEncryptCBCPKCS5(marshal, []byte(gamelifeCache.Aes), []byte(gamelifeCache.IV))
|
||||||
// 加密用户信息
|
|
||||||
aesEncrypt, err := encrypt.AesEncrypt(marshal, []byte(gamelifeCache.Aes), []byte(gamelifeCache.IV))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", ecode.Fail.Sub("加密用户信息失败")
|
return nil, ecode.Fail.Sub("加密用户信息失败")
|
||||||
}
|
}
|
||||||
platUserInfoStr := encrypt.Base64Encode(aesEncrypt)
|
platUserInfoStr := encrypt.Base64Encode(aesEncrypt)
|
||||||
explatData := g.MapStrStr{"Token": gamelifeCache.Token, "PlatUserInfoStr": platUserInfoStr}
|
|
||||||
|
|
||||||
queryParams := baseUrl.Query()
|
postBody := g.MapStrStr{
|
||||||
queryParams.Add("plat_id", s.PlatId)
|
"plat_id": s.PlatId,
|
||||||
queryParams.Add("plat_user_info", popenid)
|
"plat_user_info": popenid,
|
||||||
queryParams.Add("plat_user_str", url.QueryEscape(gconv.String(explatData)))
|
"plat_user_str": gconv.String(g.MapStrStr{"Token": gamelifeCache.Token, "PlatUserInfoStr": platUserInfoStr}),
|
||||||
baseUrl.RawQuery = queryParams.Encode()
|
}
|
||||||
|
var result model.UserBoundResult
|
||||||
return baseUrl.String(), nil
|
resp, err := resty.New().R().SetBody(postBody).SetResult(&result).Post(rooturl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, ecode.Fail.Sub("向游戏人生获取绑定信息出现异常")
|
||||||
|
}
|
||||||
|
if resp.StatusCode() != 200 {
|
||||||
|
return nil, ecode.Fail.Sub("向游戏人生获取绑定信息失败")
|
||||||
|
}
|
||||||
|
glog.Infof(ctx, "获取用户游戏人生绑定信息: %v", result)
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,7 +63,7 @@ type (
|
|||||||
func GenerateToken(in *TokenIn) (string, error) {
|
func GenerateToken(in *TokenIn) (string, error) {
|
||||||
expire := in.ExpireTime
|
expire := in.ExpireTime
|
||||||
if expire <= 0 {
|
if expire <= 0 {
|
||||||
expire = 2 * time.Hour
|
expire = 24 * time.Hour
|
||||||
}
|
}
|
||||||
|
|
||||||
claims := jwtClaims{
|
claims := jwtClaims{
|
||||||
|
|||||||
Reference in New Issue
Block a user