Merge branch 'main' of github.com:netcccyun/dnsmgr

This commit is contained in:
net909 2025-05-13 10:44:45 +08:00
commit 8e7adead48
7 changed files with 654 additions and 27 deletions

View File

@ -392,7 +392,7 @@ function clearDirectory($dir): bool
return true;
}
function curl_client($url, $data = null, $referer = null, $cookie = null, $headers = null, $proxy = false, $method = null, $timeout = 5)
function curl_client($url, $data = null, $referer = null, $cookie = null, $headers = null, $proxy = false, $method = null, $timeout = 5, $default_headers = true)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
@ -400,11 +400,15 @@ function curl_client($url, $data = null, $referer = null, $cookie = null, $heade
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$httpheader[] = "Accept: */*";
$httpheader[] = "Accept-Language: zh-CN,zh;q=0.8";
$httpheader[] = "Connection: close";
if ($headers) {
$httpheader = array_merge($httpheader, $headers);
if ($default_headers === true) {
$httpheader[] = "Accept: */*";
$httpheader[] = "Accept-Language: zh-CN,zh;q=0.8";
$httpheader[] = "Connection: close";
if ($headers) {
$httpheader = array_merge($headers, $httpheader);
}
} else {
$httpheader = $headers;
}
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader);
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.95 Safari/537.36");

View File

@ -1210,6 +1210,74 @@ class DeployHelper
],
],
],
'wangsu' => [
'name' => '网宿科技',
'class' => 2,
'icon' => 'wangsu.ico',
'note' => '适用产品网页加速、下载分发、全站加速、点播分发、直播分发、上传加速、移动加速、上网加速、S-P2P、PCDN、应用性能管理、WEB应用防火墙、BotGuard爬虫管理、WSS、DMS、DDoS云清洗、应用加速、应用安全加速解决方案、IPv6一体化解决方案、电商安全加速解决方案、金融安全加速解决方案、政企安全加速解决方案、DDoS云清洗(非网站业务)、区块链安全加速解决方案、IPv6安全加速解决方案、CDN Pro。暂不支持AKSK鉴权。',
'inputs' => [
'username' => [
'name' => '账号',
'type' => 'input',
'placeholder' => '',
'required' => true,
],
'apiKey' => [
'name' => 'APIKEY',
'type' => 'input',
'placeholder' => '自行联系提供商申请',
'required' => true,
],
'spKey' => [
'name' => '特殊KEY',
'type' => 'input',
'placeholder' => '特殊场景下才需要使用的APIKEY留空默认同APIKEY',
],
'proxy' => [
'name' => '使用代理服务器',
'type' => 'radio',
'options' => [
'0' => '否',
'1' => '是',
],
'value' => '0'
],
],
'taskinputs' => [
'product' => [
'name' => '要部署的产品',
'type' => 'select',
'options' => [
['value'=>'cdn', 'label'=>'CDN'],
['value'=>'cdnpro', 'label'=>'CDN Pro'],
['value'=>'certificate', 'label'=>'证书管理']
],
'value' => 'cdn',
'required' => true,
],
'domains' => [
'name' => '绑定的域名',
'type' => 'input',
'show' => 'product==\'cdn\'',
'placeholder' => '多个域名可使用,分隔',
'required' => true,
],
'domain' => [
'name' => '绑定的域名',
'type' => 'input',
'show' => 'product==\'cdnpro\'',
'placeholder' => '不支持输入多个域名',
'required' => true,
],
'cert_id' => [
'name' => '证书ID',
'type' => 'input',
'show' => 'product==\'certificate\'',
'placeholder' => '',
'required' => true,
],
],
],
'baishan' => [
'name' => '白山云',
'class' => 2,
@ -1371,7 +1439,7 @@ class DeployHelper
'name' => 'AWS',
'class' => 2,
'icon' => 'aws.ico',
'note' => '支持部署到Amazon CloudFront',
'note' => '支持部署到Amazon CloudFront、AWS Certificate Manager',
'inputs' => [
'AccessKeyId' => [
'name' => 'AccessKeyId',
@ -1401,14 +1469,24 @@ class DeployHelper
'type' => 'select',
'options' => [
['value'=>'cloudfront', 'label'=>'CloudFront'],
['value'=>'acm', 'label'=>'AWS Certificate Manager'],
],
'value' => 'cloudfront',
'value' => 'acm',
'required' => true,
],
'distribution_id' => [
'name' => '分配ID',
'type' => 'input',
'placeholder' => 'distributions id',
'show' => 'product==\'cloudfront\'',
'required' => true,
],
'acm_arn' => [
'name' => 'ACM ARN',
'type' => 'input',
'placeholder' => '',
'show' => 'product==\'acm\'',
'note' => '在AWS Certificate Manager控制台查看证书的ARN',
'required' => true,
],
],

View File

@ -145,6 +145,7 @@ class AWS
$path = '/' . $this->version . $path;
$body = '';
$query = [];
if ($method == 'GET' || $method == 'DELETE') {
$query = $params;
} else {
@ -327,16 +328,31 @@ class AWS
return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true);
}
private function array2xml($array, $xml = null)
private function array2xml($array, $xml = null, $parentTagName = 'root')
{
if ($xml === null) {
$xml = new \SimpleXMLElement('<root/>');
}
foreach ($array as $key => $value) {
// 确定当前标签名:如果是数字键名,使用父级标签名,否则使用当前键名
$tagName = is_numeric($key) ? $parentTagName : $key;
if (is_array($value)) {
$subNode = $xml->addChild($key);
$this->array2xml($value, $subNode);
// 检查数组的第一个子节点的键是否为0
$firstKey = array_key_first($value);
$isFirstKeyZero = ($firstKey === 0 || $firstKey === '0');
if ($isFirstKeyZero) {
// 如果第一个子节点的键是0则不生成当前节点标签直接递归子节点
$this->array2xml($value, $xml, $tagName);
} else {
// 否则生成当前节点标签,并递归子节点
$subNode = $xml->addChild($tagName);
$this->array2xml($value, $subNode, $tagName);
}
} else {
$xml->addChild($key, $value);
}

View File

@ -236,7 +236,7 @@ class aliyun implements DeployInterface
if (empty($config['oss_endpoint'])) throw new Exception('OSS Endpoint不能为空');
if (empty($config['oss_bucket'])) throw new Exception('OSS Bucket不能为空');
$client = new AliyunOSS($this->AccessKeyId, $this->AccessKeySecret, $config['oss_endpoint']);
$client->addBucketCnameCert($config['oss_bucket'], $config['domain'], $cert_id);
$client->addBucketCnameCert($config['oss_bucket'], $config['domain'], $cert_id . '-cn-hangzhou');
$this->log('OSS域名 ' . $config['domain'] . ' 部署证书成功!');
}

View File

@ -29,22 +29,24 @@ class aws implements DeployInterface
}
public function deploy($fullchain, $privatekey, $config, &$info)
{
if ($config['product'] == 'acm') {
if (empty($config['acm_arn'])) throw new Exception('ACM ARN不能为空');
$this->get_cert_id($fullchain, $privatekey, $config['acm_arn'], true);
} else {
$this->deploy_cloudfront($fullchain, $privatekey, $config, $info);
}
}
private function deploy_cloudfront($fullchain, $privatekey, $config, &$info)
{
if (empty($config['distribution_id'])) throw new Exception('分配ID不能为空');
$certInfo = openssl_x509_parse($fullchain, true);
if (!$certInfo) throw new Exception('证书解析失败');
$config['cert_name'] = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
if (isset($info['cert_id']) && isset($info['cert_name']) && $info['cert_name'] == $config['cert_name']) {
$cert_id = $info['cert_id'];
$this->log('证书已上传:' . $cert_id);
} else {
$cert_id = $this->get_cert_id($fullchain, $privatekey);
$this->log('证书上传成功:' . $cert_id);
$info['cert_id'] = $cert_id;
$info['cert_name'] = $config['cert_name'];
usleep(500000);
}
$cert_id = isset($info['cert_id']) ? $info['cert_id'] : null;
$cert_id = $this->get_cert_id($fullchain, $privatekey, $cert_id);
usleep(500000);
$client = new AWSClient($this->AccessKeyId, $this->SecretAccessKey, 'cloudfront.amazonaws.com', 'cloudfront', '2020-05-31', 'us-east-1', $this->proxy);
try {
@ -54,20 +56,71 @@ class aws implements DeployInterface
}
$data['ViewerCertificate']['ACMCertificateArn'] = $cert_id;
$data['ViewerCertificate']['CloudFrontDefaultCertificate'] = false;
$xml = new \SimpleXMLElement('<?xml version="1.0" encoding="utf-8"?><DistributionConfig></DistributionConfig>');
$data['ViewerCertificate']['CloudFrontDefaultCertificate'] = 'false';
unset($data['ViewerCertificate']['Certificate']);
unset($data['ViewerCertificate']['CertificateSource']);
$xml = new \SimpleXMLElement('<DistributionConfig xmlns="http://cloudfront.amazonaws.com/doc/2020-05-31/"></DistributionConfig>');
$client->requestXmlN('PUT', '/distribution/' . $config['distribution_id'] . '/config', $data, $xml);
$this->log('分配ID: ' . $config['distribution_id'] . ' 证书部署成功!');
}
private function get_cert_id($fullchain, $privatekey)
private function get_cert_id($fullchain, $privatekey, $cert_id = null, $acm = false)
{
$cert = explode('-----END CERTIFICATE-----', $fullchain)[0] . '-----END CERTIFICATE-----';
if ($acm === true && $cert_id == null) {
throw new Exception('ACM ARN不能为空');
}
$certificates = explode('-----END CERTIFICATE-----', $fullchain);
$cert = $certificates[0] . '-----END CERTIFICATE-----';
$client = new AWSClient($this->AccessKeyId, $this->SecretAccessKey, 'acm.us-east-1.amazonaws.com', 'acm', '', 'us-east-1', $this->proxy);
if (!empty($cert_id)) {
try {
$data = $client->request('POST', 'CertificateManager.GetCertificate', [
'CertificateArn' => $cert_id
]);
// 如果成功获取证书信息说明证书存在直接返回cert_id
if (isset($data['Certificate']) && trim($data['Certificate']) == trim($cert)) {
$this->log('证书已是最新ACM ARN' . $cert_id);
return $cert_id;
} else {
$this->log('证书已过期或被删除,准备更新或者重新上传');
}
} catch (Exception $e) {
if ($acm === true) {
throw new Exception('获取证书信息失败请检查ACM ARN是否正确' . $e->getMessage());
}
$this->log('证书已被删除:' . $cert_id. ',准备重新上传');
}
}
$certificateChain = '';
if (count($certificates) > 1) {
// 从第二个证书开始,重新拼接中间证书链
for ($i = 1; $i < count($certificates); $i++) {
if (trim($certificates[$i]) !== '') { // 忽略空字符串(可能由末尾分割产生)
$certificateChain .= $certificates[$i] . '-----END CERTIFICATE-----';
}
}
}
$param = [
'Certificate' => base64_encode($cert),
'PrivateKey' => base64_encode($privatekey),
];
// 如果有中间证书链,则添加到参数中
if (!empty($certificateChain)) {
$param['CertificateChain'] = base64_encode($certificateChain);
}
// 如果是ACM则添加ARN参数用于更新证书
if ($acm === true) {
$param['CertificateArn'] = $cert_id;
}
$client = new AWSClient($this->AccessKeyId, $this->SecretAccessKey, 'acm.us-east-1.amazonaws.com', 'acm', '', 'us-east-1', $this->proxy);
try {
$data = $client->request('POST', 'CertificateManager.ImportCertificate', $param);
@ -75,6 +128,11 @@ class aws implements DeployInterface
} catch (Exception $e) {
throw new Exception('上传证书失败:' . $e->getMessage());
}
$this->log('证书上传成功:' . $cert_id);
$info['cert_id'] = $cert_id;
return $cert_id;
}

471
app/lib/deploy/wangsu.php Normal file
View File

@ -0,0 +1,471 @@
<?php
namespace app\lib\deploy;
use app\lib\DeployInterface;
use Exception;
class wangsu implements DeployInterface
{
private $logger;
private $username;
private $apiKey;
private $spKey;
private $proxy;
public function __construct($config)
{
$this->username = $config['username'];
$this->apiKey = $config['apiKey'];
$this->spKey = $config['spKey'];
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
}
public function check()
{
if (empty($this->username) || empty($this->apiKey)) throw new Exception('必填参数不能为空');
$this->request('/cdn/certificates');
return true;
}
public function deploy($fullchain, $privatekey, $config, &$info)
{
if ($config['product'] == 'cdnpro') {
$this->deploy_cdnpro($fullchain, $privatekey, $config, $info);
} elseif ($config['product'] == 'cdn') {
$this->deploy_cdn($fullchain, $privatekey, $config, $info);
} elseif ($config['product'] == 'certificate') {
$certInfo = openssl_x509_parse($fullchain, true);
if (!$certInfo) {
throw new Exception('证书解析失败');
}
$cert_name = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
$serial_no = strtolower($certInfo['serialNumberHex']);
$this->get_cert_id($fullchain, $privatekey, $cert_name, $config['cert_id'], $serial_no, true);
} else {
throw new Exception('未知的产品类型');
}
}
public function deploy_cdn($fullchain, $privatekey, $config, &$info)
{
if (empty($config['domains'])) {
throw new Exception('绑定的域名不能为空');
}
$domains = explode(',', $config['domains']);
$certInfo = openssl_x509_parse($fullchain, true);
if (!$certInfo) {
throw new Exception('证书解析失败');
}
$cert_name = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
$serial_no = strtolower($certInfo['serialNumberHex']);
$this->log('证书序列号:' . $serial_no);
$cert_id = isset($info['cert_id']) ? $info['cert_id'] : null;
$cert_id = $this->get_cert_id($fullchain, $privatekey, $cert_name, $cert_id, $serial_no, false);
$param = [
'certificateId' => $cert_id,
'domainNames' => $domains
];
try {
$data = $this->request('/api/config/certificate/batch', $param, true, null, 'PUT');
} catch (Exception $e) {
throw new Exception('绑定域名失败:' . $e->getMessage());
}
$this->log('绑定证书成功证书ID' . $cert_id);
$info['cert_id'] = $cert_id;
}
public function deploy_cdnpro($fullchain, $privatekey, $config, &$info)
{
if (empty($config['domain'])) {
throw new Exception('绑定的域名不能为空');
}
$domain = $config['domain'];
$certInfo = openssl_x509_parse($fullchain, true);
if (!$certInfo) {
throw new Exception('证书解析失败');
}
$cert_name = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
$cert_id = $this->get_cert_id_cdnpro($fullchain, $privatekey, $cert_name);
try {
$hostnameInfo = $this->request('/cdn/hostnames/' . $domain);
} catch (Exception $e) {
throw new Exception('获取域名信息失败:' . $e->getMessage());
}
if (empty($hostnameInfo["propertyInProduction"])) {
throw new Exception('域名 ' . $domain . ' 不存在或未部署到生产环境');
} else {
$this->log('CDN域名 ' . $domain . ' 对应的加速项目ID' . $hostnameInfo["propertyInProduction"]["propertyId"]);
$this->log('CDN域名 ' . $domain . ' 对应的加速项目生产版本:' . $hostnameInfo["propertyInProduction"]["version"]);
}
if ($hostnameInfo["propertyInProduction"]["certificateId"] == $cert_id) {
$this->log('CDN域名 ' . $domain . ' 已绑定证书:' . $cert_name);
return;
}
try {
$properity = $this->request('/cdn/properties/' . $hostnameInfo["propertyInProduction"]["propertyId"] . '/versions/' . $hostnameInfo["propertyInProduction"]["version"]);
} catch (Exception $e) {
throw new Exception('获取加速项目版本信息失败:' . $e->getMessage());
}
$properityConfig = $properity["configs"];
$properityConfig["tlsCertificateId"] = $cert_id;
try {
$data = $this->request('/cdn/properties/' . $hostnameInfo["propertyInProduction"]["propertyId"] . '/versions', $properityConfig, true);
} catch (Exception $e) {
throw new Exception('新增加速项目版本失败:' . $e->getMessage());
}
$url_parts = parse_url($data);
$path_parts = explode('/', $url_parts['path']);
$newVersion = end($path_parts);
$param = [
'propertyId' => $hostnameInfo["propertyInProduction"]["propertyId"],
'version' => intval($newVersion),
];
try {
$data = $this->request('/cdn/validations', $param, true);
} catch (Exception $e) {
throw new Exception('发起加速项目验证失败:' . $e->getMessage());
}
$url_parts = parse_url($data);
$path_parts = explode('/', $url_parts['path']);
$validationTaskId = end($path_parts);
$this->log('验证任务ID' . $validationTaskId);
$attempts = 0;
$maxAttempts = 12;
$status = null;
do {
sleep(5);
try {
$data = $this->request('/cdn/validations/' . $validationTaskId);
} catch (Exception $e) {
throw new Exception('获取验证任务状态失败:' . $e->getMessage());
}
$status = $data['status'];
if ($status === 'failed') {
throw new Exception('证书绑定失败,加速项目验证失败');
}
if ($status === 'succeeded') {
break; // 验证成功立即退出循环
}
$attempts++;
} while ($attempts < $maxAttempts);
if ($status !== 'succeeded') {
throw new Exception('证书绑定超时,加速项目验证时间过长');
}
$this->log('加速项目验证成功,开始部署...');
$deploymentTasks = [
'target' => 'production',
'actions' => [
[
'action' => 'deploy_cert',
'certificateId' => $cert_id,
'version' => 1,
],
[
'action' => 'deploy_property',
'propertyId' => $hostnameInfo["propertyInProduction"]["propertyId"],
'version' => intval($newVersion),
]
],
'name' => 'Deploy certificate and property for ' . $hostnameInfo["propertyInProduction"]["propertyId"],
];
try {
$data = $this->request('/cdn/deploymentTasks', $deploymentTasks, true, null, 'POST', false, ['Check-Certificate' => 'no', 'Check-Usage' => 'no']);
} catch (Exception $e) {
throw new Exception('下发证书部署任务失败:' . $e->getMessage());
}
$url_parts = parse_url($data);
$path_parts = explode('/', $url_parts['path']);
$deploymentTaskId = end($path_parts);
$this->log('CDN域名 ' . $domain . ' 绑定证书部署任务下发成功部署任务ID' . $deploymentTaskId);
$info['cert_id'] = $cert_id;
}
private function get_cert_id($fullchain, $privatekey, $cert_name, $cert_id = null, $serial_no = null, $overwrite = false)
{
if ($cert_id) {
try {
$data = $this->request('/api/certificate/' . $cert_id);
} catch (Exception $e) {
throw new Exception('获取证书详情失败:' . $e->getMessage());
}
if (isset($data['message']) && $data['message'] == 'success' && $data['data']['name'] == $cert_name && $data['data']['serial'] == $serial_no) {
$this->log('证书已是最新证书ID' . $cert_id);
return $cert_id;
}
$this->log('证书已过期或被删除,准备重新上传');
} elseif ($overwrite === true) {
throw new Exception('证书ID不能为空');
}
if ($overwrite === true) {
$param = [
'name' => $cert_name,
'certificate' => $fullchain,
'privateKey' => $privatekey,
];
try {
$data = $this->request('/api/certificate/' . $cert_id, $param, true, null, 'PUT');
$this->log('更新证书成功证书ID' . $cert_id);
return $cert_id;
} catch (Exception $e) {
throw new Exception('更新证书失败:' . $e->getMessage());
}
}
try {
$data = $this->request('/api/ssl/certificate');
} catch (Exception $e) {
throw new Exception('获取证书列表失败:' . $e->getMessage());
}
$certificates = $data['ssl-certificate'];
if (!empty($certificates)) {
foreach ($certificates as $cert) {
if ($serial_no == $cert['certificate-serial']) {
$cert_id = $cert['certificate-id'];
$this->log('证书' . $cert_name . '已存在新证书ID' . $cert_id);
try {
$this->request('/api/certificate/' . $cert_id, ['name' => $cert_name], true, null, 'PUT');
} catch (Exception $e) {
throw new Exception('证书更名失败:' . $e->getMessage());
}
$this->log('将证书ID为' . $cert_id . '的证书更名为:' . $cert_name);
return $cert_id;
} elseif ($cert_name == $cert['name']) {
$this->log('证书' . $cert_name . '已存在,但序列号(' . $cert['certificate-id'] . ')不匹配,准备重新上传');
try {
$this->request('/api/certificate/' . $cert['certificate-id'], [['name'] => $cert_name . '-bak'], true, null, 'PUT');
} catch (Exception $e) {
throw new Exception('证书更名失败:' . $e->getMessage());
}
$this->log('将证书ID为' . $cert['certificate-id'] . '的证书更名为:' . $cert_name . '-bak');
}
}
}
$param = [
'name' => $cert_name,
'certificate' => $fullchain,
'privateKey' => $privatekey,
];
try {
$data = $this->request('/api/certificate', $param, true, null, 'POST', true);
} catch (Exception $e) {
throw new Exception('上传证书失败:' . $e->getMessage());
}
$url_parts = parse_url($data);
$path_parts = explode('/', $url_parts['path']);
$cert_id = end($path_parts);
$this->log('上传证书成功证书ID' . $cert_id);
return $cert_id;
}
private function get_cert_id_cdnpro($fullchain, $privatekey, $cert_name)
{
$cert_id = null;
try {
$data = $this->request('/cdn/certificates?search=' . urlencode($cert_name));
} catch (Exception $e) {
throw new Exception('获取证书列表失败:' . $e->getMessage());
}
if ($data['count'] > 0) {
foreach ($data['certificates'] as $cert) {
if ($cert_name == $cert['name']) {
$cert_id = $cert['certificateId'];
$this->log('证书' . $cert_name . '已存在证书ID' . $cert_id);
return $cert_id;
}
}
}
$date = gmdate("D, d M Y H:i:s T");
$encryptedKey = $this->encryptPrivateKey($privatekey, $date);
$param = [
'name' => $cert_name,
'autoRenew' => 'Off',
'newVersion' => [
'privateKey' => $encryptedKey,
'certificate' => $fullchain,
]
];
try {
$data = $this->request('/cdn/certificates', $param, true, $date);
} catch (Exception $e) {
throw new Exception('上传证书失败:' . $e->getMessage());
}
$url_parts = parse_url($data);
$path_parts = explode('/', $url_parts['path']);
$cert_id = end($path_parts);
$this->log('上传证书成功证书ID' . $cert_id);
usleep(500000);
return $cert_id;
}
private function encryptPrivateKey($privateKey, $date = null)
{
// 获取当前 GMT 时间DATE
if (empty($date)) {
$date = gmdate("D, d M Y H:i:s T");
}
// 生成 HMAC-SHA256 密钥材料
if (!empty($this->spKey)) {
$apiKey = $this->spKey;
} else {
$apiKey = $this->apiKey;
}
$hmac = hash_hmac('sha256', $date, $apiKey, true);
$aesIvKeyHex = bin2hex($hmac);
if (strlen($aesIvKeyHex) != 64) {
throw new Exception("Invalid HMAC length: " . strlen($aesIvKeyHex));
}
// 提取 IV 和 Key
$ivHex = substr($aesIvKeyHex, 0, 32);
$keyHex = substr($aesIvKeyHex, 32, 64);
$iv = hex2bin($ivHex);
$key = hex2bin($keyHex);
$blockSize = 16; // AES 块大小为 16 字节
$plainLen = strlen($privateKey);
$padLen = $blockSize - ($plainLen % $blockSize);
$padding = str_repeat(chr($padLen), $padLen);
$plainText = $privateKey . $padding;
// AES-128-CBC 加密
$encrypted = openssl_encrypt(
$plainText,
'AES-128-CBC',
$key,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING,
$iv
);
if ($encrypted === false) {
throw new Exception("Encryption failed: " . openssl_error_string());
}
// 返回 Base64 编码结果
return base64_encode($encrypted);
}
private function request($path, $data = null, $json = false, $date = null, $method = null, $getLocation = false, $headers = [])
{
$body = null;
if ($data) {
$body = $json ? json_encode($data) : http_build_query($data);
}
if (empty($date)) {
$date = gmdate("D, d M Y H:i:s T");
}
$hmac = hash_hmac('sha1', $date, $this->apiKey, true);
$signature = base64_encode($hmac);
$authorization = 'Basic ' . base64_encode($this->username . ':' . $signature);
if (empty($headers)) {
$headers = [
'Authorization: ' . $authorization,
'Date: ' . $date,
'Accept: application/json',
'Connection: close',
];
} else {
$headers[] = 'Authorization: ' . $authorization;
$headers[] = 'Date: ' . $date;
$headers[] = 'Accept: application/json';
$headers[] = 'Connection: close';
}
if ($body && $json) {
$headers[] = 'Content-Type: application/json';
}
$url = 'https://open.chinanetcenter.com' . $path;
$response = curl_client($url, $body, null, null, $headers, $this->proxy, $method, 30, false);
$result = json_decode($response['body'], true);
if ((isset($response['code']) && $response['code'] == 201) || (isset($response['code']) && $response['code'] == 200 && $getLocation === true)) {
if (preg_match('/Location:\s*(.*)/i', $response['header'], $matches)) {
$location = trim($matches[1]); // 提取 Location 头部的值并去除多余空格
if (!empty($location)) {
return $location;
}
}
// 如果没有找到 Location 头部,返回默认值 true
return true;
} elseif (isset($response['code']) && $response['code'] >= 200 && $response['code'] <= 299) {
return isset($result) ? $result : true;
} elseif (isset($result['message'])) {
throw new Exception($result['message']);
} else {
throw new Exception('请求失败');
}
}
public function setLogger($func)
{
$this->logger = $func;
}
private function log($txt)
{
if ($this->logger) {
call_user_func($this->logger, $txt);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB