339 lines
11 KiB
PHP
Executable File
339 lines
11 KiB
PHP
Executable File
<?php
|
||
/**
|
||
* @author Fm453
|
||
* @site www.hiluker.com
|
||
* @remark:接口通信证书处理
|
||
*/
|
||
defined('HI_FM') or exit("Access Denied");
|
||
|
||
fm_load()->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 = "<?php";
|
||
$content .= "\r\n";
|
||
$content .= "/*";
|
||
$content .= "\r\n";
|
||
|
||
$content .= "* 通讯证书文件";
|
||
$content .= "\r\n";
|
||
|
||
$content .= "@status 站点状态,-1禁止 0只读 1可用";
|
||
$content .= "\r\n";
|
||
$content .= "@siteId 站点ID";
|
||
$content .= "\r\n";
|
||
$content .= "@suIp 允许访问的IP";
|
||
$content .= "\r\n";
|
||
$content .= "@suDomain 允许访问的域名";
|
||
$content .= "\r\n";
|
||
$content .= "@api 接口用户名(站点用户名)";
|
||
$content .= "\r\n";
|
||
$content .= "@code 用户授权码";
|
||
$content .= "\r\n";
|
||
$content .= "@token 接口通讯的accesstoken文件";
|
||
$content .= "\r\n";
|
||
|
||
$content .= "*/";
|
||
$content .= "\r\n";
|
||
$content .= "\$cert = array();";
|
||
$content .= "\r\n";
|
||
foreach($cert as $k=>$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;
|
||
} |