148 lines
3.5 KiB
PHP
148 lines
3.5 KiB
PHP
<?php
|
||
|
||
namespace app;
|
||
|
||
// 应用请求对象类
|
||
use app\http\HttpAuth;
|
||
use app\http\HttpClient;
|
||
use think\exception\ValidateException;
|
||
use think\helper\Macroable;
|
||
|
||
/**
|
||
* @method static HttpClient getClient() 获取客户端信息
|
||
*/
|
||
class Request extends \think\Request
|
||
{
|
||
use Macroable;
|
||
|
||
/**
|
||
* @var string
|
||
*/
|
||
public string $contextId = '';
|
||
/**
|
||
* @var HttpAuth
|
||
*/
|
||
private readonly HttpAuth $auth;
|
||
|
||
/**
|
||
* 设置Auth
|
||
* @param HttpAuth $auth
|
||
* @return Request
|
||
*/
|
||
public function setAuth(HttpAuth $auth): self
|
||
{
|
||
$this->auth = $auth;
|
||
return $this;
|
||
}
|
||
|
||
public function getAuth(): ?HttpAuth
|
||
{
|
||
// var_dump($this->auth);
|
||
return $this->auth ?? null;
|
||
}
|
||
|
||
|
||
public function getBrowser(): string
|
||
{
|
||
return get_agent_browser($this->server('HTTP_USER_AGENT'));
|
||
}
|
||
|
||
public function getDevice(): string
|
||
{
|
||
return get_agent_device($this->server('HTTP_USER_AGENT'));
|
||
}
|
||
|
||
public function getOs(): string
|
||
{
|
||
return get_agent_os($this->server('HTTP_USER_AGENT'));
|
||
}
|
||
|
||
const string DATA_SECRET_KEY = "mySecretKey123!";
|
||
/**
|
||
* 加密数据
|
||
*/
|
||
static function encryptCryptoJSAES($data): string
|
||
{
|
||
// 如果是数组或对象,转换为 JSON 字符串
|
||
if (is_array($data) || is_object($data)) {
|
||
$data = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||
}
|
||
|
||
// 生成随机 salt (8字节)
|
||
$salt = openssl_random_pseudo_bytes(8);
|
||
|
||
// 通过 EVP_BytesToKey 派生密钥和 IV
|
||
$keyIv = self::evpBytesToKey($salt);
|
||
$key = $keyIv['key'];
|
||
$iv = $keyIv['iv'];
|
||
|
||
// 添加 PKCS#7 填充
|
||
$blockSize = 16; // AES 块大小
|
||
$padding = $blockSize - (strlen($data) % $blockSize);
|
||
$data .= str_repeat(chr($padding), $padding);
|
||
|
||
// AES-256-CBC 加密
|
||
$encrypted = openssl_encrypt(
|
||
$data,
|
||
'aes-256-cbc',
|
||
$key,
|
||
OPENSSL_RAW_DATA,
|
||
$iv
|
||
);
|
||
|
||
// 组合 Salted__ + salt + 加密数据
|
||
$result = "Salted__" . $salt . $encrypted;
|
||
|
||
// Base64 编码
|
||
return base64_encode($result);
|
||
}
|
||
|
||
/**
|
||
* 解密 CryptoJS 加密的数据
|
||
*/
|
||
static function decryptCryptoJSData(string $encryptedData): string
|
||
{
|
||
// Base64解码
|
||
$data = base64_decode($encryptedData);
|
||
|
||
// 检查Salted__头
|
||
if (!str_starts_with($data, "Salted__")) {
|
||
throw new ValidateException('Invalid format: missing "Salted__" header');
|
||
}
|
||
|
||
// 提取Salt (8字节)
|
||
$salt = substr($data, 8, 8);
|
||
$ct = substr($data, 16);
|
||
|
||
// 通过EVP_BytesToKey派生密钥和IV
|
||
$keyIv = self::evpBytesToKey($salt);
|
||
$key = $keyIv['key'];
|
||
$iv = $keyIv['iv'];
|
||
|
||
// AES-256-CBC解密
|
||
return openssl_decrypt(
|
||
$ct,
|
||
'aes-256-cbc',
|
||
$key,
|
||
OPENSSL_RAW_DATA,
|
||
$iv
|
||
);
|
||
}
|
||
|
||
static private function evpBytesToKey($salt): array
|
||
{
|
||
$password = self::DATA_SECRET_KEY;
|
||
$bytes = '';
|
||
$last = '';
|
||
// 生成48字节:32字节key + 16字节IV
|
||
while(strlen($bytes) < 48) {
|
||
$last = md5($last . $password . $salt, true);
|
||
$bytes .= $last;
|
||
}
|
||
return [
|
||
'key' => substr($bytes, 0, 32),
|
||
'iv' => substr($bytes, 32, 16)
|
||
];
|
||
}
|
||
}
|