| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675 |
- <?php
- /**
- * sdk接口公共控制器类
- *
- */
- namespace app\api\controller;
- use app\common\controller\Base;
- use app\common\model\App;
- use think\Config;
- use think\Cache;
- use think\Db;
- use app\common\model\PolychannelGame;
- use think\Env;
- use think\Request;
- class Api extends Base
- {
- protected $noNeedLogin = []; // 不验证登录的接口, 例如:['index', 'login']
- protected $appInfo = ''; //游戏的app信息
- protected $input = []; //客户端提交的参数
- protected $isApiDebug = false; //接 口测试请求时
- protected $code_num = 0; // 开启报错自定义code码
- protected $request_frequency_handle = ['v2.user/personalIdentity']; // 请求频率管控的路由
- protected $authKey = 'auth_key'; // token和密码 加密的key
- protected $aesKey = 'aes_key'; // 加密解密(安卓/IOS/H5)
- protected $device = 0; // 设备来源:1=PC, 2=安卓, 3=苹果, 4=H5
- protected $version = ''; // 当前请求版本
- /**
- * 定义当前设备来源,并判断相关签名方式
- */
- protected function initDevice(){
- // 获取 header 设备号(主要针对 H5)
- $device = Request::instance()->header('device', 0, 'intval');
- if ($device > 0) {
- $this->device = $device;
- // if($device == 4){
- // $this->aesKey = 'aes_key_h5';
- // }
- }
- }
- /**
- * 初始化操作
- */
- public function _initialize()
- {
- self::initDevice();
- //密文
- $data = file_get_contents('php://input');
- log_message('Api@_initialize.sign_input:' . $data, 'log', LOG_PATH . 'apilog/');
- $decryptStr = $this->opensslDecrypt($data, Env::get($this->aesKey)); // 解密后的明文
- $api_debug = Request::instance()->header('qmapidebug', false, 'boolval'); // 是否开启API调试模式
- // 解密后的明文为空时
- if (!$decryptStr || $api_debug == true) {
- // 设置不加密
- if ($api_debug == true) {
- $this->isApiDebug = true;
- }
- $this->input = input();
- if(count($this->input) <= 0){
- $this->jsonResult('', -111, 'API数据解密失败,请确认加密数据!');
- }
- } else {
- $params = json_decode($decryptStr, true);
- if (JSON_ERROR_NONE == json_last_error()) {
- $this->input = $params;
- } else {
- $this->jsonResult('', -112, 'API解码失败,请检查数据格式!');
- }
- }
- // 获取 body 的设备号(主要针对 安卓/IOS客户端)
- $device = $this->input('device', 0, 'intval');
- if ($device > 0 && $this->device == 0) {
- $this->device = $device;
- }
- // trace('input:' . json_encode($this->input), 'Api@_initialize.input');
- log_message("Api@_initialize.input: {$device} -- " . json_encode($this->input), 'log', LOG_PATH . 'apilog/');
- /********************* 接口签名验证 start *********************/
- $appid = $this->input('appid'); //游戏的appid
- if (empty($appid)) {
- $this->jsonResult('', -121, '密钥ID不能为空');
- } else {
- $appModel = new App;
- $this->appInfo = $appModel->field('id,appkey,client_appkey,gameid')->where(['id' => $appid])->find();
- if (empty($this->appInfo)) {
- $this->jsonResult('', -122, '当前应用不存在!');
- } elseif (empty($this->appInfo['appkey'])) {
- $this->jsonResult('', -123, '当前应用配置有误!');
- }
-
- //签名验证
- if (!$this->md5Verify($this->input, $this->input('sign')) && Config::get('api_debug') == false && $api_debug == false) {
- $this->jsonResult('', -124, '签名不能通过,被拒绝访问');
- }
- }
- /********************* 接口签名验证 end *********************/
- // 母包渠道ID
- if (empty($this->input['channel_id'])) {
- $this->input['channel_id'] = Config::get('initial_channel_id');
- }
- $this->version = str_ireplace('v', '', $this->input('version'));
- // 校验登录
- $this->checkLogin();
- }
- /**
- * md5方式接口数据验签
- *
- * @param $data array 待签名数据
- * @param $sign string 签名结果
- *
- * @return boolean 验签结果
- */
- private function md5Verify($data, $sign)
- {
- $data = $this->paraFilter($data);
- ksort($data); // 按key升序排列
- reset($data);
-
- //拼接待签名字符串
- $arg = "";
- foreach ($data as $key => $val) {
- $arg .= $key . "=" . $val;
- }
- //如果存在转义字符,那么去掉转义
- if (get_magic_quotes_gpc()) {
- $arg = stripslashes($arg);
- }
- //添加客户端appkey参数值
- $arg .= $this->appInfo['client_appkey'];
- //全部转化成小写
- $arg = mb_strtolower($arg, 'UTF-8');
- $arg = $this->romanToUppercase($arg);
- $mysgin = md5($arg);
- if (strcasecmp($mysgin, $sign) == 0){
- // trace('数据验签 - 客户端sign:' . $sign . ' - 服务端sign:' . $mysgin . ' - ' . $arg, 'Api@md5Verify.error');
- return true;
- } else {
- trace('数据验签失败 - 客户端sign:' . $sign . ' - 服务端sign:' . $mysgin . ' - ' . $arg, 'Api@md5Verify.error');
- return false;
- }
- }
- private function romanToUppercase($roman)
- {
- $uppercase = array(
- 'Ⅰ', 'Ⅱ', 'Ⅲ', 'Ⅳ', 'Ⅴ', 'Ⅵ', 'Ⅶ', 'Ⅷ', 'Ⅸ', 'Ⅹ', 'Ⅺ', 'Ⅻ'
- );
- $lowercase = array(
- 'ⅰ', 'ⅱ', 'ⅲ', 'ⅳ', 'ⅴ', 'ⅵ', 'ⅶ', 'ⅷ', 'ⅸ', 'ⅹ', 'ⅺ', 'ⅻ'
- );
- return str_replace($uppercase, $lowercase, $roman);
- }
- /**
- * 过滤不参与签名的参数
- *
- * @param $para array 待签名数据
- *
- * @return array
- */
- private function paraFilter($para)
- {
- $para_filter = array();
- // while (list ($key, $val) = each ($para)) {
- foreach ($para as $key => $val) {
- if ($key == "sign") continue;
- else $para_filter[$key] = $para[$key];
- }
- return $para_filter;
- }
- /**
- * 使用openssl库进行加密
- * @param string $data 加密数据
- * @param string $key 加密用的key
- * @param string $method 加密方法
- * @return string 加密后的密文
- */
- private function opensslEncrypt($data, $key, $method = 'AES-128-ECB')
- {
- $str = openssl_encrypt($data, $method, $key);
- return $str;
- }
- /**
- * 使用openssl库进行解密
- * @param string $data 解密密文
- * @param string $key 解密用的key
- * @param string $method 解密方法
- *
- * @return string 解密后的明文
- */
- protected function opensslDecrypt($data, $key, $method = 'AES-128-ECB')
- {
- try {
- $str = openssl_decrypt($data, $method, $key);
- } catch (\Exception $e) {
- $this->jsonResult('', 0, '接口参数解密失败' . $e->getMessage() . ',请检查参数格式!');
- }
- return $str;
- }
- /**
- * 获取参数
- * @param string $key 数组index
- * @param string $default 默认值
- * @return mixed|null
- */
- protected function input($key, $default = null)
- {
- return isset($this->input[$key]) ? $this->input[$key] : ($default !== null ? $default : null);
- }
- /**
- * 校验登录
- */
- protected function checkLogin()
- {
- $controller = request()->controller();
- $action = request()->action();
- if (strpos($controller, '.') !== false) {
- list($version, $controller) = explode('.', $controller);
- }
- // 需要检验登录的操作
- if (!in_array($controller, ['register', 'sendSms', 'CheckUpdate', 'complexLogin', 'startup', 'forget', 'privacy', 'config', 'Third']) && !in_array($action, $this->noNeedLogin)) {
- $imeil = $this->input('imeil');
- if (empty($imeil)) {
- $this->jsonResult('', -131, '设备IMEI不能为空! ');
- }
- $token = $this->input('token', Request::instance()->header('token'));
- if (empty($token)) {
- $this->jsonResult('', -132, '未登录! ');
- }
- $redisImeil = Cache::store('redis')->get($token);
- if (!$redisImeil) {
- $this->jsonResult('', -133, '登陆失效,请重新登录!', 'json');
- } elseif ($redisImeil != $imeil) {
- $this->jsonResult('', -134, '您的账号已在其他设备上登录!');
- }
- try {
- $tokenStr = auth_code($token, "DECODE", Env::get($this->authKey));
- if(!$tokenStr){
- $this->jsonResult('', -135, '登陆异常,请重新登录!', 'json');
- }
- $tokenArr = array_pad(explode('|', $tokenStr), 7, '');
- // version > 3.6.8 之后才会有 v3 标识,用于自动登录时候判断老版本的jwt_token的
- list($userid, $username, $gameid, $sub_username, $token_random, $type, $token_version) = $tokenArr; // 103487|13121711111|244|103487|1tRh9iUu|sdk|v3
- // if($this->input('device') == 4){
- // // $userInfo = model('common/Member')->where(['userid' => $userid, 'gameid' => $gameid])->find();
- // }
- }catch (\Exception $e){
- $this->jsonResult('', -136, '登陆异常,请重新登录!!', 'json');
- }
- $redisTokenRandom = Cache::store('redis')->get('token|' . $type . '|' . $userid . '|' . $gameid);
- //redis中玩家登录安全控制的随机码不存在时
- if (!$redisTokenRandom) {
- Cache::store('redis')->rm($token);
- $this->jsonResult('', -137, '登陆异常,请重新登录!', 'json');
- } //两个token随机码不同时
- elseif ($redisTokenRandom != $token_random) {
- Cache::store('redis')->rm($token);
- $this->jsonResult('', -138, '登陆失败,请重新登录!', 'json');
- }
- $this->input['userid'] = $userid;
- $this->input['gameid'] = $gameid;
- $this->input['username'] = $username;
- $this->input['token'] = $token;
- $this->input['sub_username'] = $sub_username;
- $this->input['member_channel_id'] = getChannelId($userid, $gameid); // 当前玩家绑定的渠道
- $this->input['token_version'] = $token_version;
- $path = request()->path();
- if(in_array($path, $this->request_frequency_handle)){
- if(requestFrequencyHandle($username, $path) == false){
- $this->jsonResult('', -138, '操作过于频繁!', 'json');
- }
- }
- }
- }
- /**
- * 重新给客户端提交的参数赋值
- *
- * @param $key :键
- * @param $value :值
- */
- public function setInput($key, $value)
- {
- $this->input[$key] = $value;
- }
- /**
- * 保存设备信息
- *
- * @param $game_id : int 游戏ID
- * @param $imei : string 游戏imei码
- * @param $platform : int 0:安卓 1:IOS
- *
- * @return boolean
- */
- protected function saveDevices($game_id, $imei, $platform)
- {
- $os_version = trim($this->input('os_version')); //操作系统版本
- if (empty($os_version) || $platform == null || empty($this->input('phone_brand')) || empty($this->input('phone_model'))) {
- return false;
- }
- //设备信息记录不存在时
- if (!Db::table('nw_devices')->where(['game_id' => $game_id, 'imei' => $imei, 'os_version' => $os_version])->find()) {
- $devicesData['game_id'] = $game_id;
- $devicesData['platform'] = !empty($platform) ? 1 : 0;
- $devicesData['imei'] = $imei;
- $devicesData['phone_brand'] = $this->input('phone_brand');
- $devicesData['phone_model'] = $this->input('phone_model');
- $devicesData['os_version'] = $os_version;
- $devicesData['update_time'] = NOW_TIMESTAMP;
- $devicesData['create_time'] = NOW_TIMESTAMP;
- Db::table('nw_devices')->insert($devicesData);
- return true;
- }
- return false;
- }
- /**
- * 保存玩家游戏区服信息
- *
- * @param $game_id : int 游戏ID
- * @param $imei : string 游戏imei码
- * @param $platform : int 0:安卓 1:IOS
- *
- * @return boolean
- */
- protected function saveGameServer($userid, $gameid, $imeil = '', $source = 'role')
- {
- $serverid = $this->input('serverid', '', 'trim');
- $servername = $this->input('servername', '', 'trim');
- $roleid = $this->input('roleid', '', 'trim');
- $rolename = $this->input('rolename', '', 'trim');
- $viplevel = $this->input('viplevel', '', 'trim');
- $rolelevel = $this->input('rolelevel', '', 'trim');
- //游戏ID不能为空
- if (empty($gameid)) {
- return false;
- } elseif (empty($userid) || empty($servername) || empty($serverid) || empty($roleid) || empty($rolename) || empty($imeil)) {
- return false;
- }
- $memberGameInfo = model('MemberChannelGame')->where(['member_id' => $userid, 'game_id' => $gameid])->find();
- if (empty($memberGameInfo)) {
- return false;
- }
- // 修复部分仙剑的区服ID位数不对问题
- if($handleServcerId = repairRoleServerid($gameid, $serverid)){
- $serverid = $handleServcerId;
- }
- $memberGameServerInfo = model('MemberGameServer')->where(['member_id' => $userid, 'game_id' => $gameid, 'roleid' => $roleid])->find();
- if ($memberGameServerInfo) {
- $updRoleData = array();
- $updRoleData['serverid'] = $serverid;
- $updRoleData['servername'] = $servername;
- $updRoleData['roleid'] = $roleid;
- $updRoleData['rolename'] = $rolename;
- if ($rolelevel && $rolelevel <> '0') {
- $updRoleData['rolelevel'] = $rolelevel;
- }
- if ($viplevel && $viplevel <> '0') {
- $updRoleData['viplevel'] = $viplevel;
- }
- $updRoleData['last_ip'] = request()->ip();
- $updRoleData['last_imeil'] = $imeil;
- $updRoleData['update_time'] = NOW_TIMESTAMP;
- $updResult = model('MemberGameServer')->where(['member_id' => $userid, 'game_id' => $gameid, 'roleid' => $roleid])->update($updRoleData);
- } else {
- $cahce_key = md5($userid . $gameid . $serverid . $roleid);
- if(!Cache::get(':Role_add:MemberGameServer:'.$cahce_key)){
- Cache::set(':Role_add:MemberGameServer:'.$cahce_key,'1',3600);
- if ($memberGameInfo) {
- $addRoleData = array();
- $addRoleData['member_id'] = $memberGameInfo['member_id'];
- $addRoleData['channel_id'] = $memberGameInfo['channel_id'];
- $addRoleData['game_id'] = $memberGameInfo['game_id'];
- $addRoleData['serverid'] = $serverid;
- $addRoleData['ip'] = request()->ip();
- $addRoleData['imeil'] = $imeil;
- $addRoleData['create_time'] = NOW_TIMESTAMP;
- $addRoleData['servername'] = $servername;
- $addRoleData['roleid'] = $roleid;
- $addRoleData['rolename'] = $rolename;
- $addRoleData['rolelevel'] = $rolelevel;
- $addRoleData['viplevel'] = $viplevel;
- $addRoleData['last_ip'] = request()->ip();
- $addRoleData['last_imeil'] = $imeil;
- $addRoleData['update_time'] = NOW_TIMESTAMP;
- $insertId = model('MemberGameServer')->insert($addRoleData);
- }
- }
- }
- $gameServerInfo = model('GameServer')->where(['game_id' => $gameid, 'serverid' => $serverid])->find();
- if ($gameServerInfo) {
- $updServerData = array();
- $updServerData['servername'] = $servername;
- $updServerData['update_time'] = NOW_TIMESTAMP;
- $updServerResult = model('GameServer')->where(['game_id' => $gameid, 'serverid' => $serverid])->update($updServerData);
- } else {
- $cahce_key = md5($gameid.$serverid);
- if(!Cache::get(':Role_add:GameServer:'.$cahce_key)) {
- Cache::set(':Role_add:MemberGameServer:' . $cahce_key, '1', 3600);
- $addServerData = array();
- $addServerData['game_id'] = $gameid;
- $addServerData['serverid'] = $serverid;
- $addServerData['create_time'] = NOW_TIMESTAMP;
- $addServerData['servername'] = $servername;
- $addServerData['update_time'] = NOW_TIMESTAMP;
- $insertServerId = model('GameServer')->insert($addServerData);
- }
- }
- $clientIp = request()->ip();
- if ($gameid && $serverid && $clientIp) {
- $addServerIpData = array();
- $addServerIpData['channel_id'] = $memberGameInfo['channel_id'];
- $addServerIpData['member_id'] = $memberGameInfo['member_id'];
- $addServerIpData['game_id'] = $gameid;
- $addServerIpData['serverid'] = $serverid;
- $addServerIpData['create_src'] = $source;
- $addServerIpData['create_time'] = NOW_TIMESTAMP;
- $addServerIpData['servername'] = $servername;
- $addServerIpData['ip'] = $clientIp;
- $insertServerIpId = model('GameServerIp')->insert($addServerIpData);
- }
- if ($gameid && $serverid && $imeil) {
- $addServerImeilData = array();
- $addServerImeilData['channel_id'] = $memberGameInfo['channel_id'];
- $addServerImeilData['member_id'] = $memberGameInfo['member_id'];
- $addServerImeilData['game_id'] = $gameid;
- $addServerImeilData['serverid'] = $serverid;
- $addServerImeilData['create_src'] = $source;
- $addServerImeilData['create_time'] = NOW_TIMESTAMP;
- $addServerImeilData['servername'] = $servername;
- $addServerImeilData['imeil'] = $imeil;
- try {
- $insertServerImeilId = model('GameServerImeil')->insert($addServerImeilData);
- }catch (\Exception $e){
- }
- }
- return true;
- }
- /**
- * 获取游戏配置参数(有些游戏有多个参数时需要特殊处理)
- * @param $game_id 游戏id
- * @param $channel_mark 渠道标识
- * @param string $data 返回的数据
- * @param string $code 返回的code
- * @param string $msg 返回的msg
- * @return mixed
- */
- public function getGameParam($game_id, $channel_mark, $data = '', $code = '0', $msg = '该游戏的验签参数还未设置')
- {
- $polyChannelGameModel = new PolychannelGame;
- $param = $polyChannelGameModel->alias('a')
- ->join('nw_channel b', 'b.id = a.channel_id')
- ->where(['a.game_id' => $game_id, 'b.mark' => $channel_mark])
- ->value('a.param');
- if (empty($param)) {
- log_message('后台渠道游戏中,用于该游戏的验签参数还未设置,game_id=' . $game_id . ',渠道标识=' . $channel_mark, 'error', LOG_PATH . '../complexPaylog/');
- $this->jsonResult($data, $code, $msg);
- }
- $param = unserialize($param);
- return $param;
- }
- /**登录验证返回的数据
- * @param $code 1 登录验证成功 0验证失败
- * @param string $msg 登录验证提示语
- * @param array $data 需要重写的参数 key=》val key必须和complexLogin 里的key一致
- * @param string $exparam 扩展参数
- * @return array
- */
- public function reParam($code, $msg = '', $data = [], $exparam = '')
- {
- return ['code' => $code, 'msg' => $msg, 'extparam' => $exparam, 'data' => $data];
- }
- /**
- * 返回封装后的 API json数据到客户端
- * @access protected
- * @param mixed $data 要返回的数据
- * @param int $code 返回的 code
- * @param mixed $msg 提示信息
- * @param $isExit boolean 是否强制退出,默认true
- * @return void
- *
- */
- public function jsonResult($data, $code = 0, $msg = '', $isExit = true)
- {
- $err_code = $code;
- // if($this->device != 4){
- // if ($code >= 200) {
- // $code = 1;
- // } else {
- // $code = 0;
- // }
- // }
- if ($code > 0) {
- $code = 1;
- } else {
- $code = 0;
- }
- $result = [
- 'code' => $code,
- 'msg' => $msg,
- 'time' => request()->server('REQUEST_TIME'),
- 'data' => $data,
- 'err_code' => $err_code,
- ];
- log_message('Api@jsonResult.result:'. ' code:'. $code." - result:". json_encode($result), 'log', LOG_PATH . 'apilog/');
- // debug模式下返回明文
- if ($this->isApiDebug) {
- // echo json_encode($result);
- if($err_code < 0){
- $msg .= '(err:'.$err_code.')';
- }
- $this->result($data,$code,$msg,'json'); //api debug模式下fastcgi_finish_request无法使用
- } //正式环境返回密文
- else {
- log_message('Api@jsonResultUnescape.result:'. $this->opensslEncrypt(json_encode($result, JSON_UNESCAPED_SLASHES), Env::get($this->aesKey)), 'log', LOG_PATH . 'apilog/');
- echo $this->opensslEncrypt(json_encode($result), Env::get($this->aesKey));
- }
- //为了使用fastcgi_finish_request等函数时,后续的执行能够继续生效
- if ($isExit) exit;
- }
- public function jsonResultUnescape($data, $code = 0, $msg = '', $isExit = true)
- {
- // if($this->device != 4){
- // if ($code > 200) {
- // $code = 1;
- // } else {
- // $code = 0;
- // }
- // }
- if ($code > 0) {
- $code = 1;
- } else {
- $code = 0;
- }
- $result = [
- 'code' => $code,
- 'msg' => $msg,
- 'time' => request()->server('REQUEST_TIME'),
- 'data' => $data,
- ];
- log_message('Api@jsonResultUnescape.result:'. ' code:'. $code ." - result:". json_encode($result), 'log', LOG_PATH . 'apilog/');
- //debug模式下返回明文
- if ($this->isApiDebug) {
- echo json_encode($result, JSON_UNESCAPED_SLASHES);
- //$this->result($data,$code,$msg,'json'); //api debug模式下fastcgi_finish_request无法使用
- } //正式环境返回密文
- else {
- // log_message('Api@jsonResultUnescape.result:'. $this->opensslEncrypt(json_encode($result, JSON_UNESCAPED_SLASHES), Env::get($this->aesKey)), 'log', LOG_PATH . 'apilog/');
- echo $this->opensslEncrypt(json_encode($result, JSON_UNESCAPED_SLASHES), Env::get($this->aesKey));
- }
- //为了使用fastcgi_finish_request等函数时,后续的执行能够继续生效
- if ($isExit) exit;
- }
- /**
- * 返回封装后的 API json数据到客户端
- * @access protected
- * @param mixed $data 要返回的数据
- * @param int $code 返回的 code
- * @param mixed $msg 提示信息
- * @param $isExit boolean 是否强制退出,默认true
- * @return void
- *
- */
- public function jsonResultTrue($data, $code = 0, $msg = '',$isExit=true)
- {
- // if($this->device != 4){
- // if ($code > 0) {
- // $code = 1;
- // } else {
- // $code = 0;
- // }
- // }
- if ($code > 0) {
- $code = 1;
- } else {
- $code = 0;
- }
- $result = [
- 'code' => $code,
- 'msg' => $msg,
- 'time' => request()->server('REQUEST_TIME'),
- 'data' => $data,
- ];
- //debug模式下返回明文
- echo json_encode($result);
- //为了使用fastcgi_finish_request等函数时,后续的执行能够继续生效
- if ($isExit) exit;
- }
- // 空方法
- public function _empty()
- {
- return "url error!";
- }
- }
|