isBaned($req->uid), ErrCode::err_server_updating); # 检查封号 # 检查是否处于更新阶段,暂停对玩家请求的响应。 if (GAME_ONLINE # 外网 && self::isUpdating() # 更新 && !config::Inst()->isTester($req->uid)) { # 排除测试号 return ErrCode::err_server_updating; } $ssd = GameConfig::service_schedule_getItem(1); # 服务计划(固定只有1条) if (now() > $ssd->startts && now() < $ssd->endts) { # 在维护期间 // return ErrCode::err_server_maintaining; Err(ErrCode::err_server_maintaining, $ssd->reason); } // if (!GAME_ONLINE || config::Inst()->isTester($req->uid)) { # 内网不做token校验操作 return ErrCode::ok; // } if ($req->cmd == CmdCode::cmd_user_getzonelist) { # 如果是获取分区列表的消息初始化tk信息,不做校验 $siginfo = SigInfo::InitSig($req); # 初始化sigInfo $req->sig = $siginfo->sig; # 每次resp会从req上取sig值返回 $req->msgid = PreProc::createMsgId($siginfo); # 初始化msgid $siginfo->msgid = SigInfo::calcMsgid($siginfo, $req); # sigInfo和本次的$req身上的msgid,用于下次消息校验, self::updateUserSigInfo($req, $siginfo); # 写入Cmem,下次登录的时候校验 return ErrCode::ok; } # 其余消息,全部进行校验 $siginfo = self::getUserSigInfo($req); # 1.从Cmem读取证书验证信息 if ($siginfo == null) { return ErrCode::err_signo; } if ($req->sig != $siginfo->sig) { # 2.如果一次性证书不一致,则证明已经在其他地方登录 return ErrCode::err_anotherlogin; } if ($siginfo->msgid != SigInfo::calcMsgid($siginfo, $req)) { # 3.如果消息id错位,则证明该条消息非法 return ErrCode::err_illegal; } if (now() - $req->ts > OFFSET_MSGTIME) { # 4.如果服务端客户端时间戳超过规定误差,则消息非法 return ErrCode::err_outtime; } # 更新siginfo $siginfo->msgnum++; # 消息编号递增 $req->msgid = PreProc::createMsgId($siginfo); # ... $siginfo->msgid = SigInfo::calcMsgid($siginfo, $req); # 计算新的 self::updateUserSigInfo($req, $siginfo); # 回写siginfo return ErrCode::ok; # 返回操作结果,0:成功,其余为错误码 } public static function createMsgId($siginfo) { $temp = 8888 - $siginfo->msgnum; return substr(md5(substr($siginfo->uid, 2, 20) . $temp . substr($siginfo->sig, 5, 15)), 7, 23); } /** * 返回是否处于更新中 * @return boolean */ public static function isUpdating() { $ts = now(); if ($ts > glc()->updatingBeginTs && $ts < glc()->updatingEndTs) { return true; } return false; } /** * 把玩家的SigInfo写入数据库 * @param Req $req * @param SigInfo $siginfo */ public static function updateUserSigInfo($req, $siginfo) { gMem()->set(MemKey_User::Sig($req->zoneid, $siginfo->uid), $siginfo); } /** * 从Cmem中读取玩家的SigInfo数据 * @param Req $req * @return SigInfo */ public static function getUserSigInfo($req) { return gMem()->get(MemKey_User::Sig($req->zoneid, $req->uid)); } } /** * 校验信息 */ class SigInfo { public $uid; public $zoneid; public $sig; public $msgnum; public $msgid; /** * * @param SigInfo $siginfo * @param Req $req */ public static function calcMsgid($siginfo, $req) { return substr(md5($siginfo->sig . $req->msgid), 0, 32); } /** * 玩家登陆的时候初始化sig * @param Req $req */ public static function InitSig($req) { $paras = array(); $paras["openid"] = $req->uid; $paras["ep"] = HttpUtil::getClientEP(); $paras["ts"] = now(); $self_url_path = "atomsoft.com"; $siginfo = new SigInfo(); $siginfo->uid = $req->uid; $siginfo->zoneid = $req->zoneid; $siginfo->sig = self::makeSig('post', $self_url_path, $paras, PROJECTNAME); $siginfo->msgnum = 0; return $siginfo; } /** * 生成签名 * * @param string $method 请求方法 "get" or "post" * @param string $url_path * @param array $params 表单参数 * @param string $secret 密钥 */ public static function makeSig($method, $url_path, $params, $secret) { $mk = self::makeSource($method, $url_path, $params); $my_sign = hash_hmac("sha1", $mk, strtr($secret, '-_', '+/'), true); $my_sign = base64_encode($my_sign); return $my_sign; } private static function makeSource($method, $url_path, $params) { $strs = strtoupper($method) . '&' . rawurlencode($url_path) . '&'; ksort($params); $query_string = array(); foreach ($params as $key => $val) { array_push($query_string, $key . '=' . $val); } $query_string = join('&', $query_string); return $strs . str_replace('~', '%7E', rawurlencode($query_string)); } }