Loading

golang 各个小游戏平台登录验证

现在小游戏的平台很多, 服务器要对接的平台也多的说不过, 这里废话不多说.
我这里只是提供思路, 和我已经实现的逻辑代码, 不能保证在你的项目,必定可以.
不喜勿碰.

我这里暂时只提供我对接过的平台, 其他的大致逻辑相似,

这里主要的平台有下面这个:

  • 微信
  • 字节跳动(抖音, 今日头条,等)
  • QQ
  • oppo
  • vivo
  • 百度
  • 快手
  • 趣头条
  • uc
  • wifi
  • 小米

首先我说明一下我对接这个登录的意图是拿到唯一标示.如果客户端可以拿到,我将不会对接他们的验证逻辑.

基础代码

// 对内置的 http的 Post 和 Get 做了一个简单的封装

func HttpPost(url, params string, contentType string, retMap interface{}) error {
	if contentType == "" {
		contentType = "application/json"
		// "application/x-www-form-urlencoded"
	}
	resp, err := http.Post(url,
		contentType,
		strings.NewReader(params))
	if err != nil {
		return err
	}

	defer func() {
		_ = resp.Body.Close()
	}()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	err = json.Unmarshal(body, retMap)
	if err != nil {
		return err
	}
	return nil
}

func HttpGet(url string, retMap interface{}) error {
	resp, err := http.Get(url)
	if err != nil {
		return err
	}

	defer func() {
		_ = resp.Body.Close()
	}()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	err = json.Unmarshal(body, retMap)
	if err != nil {
		return err
	}
	return nil
}

微信

type WeChatRetRes struct {
	Openid     string `json:"openid"`
	SessionKey string `json:"session_key"`
	Unionid    string `json:"unionid"`
	Errcode    int    `json:"errcode"`
	ErrMsg     string `json:"err_msg"`
}

// 微信验证登录
func SendWeChat(code, appId, appSecret string, retMap *WeChatRetRes) error {

	url := "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code"

	getUrl := fmt.Sprintf(url, appId, appSecret, code)

	err := HttpGet(getUrl, retMap)

	if err != nil {
		return err
	}

	if retMap.Errcode != 0 {
		return errors.New(fmt.Sprintf("code:%d,errmsg:%s", retMap.Errcode, retMap.ErrMsg))
	}

	return nil
}

QQ

// https://api.q.qq.com
type QQRetRes struct {
	Openid    string `json:"openid"`
	SessonKey string `json:"sesson_key"`
	Unionid   string `json:"unionid"`
	Errcode   int    `json:"errcode"`
	ErrMsg    string `json:"errmsg"`
}

func SendQQ(appId, appSecret, code string, retMap *QQRetRes) error {
	urlFormat := "https://api.q.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code"

	url := fmt.Sprintf(urlFormat, appId, appSecret, code)

	Log.Log.Debugf("QQ Check Login, param ===> %s", url)

	return HttpGet(url, retMap)
}

字节跳动

type ByteDanceRetRes struct {
	Openid          string `json:"openid"`
	SessonKey       string `json:"session_key"`
	AnonymousOpenid string `json:"anonymous_openid"`
	Errcode         int    `json:"errcode"`
	ErrMsg          string `json:"errmsg"`
}

func SendByteDance(appId, appSecret, code string, noLogin bool, retMap *ByteDanceRetRes) error {
	urlFormat := "https://developer.toutiao.com/api/apps/jscode2session?appid=%s&secret=%s&%s=%s"

	p := "code"

	if noLogin {
		p = "anonymous_code"
	}

	url := fmt.Sprintf(urlFormat, appId, appSecret, p, code)

	Log.Log.Debugf("ByteDance Check Login, param ===> %s", url)

	return HttpGet(url, retMap)
}

百度

type BaiDuRetRes struct {
	OpenId     string `json:"openid"`
	SessionKey string `json:"session_key"`
	Error      string `json:"error"`
	ErrorMsg   string `json:"error_description"`
}

func SendBaiDu(appKey, appSecret, code string, retMap *BaiDuRetRes) error {
	param := fmt.Sprintf("code=%s&client_id=%s&sk=%s", code, appKey, appSecret)

	Log.Log.Debugf("BaiDu Check Login, param ===> %s", param)
	return HttpPost("https://openapi.baidu.com/nalogin/getSessionKeyByCode/", param, "", retMap)
}

UC

type uCRetErrorRes struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
}

type uCRetDataRes struct {
	OpenId     string `json:"open_id"`     // 用户身份标识
	SessionKey string `json:"session_key"` // 用户的 Session Key,可能较长,最大长度为 512 字节,请留意
}

type UCRetRes struct {
	Data  uCRetDataRes  `json:"data"`
	Error uCRetErrorRes `json:"error"`
}

func SendUC(appId, clientId, clientKey, code string, retMap *UCRetRes) error {
	/*
		url: # curl -XPOST -d "app_id=xx&client_id=xx&code=xx&request_id=xx&timestamp=xx&sign=xx" -H 'Content-Type:application/x-www-form-urlencoded' https://open-auth.uc.cn/auth.code2Session

		属性		是否必填		描述
		request_id		是		请求标识,需要保证一段时间内唯一,强烈推荐使用 UUID
		code			是		通过上面第一步所获得的 Authorization Code
		app_id			是		小游戏的标识
		client_id		是		小游戏的标识
		timestamp		是		时间戳
		sign			是		请求签名

		$param_str: 将参数的 key 和 value 用 =  拼接,升序排序后用 & 连接
		$sign = MD5($client_id + $client_key + $request_id + $param_str)
	*/
	requestId := GetUUID()
	paramStr := fmt.Sprintf("app_id=%s&client_id=%s&code=%s&request_id=%s&timestamp=%d", appId, clientId, code, requestId, time.Now().Unix())
	sign := MD5(clientId + clientKey + requestId + paramStr)

	paramStr += "&sign=" + sign

	Log.Log.Debugf("UC Check Login, param ===> %s", paramStr)
	return HttpPost("https://open-auth.uc.cn/auth.code2Session", paramStr, "", retMap)
}

趣头条

type qTTDataRetRes struct {
	OpenId   string `json:"open_id"`
	NickName string `json:"nickname"`
	Avatar   string `json:"avatar"`
	UnionId  string `json:"union_id"`
}
type QTTRetRes struct {
	Code        int           `json:"code"`
	Message     string        `json:"message"`
	ErrCode     int           `json:"showErr"`
	CurrentTime int           `json:"currentTime"`
	Data        qTTDataRetRes `json:"data"`
}

func qttSign(values url.Values, appSecrect string) (r string) {
	values.Del("sign")
	values.Add("app_key", appSecrect)
	keys := make([]string, 0, len(values))
	for k, _ := range values {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, v := range keys {
		r += v + values.Get(v)
	}
	hashed := md5.Sum([]byte(r))
	r = fmt.Sprintf("%x", hashed)
	values.Del("app_key")
	return
}

func SendQtt(appId, appSecret, code, platform string, retMap *QTTRetRes) error {
	values := url.Values{}
	values.Add("app_id", appId)
	values.Add("platform", platform)
	values.Add("ticket", code)
	values.Add("time", fmt.Sprintf("%d", time.Now().Unix()))
	s := qttSign(values, appSecret)
	values.Add("sign", s)

	url := "https://newidea4-gamecenter-backend.1sapp.com/x/open/user/ticket?" + values.Encode()
	return HttpGet(url, retMap)
}

小米

// 小米验证登录
type MIRetRes struct {
	Code  int    `json:"errcode"` /*200   验证正确	1515 appId 错误	1516 uid 错误	1520 session 错误	1525 signature 错误 	4002 appid, uid, session 不匹配(常见为session过期)*/
	Msg   string `json:"errMsg"`
	Adult int    `json:"adult"` /*用户实名标识。 407 实名认证通过,年龄大于18岁  408 实名认证通过,年龄小于18岁    409 未进行实名认证*/
}

func SendMI(appid, appSecret, uid, session string, retMap *MIRetRes) error {
	var param = fmt.Sprintf("appId=%s&session=%s&uid=%s", appid, session, uid)
	//hmac ,use sha1
	key := []byte(appSecret)
	mac := hmac.New(sha1.New, key)
	mac.Write([]byte(param))
	//mac.Sum(nil)
	param += "&signature=" + hex.EncodeToString(mac.Sum(nil))
	return HttpPost("https://mis.migc.xiaomi.com/api/biz/service/loginvalidate", param, "application/x-www-form-urlencoded", retMap)
}

Vivo

func Random16() int {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	num := 0
	arr := []int{1, 10000, 100000000, 1000000000000}
	for i := 0; i < 4; i++ {
		a := r.Intn(10000)
		num += arr[i] * a
	}
	return num
}

type vivoDataRetRes struct {
	OpenId       string `json:"openId"`
	NickName     string `json:"nickName"`
	Avatar       string `json:"smallAvatar"`
	BiggerAvatar string `json:"biggerAvatar"`
	Gender       int    `json:"gender"`
}
type VivoRetRes struct {
	Code    int            `json:"code"`
	Message string         `json:"msg"`
	Data    vivoDataRetRes `json:"data"`
}

func SendVivo(appId, appSecret, token, packName string, retMap interface{}) error {
	timestamp := time.Now().UnixNano() / 1e6
	values := url.Values{}
	values.Add("token", token)
	values.Add("pkgName", packName)
	values.Add("timestamp", fmt.Sprintf("%d", timestamp))
	values.Add("nonce", fmt.Sprintf("%d", Random16()))
	urlStr := "https://quickgame.vivo.com.cn/api/quickgame/cp/account/userInfo?" + values.Encode()

	values.Add("appKey", appId)
	values.Add("appSecret", appSecret)

	hash := sha256.New()
	hash.Write([]byte(values.Encode()))
	bytes := hash.Sum(nil)
	hashCode := hex.EncodeToString(bytes)
	urlStr += "&signature=" + hashCode

	return HttpGet(urlStr, retMap)
}

其他平台

这里面我没有对接的平台有: vivo, oppo, 快手,因为这些平台你可拿到唯一标示.
我这里没有写调用的例子, 我相信这一部分代码你可以自己来.
我这里更新了一下vivo的登录验证, 因为最新的版本在审核的时候不让用低版本的登录方式,所以就填上验证方式.

需要注意的点

在百度的那个里面需要接入, 游客登录, 资产转移等逻辑, 这个也比较简单的你们看他们的文档的流程就可以实现,这里不多说.

ヾ( ̄▽ ̄)ByeBye

posted @ 2020-05-19 13:45  丫丫魏  阅读(528)  评论(0编辑  收藏  举报