ctms/vendor/fmsoft/function/cert.func.php
2025-04-10 23:19:13 +08:00

339 lines
11 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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表示错误代码)
/*
*判断机制:
1get方式接收到username与accesstoken判断是否匹配
2username对应的证书文件是否存在
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;
}