diff --git a/app/BaseController.php b/app/BaseController.php index c7646bb..e8aa16e 100644 --- a/app/BaseController.php +++ b/app/BaseController.php @@ -136,10 +136,17 @@ abstract class BaseController 'list' => $data->items() ]; } - return json(array( + + $result = array( "code" => $code, "data" => $data, "message" => $message, - )); + ); + $resultEncrypted = [ + 'encryptedData' => \app\Request::encryptCryptoJSAES($result) + ]; + + return json($resultEncrypted); } + } diff --git a/app/Request.php b/app/Request.php index aeb8aed..c574dec 100644 --- a/app/Request.php +++ b/app/Request.php @@ -5,6 +5,7 @@ namespace app; // 应用请求对象类 use app\http\HttpAuth; use app\http\HttpClient; +use think\exception\ValidateException; use think\helper\Macroable; /** @@ -56,4 +57,91 @@ class Request extends \think\Request 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); + } + + // 生成随机 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) + ]; + } } diff --git a/app/http/middleware/ContextMiddleware.php b/app/http/middleware/ContextMiddleware.php index 725e4f2..1acbbd8 100644 --- a/app/http/middleware/ContextMiddleware.php +++ b/app/http/middleware/ContextMiddleware.php @@ -8,7 +8,7 @@ use think\{exception\ValidateException, file\UploadedFile, middleware, Request, class ContextMiddleware extends middleware { - const string DATA_SECRET_KEY = "mySecretKey123!"; + public function handle(Request $request, Closure $next): Response { @@ -24,7 +24,7 @@ class ContextMiddleware extends middleware $encryptedData = $request->param('encryptedData',''); if($encryptedData) { try{ - $jsonInput = $this->decryptCryptoJSData($encryptedData); + $jsonInput = \app\Request::decryptCryptoJSData($encryptedData); }catch (\Throwable){ $jsonInput = null; } @@ -162,55 +162,4 @@ class ContextMiddleware extends middleware } } - /** - * 解密 CryptoJS 加密的数据 - * - * @param string $encryptedData Base64 编码的加密数据 - * @return array 解密后的数据 - * @throws ValidateException 解密失败时抛出异常 - */ - private 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 = $this->evpBytesToKey($salt); - $key = $keyIv['key']; - $iv = $keyIv['iv']; - - // AES-256-CBC解密 - return openssl_decrypt( - $ct, - 'aes-256-cbc', - $key, - OPENSSL_RAW_DATA, - $iv - ); - } - - 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) - ]; - } } \ No newline at end of file diff --git a/z_ele/src/api/index.ts b/z_ele/src/api/index.ts index a5c9e2c..bb20f9b 100644 --- a/z_ele/src/api/index.ts +++ b/z_ele/src/api/index.ts @@ -9,7 +9,7 @@ export interface ApiResult { /** 返回数据 */ data?: T; /** 后台做了数据加密->前端需要做对称解密 */ - encrypted?: string; + encryptedData?: string; } /** diff --git a/z_ele/src/utils/request.ts b/z_ele/src/utils/request.ts index ec3bb5a..d2db9f7 100644 --- a/z_ele/src/utils/request.ts +++ b/z_ele/src/utils/request.ts @@ -111,8 +111,8 @@ const service = axios.create({ */ service.interceptors.response.use( (res: AxiosResponse>) => { - if (res.data?.encrypted) { - res.data = decryptData(res.data.encrypted); + if (res.data?.encryptedData) { + res.data = decryptData(res.data.encryptedData); } const errorMessage = responseInterceptor(res);