fm_func('fm'); fm_load()->fm_func('file'); //判断某用户证书是否存在 /* @username 用户名 */ function fmFunc_cert_exist($username) { global $_GPC; global $_HI; global $_FM; $file = $_HI['apiCertPath'].$username.'.php'; if(file_exists($file)){ $cert = require($file); return $cert; } return false; } //登陆前,根据来路判断访问是否合规 /* @username 用户名 @ip 来路IP @domain 来路域名 *考虑到CDN加速导致IP变化的原因,域名或IP有一个符合规则即可 */ function fmFunc_cert_beforeLogin($cert,$ip,$domain) { global $_GPC; global $_HI; global $_FM; if(!is_array($cert) && !empty($cert)){ //未引入证书时,不判断 return true; }elseif(empty($ip) && empty($domain)){ //ip与domain均未传入时,不判断 return true; } $ips = isset($cert['suIp']) ? $cert['suIp'] : false; $domains = isset($cert['suDomain']) ? $cert['suDomain'] : false; $username = isset($cert['api']) ? $cert['api'] : false; if(!$ips || !$domains || !$username){ //没有有效的证书配置时,不判断 return true; } //为fm453本地开发放行,为公共密钥publicapi放行 if($username == 'vcms.localhost' || $username == 'publicapi') { return true; } //判断ip与domain的合法性 if(!in_array($ip,$ips) && !in_array($domain,$domains)){ return false; } return true; } //登陆后,更新证书 /* *有则更新,无则创建 */ function fmFunc_cert_afterLogin(){ global $_GPC; global $_HI; global $_FM; if(!isset($_FM['user']['name'])){ return false; } $_FM['viaDomain'] = isset($_FM['viaDomain']) ? $_FM['viaDomain'] : fmFunc_server_via_domain(); $_FM['viaIp'] = isset($_FM['viaIp']) ? $_FM['viaIp'] : fmFunc_server_via_ip(); $connection = yii::$app->db; //客户侧是否请求刷新token;否,则直接取token值后返回(应对同一账号同一站点多模块使用的情况)(同一账号,同一站点对应同一token;同一账号可拥有多个token) if(!isset($_GET['refreshToken']) || empty($_GET['refreshToken'])){ $sql = "SELECT `accesstoken` FROM c_access WHERE username = :username AND ip = :ip AND domain = :domain ORDER BY id ASC"; $params = array(); $params[':username'] = $_FM['user']['name']; $params[':ip'] = $_FM['viaIp']; $params[':domain'] = $_FM['viaDomain']; //$AccessToken = fmFunc_pdo_yii_fetchcolumn($connection,$sql, $params); $AccessToken = fmFunc_pdo_yii_fetchcolumn($connection,$table='c_access', $column='accesstoken', $conditions = 'username = :username AND ip = :ip AND domain = :domain ORDER BY id ASC', $params); $_FM['token'] = $AccessToken; //cert证书文件 //如果没有,创建之;如果有,更新其中的token $username = $_FM['user']['name']; $cert = fmFunc_cert_exist($username); if(!$cert){ $cert = array(); $cert['id'] = $_FM['user']['uid']; $cert['siteId'] = $_FM['timestamp']; $cert['suIp'] = array($_FM['viaIp']); $cert['siteName'] = ''; $cert['suDomain'] = array($_FM['viaDomain']); $cert['siteUrl'] = ''; $cert['api'] = $username; $cert['code'] = ''; $cert['token'] = $AccessToken; $cert['status'] = '0'; $cert['remark'] = '新创建待审核,创建时间:'.date('Y-m-d H:i:s',$_FM['timestamp']); } $_FM['apiCert'] = $cert; fmFunc_cert_write($username,$cert); if($AccessToken) { return; } } //生成一个新AccssToken /* *生成方法: @md5: 用户名username+编码useruid+当前时间戳timestamp+失效时间expiretime+随机混淆码randchars 拼接 */ $randchars = randchars(5); $content = array(); $content[] = $_FM['user']['name']; $content[] = $_FM['user']['uid']; $content[] = $_FM['timestamp']; $content[] = $expiretime = 3600 * 24 * 30; $content[] = $randchars; $_content = iserializer($content); $AccessToken = md5($_content); $_FM['token'] = $AccessToken; //在c_access表中记录登陆对应的有效token $initdata = array(); $initdata['accesstoken'] = $AccessToken; $initdata['updatetime'] = $_FM['timestamp']; $initdata['expiretime'] = $_FM['timestamp']+$expiretime; $initdata['hashchars'] = $randchars; $sql = "SELECT `id` FROM c_access WHERE username = :username AND ip = :ip AND domain = :domain ORDER BY id ASC"; $params = array(); $params[':username'] = $_FM['user']['name']; $params[':ip'] = $_FM['viaIp']; $params[':domain'] = $_FM['viaDomain']; $hasId = fmFunc_pdo_yii_fetch($connection,$sql, $params); if($hasId) { $condition = 'username = :username AND ip = :ip AND domain = :domain'; fmFunc_pdo_yii_update($connection, 'c_access', $initdata, $condition, $params); }else{ $initdata['username'] = $_FM['user']['name']; $initdata['uid'] = $_FM['user']['uid']; $initdata['userType'] = 2; //客户系统侧的用户登陆,统一归为user类型(接口使用者) $initdata['ip'] = $_FM['viaIp']; $initdata['domain'] = $_FM['viaDomain']; $initdata['data'] = $_content; $initdata['create_time'] = date('y-m-d H:i:s',$_FM['timestamp']); fmFunc_pdo_yii_insert($connection, 'c_access', $initdata); } //cert证书文件进一步校验 //如果没有,创建之;如果有,更新其中的token $username = $_FM['user']['name']; $cert = fmFunc_cert_exist($username); if(!$cert){ $cert = array(); $cert['id'] = $_FM['user']['uid']; $cert['siteId'] = $_FM['timestamp']; $cert['suIp'] = array($_FM['viaIp']); $cert['siteName'] = ''; $cert['suDomain'] = array($_FM['viaDomain']); $cert['siteUrl'] = ''; $cert['api'] = $username; $cert['code'] = ''; $cert['token'] = $AccessToken; $cert['status'] = '0'; $cert['remark'] = '新创建待审核,创建时间:'.date('Y-m-d H:i:s',$_FM['timestamp']); }elseif(is_array($cert)){ $cert['token'] = $AccessToken; $cert['updatetime'] = '更新时间:'.date('Y-m-d H:i:s',$_FM['timestamp']); } fmFunc_cert_write($username,$cert); } //为指定用户写证书 /* @username 用户名 @cert 证书内容 */ function fmFunc_cert_write($username,$cert) { global $_GPC; global $_HI; global $_FM; if(!$username){ return false; } $file = $_HI['apiCertPath'].$username.'.php'; $content = "$v){ if(!is_array($v)) { $content .= "\$cert['".$k."'] = '".(string)$v."';"; }else{ $content .= "\$cert['".$k."'] = array("; $len = count($v); for($i = $len;$i>0;$i--){ if($i>1) { $content .= "'".(string)$v[$i-1]."',"; }elseif($i==1) { $content .= "'".(string)$v[$i-1]."'"; } } $content .= ");"; } $content .= "\r\n"; } $content .= "return \$cert;"; $content .= "\r\n"; fmFunc_file_write($file,$content); } /*__________________________________________________*/ //根据请求数据返回通讯授权结果(0为正常,>0表示错误代码) /* *判断机制: (1)get方式接收到username与accesstoken,判断是否匹配 (2)username对应的证书文件是否存在 (3)根据证书检测是否合规的访问 */ function fmFunc_cert_checkAuth(){ global $_GPC; global $_HI; global $_FM; //检查appid if(!isset($_FM['get']['appid'])){ return 45300205; //未指定有效的appid }elseif(empty($_FM['get']['appid'])){ return 45300205; //未指定有效的appid } //检查accesstoken if(!isset($_FM['get']['accesstoken'])){ return 45300204; //未指定有效的accesstoken }elseif(empty($_FM['get']['accesstoken'])){ return 45300204; //未指定有效的accesstoken } //检查appid与accesstoken是否匹配 $username = $_FM['get']['appid']; $accesstoken = $_FM['get']['accesstoken']; $connection = yii::$app->db; $sql = "SELECT `id` FROM c_access WHERE username = :username AND accesstoken = :accesstoken "; $params = array(); $params[':username'] = $username; $params[':accesstoken'] = $accesstoken; $hasId = fmFunc_pdo_yii_fetch($connection,$sql, $params); if(!$hasId){ return 45300206; //不匹配 } //设置用户为登陆状态 $_FM['user']['name'] = $username; // 使用指定用户名获取用户身份实例 $User = new common\models\User; $identity = $User->findOne(['username' => $_FM['user']['name']]); $_FM['user']['uid'] = $identity->id; // 登录用户,有效期30d $duration = 3600 * 24 * 30; Yii::$app->user->login($identity, $duration); //查询服务器证书 $cert = fmFunc_cert_exist($username); if(!$cert){ return 45300104; //未查询到服务器证书 } //检查证书有效性 $ips = isset($cert['suIp']) ? $cert['suIp'] : false; $domains = isset($cert['suDomain']) ? $cert['suDomain'] : false; $api = (isset($cert['api']) && !empty($cert['api'])) ? $cert['api'] : false; if((!$ips && !$domains) || !$api){ return 45300105; //无效的证书 } //来路域名与IP $_FM['viaDomain'] = isset($_FM['viaDomain']) ? $_FM['viaDomain'] : fmFunc_server_via_domain(); $_FM['viaIp'] = isset($_FM['viaIp']) ? $_FM['viaIp'] : fmFunc_server_via_ip(); //为fm453本地开发放行,为公共密钥publicapi放行,其他来路均需判断合法性 if($username != 'vcms.localhost' && $username != 'publicapi') { //判断ip与domain的合法性 if(!in_array($_FM['viaIp'],$ips) && !in_array($_FM['viaDomain'],$domains)){ return 45300101; //来源IP或域名不在白名单中 } } $_FM['apiCert'] = $cert; return 0; }