golang 各个小游戏平台登录验证
现在小游戏的平台很多, 服务器要对接的平台也多的说不过, 这里废话不多说.
我这里只是提供思路, 和我已经实现的逻辑代码, 不能保证在你的项目,必定可以.
不喜勿碰.
我这里暂时只提供我对接过的平台, 其他的大致逻辑相似,
这里主要的平台有下面这个:
- 微信
- 字节跳动(抖音, 今日头条,等)
- 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
}
// 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×tamp=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×tamp=%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