using System; using System.Collections.Generic; using System.Net.Http; using Sog; using LitJson; using ProtoCSStruct; using Sog.Crypto; #if true namespace HttpProxy { public class HeroUSDKAccountInfo { public string userId; public string userName; public int userType; public string openId; public string channel; public int channelId; public int compensation; public string bindEmail; } public class HeroUSDKAuth { // 正式地址 private static string init_loginUrl = "https://hgsdkcdn.herogame.com/hgsrv"; private static string init_getUserInfoUrl = "/v1/login/checkUserInfo.lg"; private static string init_postContentType = "multipart/form-data; boundary=-----------------"; private static string init_amountUrl = "http://47.75.149.207/hgadmin/api/ext/account/amount/query"; private static string _getUserInfoUrl; private static string _getUserInfoUrlBackup; private static string _postContentType; private static string[] _PCODE = new string[(int)HeroUSDKProj.Max]; private static string[] _APP_KEY = new string[(int)HeroUSDKProj.Max]; private static void InitDefaultProjectInfo() { // 游戏产品在SDK平台的应用ID, 默认值只是为了方便没有添加配置项的私人服务器 _PCODE[(int)HeroUSDKProj.Min] = "0"; _PCODE[(int)HeroUSDKProj.SEA] = "17310"; _PCODE[(int)HeroUSDKProj.JP] = "17303"; _PCODE[(int)HeroUSDKProj.KR] = "17323"; // 游戏产品在SDK平台的应用私钥, 默认值只是为了方便没有添加配置项的私人服务器 _APP_KEY[(int)HeroUSDKProj.Min] = "invalid_key"; _APP_KEY[(int)HeroUSDKProj.SEA] = "2dl4v39v93dmexqcvxku"; _APP_KEY[(int)HeroUSDKProj.JP] = "7czh206jaoh27zp5cv9x"; _APP_KEY[(int)HeroUSDKProj.KR] = "qqsm0uqbnx2omt2v7ox8"; } public static void Init(HttpProxyServerConfig config) { string loginUrl = config.heroUsdk_loginUrl; if (! string.IsNullOrEmpty(loginUrl)) { _getUserInfoUrl = loginUrl + init_getUserInfoUrl; } else { _getUserInfoUrl = init_loginUrl + init_getUserInfoUrl; } //如果配置了本地校验地址,则优先使用本地校验 if(!string.IsNullOrEmpty(config.heroUsdk_loginUrlLocalCheck)) { _getUserInfoUrl = config.heroUsdk_loginUrlLocalCheck; } if(! string.IsNullOrEmpty(config.heroUsdk_loginUrlBackup)) { _getUserInfoUrlBackup = config.heroUsdk_loginUrlBackup + init_getUserInfoUrl; } TraceLog.Trace("HeroUSDKAuth.Init getUserInfoUrl {0}", _getUserInfoUrl); _postContentType = init_postContentType + DateTime.Now.Ticks.ToString("x"); InitProjectInfo(config); } public static void InitProjectInfo(HttpProxyServerConfig config) { // 默认使用代码中的默认值, 方便没有配置的私人服务器 InitDefaultProjectInfo(); // 用config中的配置项替换默认值 if (config.heroUsdkProjCfg == null || config.heroUsdkProjCfg.Count == 0) { return; } foreach (HeroUsdkProjCfgOne one in config.heroUsdkProjCfg) { TraceLog.Trace("HeroUSDKAuth.InitProjectInfo cpId {0} cpName {1} pcode {2} appKey {3} callbackKey {4}" , one.cpId, one.cpName, one.pcode, one.appKey, one.callbackKey); if (0 <= one.cpId && one.cpId < _PCODE.Length && !string.IsNullOrEmpty(one.pcode)) { _PCODE[one.cpId] = one.pcode; } if (0 <= one.cpId && one.cpId < _APP_KEY.Length && !string.IsNullOrEmpty(one.appKey)) { _APP_KEY[one.cpId] = one.appKey; } } for (int i = (int)HeroUSDKProj.Min + 1; i < (int)HeroUSDKProj.Max; i++) { if (_PCODE[i] != null) { TraceLog.Trace("HeroUSDKAuth.InitProjectInfo {0} pcode {1}", (HeroUSDKProj)i, _PCODE[i]); } if (_APP_KEY[i] != null) { TraceLog.Trace("HeroUSDKAuth.InitProjectInfo {0} appKey {1}", (HeroUSDKProj)i, _APP_KEY[i]); } } } public static HeroUSDKProj GetProject(string pcode) { if (string.IsNullOrEmpty(pcode)) { return HeroUSDKProj.Min; } foreach (HeroUsdkProjCfgOne one in HttpProxyServerUtils.GetServerConfig().heroUsdkProjCfg) { if (one.pcode == pcode) { return (HeroUSDKProj)one.cpId; } } return HeroUSDKProj.Min; } //public static string CreateData(Dictionary dataParam) //{ // JsonData data = new JsonData(); // foreach (KeyValuePair pair in dataParam) // { // data[pair.Key] = new JsonData(pair.Value); // } // string jsonStr = data.ToJson(); // string finalData = Convert.ToBase64String(Encoding.ASCII.GetBytes(jsonStr)); // if (finalData.Length > 51) // { // char[] array = finalData.ToCharArray(); // SwapChar(array, 1, 33); // SwapChar(array, 10, 42); // SwapChar(array, 18, 50); // SwapChar(array, 19, 51); // finalData = new string(array); // } // return finalData; //} //private static void SwapChar(char[] str, int idx1, int idx2) //{ // if (idx1 >= 0 && idx2 >= 0 && str.Length > Math.Max(idx1, idx2)) // { // char tmp = str[idx1]; // str[idx1] = str[idx2]; // str[idx2] = tmp; // } //} //private static string CreateParamsStr(Dictionary allParam, bool sortParam) //{ // // sign不参与签名 // allParam.Remove("sign"); // StringBuilder sb = new StringBuilder(); // // 生成签名串, "k1=v1&k2=v2...&_appKey" // if (sortParam) // { // var sortPairs = from pair in allParam orderby pair.Key select pair; // foreach (KeyValuePair pair in sortPairs) // { // sb.Append(pair.Key).Append('=').Append(pair.Value).Append('&'); // } // } // else // { // foreach (KeyValuePair pair in allParam) // { // sb.Append(pair.Key).Append('=').Append(pair.Value).Append('&'); // } // } // sb.Append(_APP_KEY); // return sb.ToString(); //} //// 传入按字母排序后的请求参数, 不包含_appKey, 返回签名 //private static string CalcSign(Dictionary allParam) //{ // var str = HeroUSDKSecurity.CreateParamsStr(allParam, true); // byte[] md5Hash = _md5.ComputeHash(Encoding.UTF8.GetBytes(str)); // string md5Str = BitConverter.ToString(md5Hash).Replace("-", "").ToLower(); // //if (md5Str.Length > 23) // //{ // // char[] array = md5Str.ToCharArray(); // // SwapChar(array, 1, 13); // // SwapChar(array, 5, 17); // // SwapChar(array, 7, 23); // // md5Str = new string(array); // //} // return md5Str; //} public static HeroUSDKAccountInfo GetUserInfo(HeroUSDKProj proj, string cUid, string accessToken, int oldCompensation = 0) { if ((int) proj >= _PCODE.Length || (int) proj >= _APP_KEY.Length) { TraceLog.Error("HeroUSDKAuth.GetUserInfo invalid project {0}", proj); return null; } var dataParam = new Dictionary(); dataParam["cUid"] = cUid; dataParam["accessToken"] = accessToken; var allParams = new Dictionary(); allParams["pcode"] = _PCODE[(int)proj]; allParams["data"] = HeroUSDKSecurity.CreateData(dataParam); allParams["timestamp"] = HttpProxyServerUtils.GetTimeSecond().ToString(); // sign以外的字段全部添加到上面用于计算签名 allParams["sign"] = HeroUSDKSecurity.CalcSign(_APP_KEY[(int)proj], allParams); var content = new FormUrlEncodedContent(allParams); string ret = HttpUtils.HttpPost(_getUserInfoUrl, content, out bool exception); TraceLog.Error($"_getUserInfoUrl:{_getUserInfoUrl}\ncUid:{cUid}\naccessToken:{accessToken}\npcode:{allParams["pcode"]}\ndata:{allParams["data"]}\ntimestamp:{allParams["timestamp"]}\nsign:{allParams["sign"]}"); if (exception || ret == null) { TraceLog.Trace("HeroUSDKAuth.GetUserInfo call result exception {0} ret==null is {1}", exception, ret==null); //增加一个sdk校验地址备份功能,英雄的本地校验进程会挂(2022/12/21日挂掉过一次,导致玩家1个小时无法登录) if (!string.IsNullOrEmpty(_getUserInfoUrlBackup)) { TraceLog.Trace("HeroUSDKAuth.GetUserInfo with _getUserInfoUrlBackup {0}", _getUserInfoUrlBackup); ret = HttpUtils.HttpPost(_getUserInfoUrlBackup, content, out exception); } else { ret = HttpUtils.HttpPost(_getUserInfoUrl, content, out exception); } } if (ret == null) { TraceLog.Error("HeroUSDKAuth.GetUserInfo HttpPost return null query failed"); return null; } TraceLog.Trace("HeroUSDKAuth.GetUserInfo ret {0}", ret); JsonData jsonData = JsonMapper.ToObject(ret); JsonData codeData = jsonData["code"]; int code = codeData != null ? (int) codeData : -1; if (code != 0) { TraceLog.Trace("HeroUSDKAuth.GetUserInfo res code {0} not 0", code); return null; } if (! jsonData.ContainsKey("cUid")) { TraceLog.Trace("HeroUSDKAuth.GetUserInfo json cUid not exist"); return null; } HeroUSDKAccountInfo usdkAccount = new HeroUSDKAccountInfo {userId = jsonData["cUid"].ToString()}; if (jsonData.ContainsKey("cName")) { usdkAccount.userName = jsonData["cName"].ToString(); } if (jsonData.ContainsKey("ext")) { var extData = jsonData["ext"]; if (extData.ContainsKey("userType")) { usdkAccount.userType = (int)extData["userType"]; } if (extData.ContainsKey("openId")) { usdkAccount.openId = extData["openId"].ToString(); } if (extData.ContainsKey("bindEmail")) { usdkAccount.bindEmail = extData["bindEmail"].ToString(); } } if (jsonData.ContainsKey("channel")) { usdkAccount.channel = jsonData["channel"].ToString(); } // usdk对渠道id的定义 if (jsonData.ContainsKey("imgId")) { usdkAccount.channelId = (int)jsonData["imgId"]; } else { if (jsonData.ContainsKey("channelId")) { usdkAccount.channelId = (int)jsonData["channelId"]; } } //todo 测试模式 // usdkAccount.bindEmail = "lvyuedeyouxi@gmail.com"; //老玩家只会查询一次,新玩家由于没有bindEmail所以不会走到此接口 if (!string.IsNullOrEmpty(usdkAccount.bindEmail) && proj == HeroUSDKProj.JP2 && oldCompensation == 0) { TraceLog.Trace("HeroUSDKAuth.GetUserInfo get mail={0},uid={1}", usdkAccount.bindEmail, usdkAccount.userId); var from = new Dictionary { ["email"] = usdkAccount.bindEmail }; ret = HttpUtils.HttpPost(init_amountUrl, new FormUrlEncodedContent(from), out exception); TraceLog.Trace("HeroUSDKAuth.GetUserInfo request={0},result={1},", usdkAccount.bindEmail, ret); if (ret != null) { var resultJson = JsonMapper.ToObject(ret); var resultCode = resultJson["code"]; code = resultCode != null ? (int)resultCode : -1; if (code == 0) { var moneyData = resultJson["data"]; var compensation = 0.00; if (moneyData != null) { if (moneyData.IsInt) { compensation = (int)moneyData; } if (moneyData.IsDouble) { compensation = (double)moneyData; } if (moneyData.IsLong) { compensation = (long)moneyData; } } TraceLog.Trace("HeroUSDKAuth.GetUserInfo get mail={0},uid={1},compensation={2}", usdkAccount.bindEmail, usdkAccount.userId, compensation); //1.2倍 //1代表查询过了。但是不会发奖 usdkAccount.compensation = Math.Max(1, (int)(compensation * 1.2)); } } } usdkAccount.compensation = Math.Max(oldCompensation, usdkAccount.compensation); return usdkAccount; } #if HERO_AUTH_POST_FORM_DATA public static string GetAccessToken(string authCode) { string timeSec = HttpProxyServerUtils.GetTimeSecond().ToString(); var allParams = new Dictionary(); allParams["productId"] = _productId; allParams["code"] = authCode; allParams["timeStamp"] = timeSec; MultipartFormDataContent content = new MultipartFormDataContent(); content.Headers.Add("ContentType", _postContentType); content.Add(new StringContent(_productId), "productId"); content.Add(new StringContent(authCode), "code"); content.Add(new StringContent(timeSec), "timeStamp"); content.Add(new StringContent(CalcSign(allParams)), "sign"); string ret = HttpUtils.HttpPost(_getAccTokenUrl, content, out bool exception); if (ret == null) { TraceLog.Error("HeroUSDKAuth.GetAccessToken HttpPost return null query failed"); return null; } TraceLog.Trace("HeroUSDKAuth.GetAccessToken ret {0}", ret); JsonData jsonData = JsonMapper.ToObject(ret); JsonData codeData = jsonData["code"]; int code = codeData != null ? (int)codeData : -1; if (code != 200) { TraceLog.Trace("HeroUSDKAuth.GetAccessToken res code {0} not 200 (succ)", code); return null; } return jsonData["data"].ToString(); } public static bool GetUserInfo(string accessToken, out string userId, out string userName) { userId = null; userName = null; string timeSec = HttpProxyServerUtils.GetTimeSecond().ToString(); var allParams = new Dictionary(); allParams["productId"] = _productId; allParams["accessToken"] = accessToken; allParams["timeStamp"] = timeSec; MultipartFormDataContent content = new MultipartFormDataContent(); content.Headers.Add("ContentType", _postContentType); content.Add(new StringContent(_productId), "productId"); content.Add(new StringContent(accessToken), "accessToken"); content.Add(new StringContent(timeSec), "timeStamp"); content.Add(new StringContent(CalcSign(allParams)), "sign"); string ret = HttpUtils.HttpPost(_getUserInfoUrl, content, out bool exception); if (ret == null) { TraceLog.Error("HeroUSDKAuth.GetUserInfo HttpGet return null query failed"); return false; } TraceLog.Trace("HeroUSDKAuth.GetUserInfo ret {0}", ret); JsonData jsonData = JsonMapper.ToObject(ret); JsonData codeData = jsonData["code"]; int code = codeData != null ? (int)codeData : -1; if (code != 200) { TraceLog.Trace("HeroUSDKAuth.GetAccessToken res code {0} not 200 (succ)", code); return false; } JsonData data = jsonData["data"]; userId = data["userId"].ToString(); userName = data["userName"].ToString(); return true; } #endif } } #endif