{"id":661,"date":"2025-10-21T15:49:38","date_gmt":"2025-10-21T07:49:38","guid":{"rendered":"https:\/\/fugary.com\/?p=661"},"modified":"2025-10-21T17:24:02","modified_gmt":"2025-10-21T09:24:02","slug":"%e5%89%8d%e7%ab%af%e5%92%8c%e5%90%8e%e7%ab%af%e5%8a%a0%e5%af%86%e8%af%b7%e6%b1%82%e6%96%b9%e6%a1%88","status":"publish","type":"post","link":"https:\/\/fugary.com\/?p=661","title":{"rendered":"\u524d\u7aef\u548c\u540e\u7aef\u52a0\u5bc6\u8bf7\u6c42\u65b9\u6848"},"content":{"rendered":"<p>\u6700\u8fd1\u516c\u53f8\u6709\u5ba2\u6237\u63d0\u51fa\u6765\u4fe1\u7528\u5361\u5728\u63d0\u4ea4\u5230\u540e\u53f0\u4fdd\u5b58\u65f6\u901a\u8fc7<code>F12<\/code>\u770b\u5230\u4e86\u660e\u6587\u7684\u8bf7\u6c42\u6570\u636e\uff0c\u5176\u5b9e\u7f51\u7ad9\u5df2\u7ecf\u6709<code>https<\/code>\u4fdd\u5b58\uff0c\u8bf7\u6c42\u4e2d\u7b2c\u4e09\u65b9\u662f\u6293\u4e0d\u5230\u8fd9\u4e9b\u6570\u636e\u7684\uff0c\u5ba2\u6237\u770b\u5230\u7684\u53ea\u662f\u81ea\u5df1\u6d4f\u89c8\u5668\u8bf7\u6c42\u8bb0\u5f55\uff0c\u81ea\u5df1\u586b\u5199\u7684\u6570\u636e\u4e5f\u4e0d\u5b58\u5728\u5b89\u5168\u95ee\u9898\uff0c\u4e0d\u8fc7\u65e2\u7136\u5ba2\u6237\u9700\u8981\uff0c\u6211\u4eec\u4e5f\u53ef\u4ee5\u5b9e\u73b0\u4e0b\u524d\u7aef\u548c\u540e\u7aef\u4ea4\u4e92\u8bf7\u6c42\u52a0\u5bc6\u3002<\/p>\n<p><strong>\u6ce8\u610f\uff1a\u5176\u5b9e<code>JS<\/code>\u57fa\u672c\u5f88\u96be\u5b9e\u73b0\u771f\u6b63\u7684\u4fdd\u5bc6\uff0c\u5f88\u591a\u79d8\u94a5\u5728\u524d\u7aef\u662f\u53ef\u4ee5\u83b7\u53d6\u5230\u7684\uff0c\u8fd9\u91cc\u5b9e\u73b0\u7684\u662f<code>JS<\/code>\u52a0\u5bc6\uff0c\u540e\u7aef\u89e3\u5bc6\uff0c\u4ee5\u53ca\u540e\u7aef\u8fd4\u56de\u6570\u636e\u52a0\u5bc6\uff0c\u524d\u7aef<code>JS<\/code>\u89e3\u5bc6\u3002<\/strong><\/p>\n<p>\u76f4\u63a5\u901a\u8fc7\u62e6\u622a\u5668\u5b9e\u73b0\uff0c\u53ef\u4ee5\u65b9\u4fbf\u5168\u90e8\u52a0\u5bc6\u6216\u8005\u90e8\u5206\u52a0\u5bc6\uff0c\u5bf9\u73b0\u6709\u4ee3\u7801\u57fa\u672c\u65e0\u4fb5\u5165\u3002<\/p>\n<h3>\u524d\u7aef\u903b\u8f91<\/h3>\n<h4>\u4f9d\u8d56\u9879<\/h4>\n<ol>\n<li>jsencrypt\uff1a<a href=\"https:\/\/github.com\/travist\/jsencrypt\">https:\/\/github.com\/travist\/jsencrypt<\/a><\/li>\n<li>crypto-js\uff1a<a href=\"https:\/\/github.com\/brix\/crypto-js\">https:\/\/github.com\/brix\/crypto-js<\/a><\/li>\n<\/ol>\n<p>\u5176\u5b9e\u5f88\u591a\u6d4f\u89c8\u5668\u5df2\u7ecf\u6709\u81ea\u5e26\u7684<code>crypto<\/code>\u5de5\u5177\uff0c\u4f46\u662f\u8fd8\u662f\u6709\u5c11\u91cf\u6d4f\u89c8\u5668\u4e0d\u517c\u5bb9\uff0c\u56e0\u6b64\u4f7f\u7528\u7b2c\u4e09\u65b9\u5e93\u3002<\/p>\n<h4>\u524d\u7aef\u6d41\u7a0b<\/h4>\n<p>\u53d1\u9001\u6570\u636e\u524d\u7aef\u52a0\u5bc6\uff0c\u540e\u7aef\u89e3\u5bc6\uff0c\u4f7f\u7528\u7c7b\u4f3c<code>https<\/code>\u7684\u6a21\u5f0f\uff0c<code>AES+RSA<\/code>\u52a0\u5bc6\u7b97\u6cd5\uff0c\u56e0\u4e3a\u975e\u5bf9\u79f0\u52a0\u5bc6\u6bd4\u8f83\u6162\uff0c\u800c\u4e14\u6709\u957f\u5ea6\u9650\u5236\uff0c\u56e0\u6b64\u9700\u8981\u5bf9\u79f0\u52a0\u5bc6\u548c\u975e\u5bf9\u79f0\u52a0\u5bc6\u7ed3\u5408\u3002<\/p>\n<p><div class='fancybox-wrapper' data-fancybox='post-images' href='https:\/\/git.mengqingpo.com:8888\/fugary\/blogpic\/uploads\/fe1e71ed8f805108e073ef9d0c189973\/1.png'><img src=\"https:\/\/git.mengqingpo.com:8888\/fugary\/blogpic\/uploads\/fe1e71ed8f805108e073ef9d0c189973\/1.png\" alt=\"1\" \/><\/div><\/p>\n<p>\u63a5\u6536\u6570\u636e\uff0c\u540e\u7aef\u52a0\u5bc6\uff0c\u524d\u7aef\u89e3\u5bc6<\/p>\n<p><div class='fancybox-wrapper' data-fancybox='post-images' href='https:\/\/git.mengqingpo.com:8888\/fugary\/blogpic\/uploads\/64ab9bfc71a7c2319dab63c221c17232\/2.png'><img src=\"https:\/\/git.mengqingpo.com:8888\/fugary\/blogpic\/uploads\/64ab9bfc71a7c2319dab63c221c17232\/2.png\" alt=\"2\" \/><\/div><\/p>\n<h4>\u524d\u7aef\u4ee3\u7801<\/h4>\n<p>\u5de5\u5177\u65b9\u6cd5<\/p>\n<pre><code class=\"language-js\">import { JSEncrypt } from &#039;jsencrypt&#039;\nimport CryptoJS from &#039;crypto-js&#039;\nimport { isObject, isPlainObject, isString } from &#039;lodash\/lang&#039;\n\nconst encryptBodyPublicKey = process.env.VUE_APP_ENCRYPT_BODY_PUBLIC_KEY\n\n\/**\n * js\u8bf7\u6c42\u52a0\u5bc6\u89e3\u5bc6\u670d\u52a1\n *\/\nconst crypt = new JSEncrypt()\ntry {\n  crypt.setPublicKey(encryptBodyPublicKey)\n  console.log(&#039;JSEncryptProvider initialized&#039;)\n} catch (error) {\n  console.error(&#039;JSEncryptProvider init failed:&#039;, error.message)\n  throw error\n}\n\/**\n * \u52a0\u5bc6\u6570\u636e\n * @param data\n * @returns {{aesKey: *, encryptedKey: string, encryptedBody: boolean, data: {encryptedData: null}}}\n *\/\nexport const encrypt = function (data) {\n  try {\n    const aesKey = CryptoJS.lib.WordArray.random(32) \/\/ \u751f\u6210\u968f\u673a AES-256 \u5bc6\u94a5\n    const aesKeyBase64 = aesKey.toString(CryptoJS.enc.Base64)\/\/ \u5c06 AES \u5bc6\u94a5\u8f6c\u4e3a Base64 \u7f16\u7801\n    \/\/ \u7528 RSA \u52a0\u5bc6 AES \u5bc6\u94a5 \uff08RSA\u6bd4\u8f83\u6162\uff0c\u4ec5\u52a0\u5bc6\u79d8\u94a5\uff09\n    const encryptedKey = crypt.encrypt(aesKeyBase64)\n    if (!encryptedKey) {\n      throw new Error(&#039;RSA encryption failed for AES key&#039;)\n    }\n    let encryptedData = null\n    if (data) {\n      const iv = CryptoJS.lib.WordArray.random(16) \/\/ \u968f\u673a\u5411\u91cf\n      \/\/ \u7528AES-256-CBC\u52a0\u5bc6\u6570\u636e\n      const encrypted = CryptoJS.AES.encrypt(data, aesKey, {\n        mode: CryptoJS.mode.CBC,\n        padding: CryptoJS.pad.Pkcs7, \/\/ PKCS#7 \u517c\u5bb9 Java PKCS5Padding\n        iv: iv\n      })\n      encryptedData = CryptoJS.enc.Base64.stringify(iv.concat(encrypted.ciphertext)) \/\/ \u5411\u91cf\u62fc\u63a5\u5230\u5185\u5bb9\n    }\n    return {\n      aesKey: aesKey,\n      encryptedKey: encryptedKey,\n      encryptedData: encryptedData\n    }\n  } catch (error) {\n    console.error(&#039;Encryption error:&#039;, error)\n  }\n}\n\n\/**\n * \u89e3\u6790\u540e\u7aef\u6570\u636e\n * @param data {{&quot;encryptedKey&quot;: string, &quot;encryptedData&quot;:string}}\n * @param aesKey {CryptoJS.lib.WordArray|string}\n * @returns {Object}\n *\/\nexport const decrypt = function (data, aesKey) {\n  const encryptedData = data.encryptedData\n  const combined = CryptoJS.enc.Base64.parse(encryptedData)\n  const iv = CryptoJS.lib.WordArray.create(\n    combined.words.slice(0, 4), \/\/ 16 \u5b57\u8282 = 4 words\n    16\n  )\n  const cipherText = CryptoJS.lib.WordArray.create(\n    combined.words.slice(4),\n    combined.sigBytes - 16\n  )\n  if (typeof aesKey === &#039;string&#039;) {\n    aesKey = CryptoJS.enc.Base64.parse(aesKey)\n  }\n  const decrypted = CryptoJS.AES.decrypt(\n    { ciphertext: cipherText },\n    aesKey,\n    { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }\n  )\n  return JSON.parse(decrypted.toString(CryptoJS.enc.Utf8))\n}\n\nconst isJsonContent = function (config) {\n  const data = config.data\n  return config.method?.toUpperCase() === &#039;POST&#039; &amp;&amp; (isPlainObject(data) || isString(data))\n}\n\nexport const processEncryptBody = function (config) {\n  if (encryptBodyPublicKey &amp;&amp; (config.encryptBody || config.encryptResponse)) {\n    let dataStr = &#039;&#039;\n    if (isJsonContent(config) &amp;&amp; config.encryptBody) {\n      dataStr = JSON.stringify(config.data)\n    }\n    const encryptedResult = encrypt(dataStr)\n    if (encryptedResult) {\n      config.headers[&#039;x-encrypted-key&#039;] = encryptedResult.encryptedKey \/\/ \u6807\u8bb0\u5df2\u7ecf\u52a0\u5bc6\n      if (config.encryptResponse) {\n        config.headers[&#039;x-encrypted-response&#039;] = config.encryptResponse \/\/ \u6807\u8bb0\u5df2\u7ecf\u52a0\u5bc6\n        config.aesKey = encryptedResult.aesKey \/\/ key\u6682\u5b58\n      }\n      if (encryptedResult.encryptedData) {\n        config.headers[&#039;x-encrypted-body&#039;] = config.encryptBody \/\/ \u6807\u8bb0\u5df2\u7ecf\u52a0\u5bc6\n        config.data = {\n          encryptedData: encryptedResult.encryptedData\n        }\n      }\n    }\n  }\n}\n\nexport const processDecryptBody = function (response) {\n  const config = response.config\n  const data = response.data\n  if (encryptBodyPublicKey &amp;&amp; config.headers[&#039;x-encrypted-response&#039;] &amp;&amp;\n    config.aesKey &amp;&amp; data &amp;&amp; data.encryptedData) {\n    const decryptedData = decrypt(data, config.aesKey)\n    if (decryptedData) {\n      response.data = decryptedData\n    }\n  }\n}<\/code><\/pre>\n<p><code>axios<\/code>\u62e6\u622a\u5668\u914d\u7f6e\uff1a<\/p>\n<pre><code class=\"language-js\">axios.interceptors.request.use(config =&gt; {\n  processEncryptBody(config)\n  \/\/ other code\n  return config\n})\naxios.interceptors.response.use(data =&gt; {\n  if (data.config.aesKey) {\n    processDecryptBody(data)\n  }\n  \/\/ other code\n  return data\n})<\/code><\/pre>\n<h3>\u540e\u7aef\u903b\u8f91<\/h3>\n<h4>\u540e\u7aef\u6d41\u7a0b<\/h4>\n<p><div class='fancybox-wrapper' data-fancybox='post-images' href='https:\/\/git.mengqingpo.com:8888\/fugary\/blogpic\/uploads\/c8a1521ab69f9277d4ec1afccf493e34\/3.png'><img src=\"https:\/\/git.mengqingpo.com:8888\/fugary\/blogpic\/uploads\/c8a1521ab69f9277d4ec1afccf493e34\/3.png\" alt=\"3\" \/><\/div><\/p>\n<p><div class='fancybox-wrapper' data-fancybox='post-images' href='https:\/\/git.mengqingpo.com:8888\/fugary\/blogpic\/uploads\/150d3c6adbd966c64b0eb88238e572d7\/4.png'><img src=\"https:\/\/git.mengqingpo.com:8888\/fugary\/blogpic\/uploads\/150d3c6adbd966c64b0eb88238e572d7\/4.png\" alt=\"4\" \/><\/div><\/p>\n<h4>\u540e\u7aef\u4ee3\u7801<\/h4>\n<p>\u540e\u7aef\u89e3\u5bc6\u5de5\u5177\uff1a<\/p>\n<pre><code class=\"language-java\">public class EncryptBodyUtils {\n    private EncryptBodyUtils() {\n    }\n\n    \/**\n     * \u52a0\u5bc6\u6570\u636e\u5b57\u6bb5\u540d\n     *\/\n    public static final String ENCRYPTED_DATA_KEY = &quot;encryptedData&quot;;\n    \/**\n     * \u8bf7\u6c42\u52a0\u5bc6\u5224\u65ad\uff0ctrue\u3001false\n     *\/\n    public static final String ENCRYPTED_BODY_HEADER = &quot;x-encrypted-body&quot;;\n    \/**\n     * \u54cd\u5e94\u52a0\u5bc6\u5224\u65ad\uff0ctrue\u3001false\n     *\/\n    public static final String ENCRYPTED_RESPONSE_HEADER = &quot;x-encrypted-response&quot;;\n    \/**\n     * \u968f\u673akey\u52a0\u5bc6\u540e\u5934\n     *\/\n    public static final String ENCRYPTED_KEY_HEADER = &quot;x-encrypted-key&quot;;\n    \/**\n     * RSA\u52a0\u5bc6\u7b97\u6cd5\n     *\/\n    public static final String RSA_CIPHER_ALGORITHM = &quot;RSA\/ECB\/PKCS1Padding&quot;;\n    \/**\n     * AES\u52a0\u5bc6\u7b97\u6cd5\n     *\/\n    public static final String AES_CIPHER_ALGORITHM = &quot;AES\/CBC\/PKCS5Padding&quot;;\n    \/**\n     * \u524d\u7aef\u52a0\u5bc6\u516c\u94a5\n     *\/\n    public static final String JS_ENCRYPT_BODY_PUBLIC_KEY = &quot;ENCRYPT_BODY_PUBLIC_KEY&quot;;\n    \/**\n     * \u52a0\u5bc6\u516c\u94a5\n     *\/\n    public static final String ENCRYPT_BODY_PUBLIC_KEY;\n    \/**\n     * \u52a0\u5bc6\u79c1\u94a5\n     *\/\n    public static final String ENCRYPT_BODY_PRIVATE_KEY;\n\n    static {\n        ENCRYPT_BODY_PUBLIC_KEY = SystemConfigUtils.getString(&quot;system.encrypted.body.public.key&quot;);\n        ENCRYPT_BODY_PRIVATE_KEY = SystemConfigUtils.getString(&quot;system.encrypted.body.private.key&quot;);\n    }\n\n    \/**\n     * json\u6620\u5c04\u5de5\u5177\n     *\n     * @return\n     *\/\n    public static ObjectMapper getDefaultMapper() {\n        ObjectMapper objectMapper = new ObjectMapper();\n        JsonUtils.initMapper(objectMapper, true);\n        return objectMapper;\n    }\n\n    \/**\n     * \u751f\u6210 16 \u5b57\u8282 IV\n     *\n     * @return\n     *\/\n    public static byte[] generateIv() {\n        byte[] iv = new byte[16]; \/\/ AES-CBC\/GCM \u6807\u51c6 IV \u957f\u5ea6 = 16 \u5b57\u8282\n        new SecureRandom().nextBytes(iv);\n        return iv;\n    }\n\n    \/**\n     * \u89e3\u6790\u79c1\u94a5\n     *\n     * @param privateKeyPEM\n     * @return\n     * @throws Exception\n     *\/\n    public static PrivateKey parsePrivateKey(String privateKeyPEM) throws NoSuchAlgorithmException, InvalidKeySpecException {\n        privateKeyPEM = privateKeyPEM.replace(&quot;-----BEGIN PRIVATE KEY-----&quot;, &quot;&quot;)\n                .replace(&quot;-----END PRIVATE KEY-----&quot;, &quot;&quot;)\n                .replaceAll(&quot;\\\\s&quot;, &quot;&quot;);\n        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyPEM);\n        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);\n        KeyFactory keyFactory = KeyFactory.getInstance(&quot;RSA&quot;);\n        return keyFactory.generatePrivate(keySpec);\n    }\n\n    \/**\n     * \u89e3\u5bc6AES\u7684\u5bc6\u7801\n     *\n     * @param encryptedAesKey\n     * @return\n     * @throws Exception\n     *\/\n    public static byte[] decryptAesKey(String encryptedAesKey, PrivateKey privateKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {\n        Cipher rsaCipher = Cipher.getInstance(RSA_CIPHER_ALGORITHM);\n        rsaCipher.init(Cipher.DECRYPT_MODE, privateKey);\n        byte[] aesKeyBytes = rsaCipher.doFinal(Base64.getDecoder().decode(encryptedAesKey));\n        return Base64.getDecoder().decode(aesKeyBytes);\n    }\n\n    \/**\n     * \u52a0\u5bc6\u6570\u636e\u89e3\u6790\uff0c\u9ed8\u8ba4\u60c5\u51b5\u4e0b\u524d16\u5b57\u8282\u4e3aIV\uff0c\u540e\u9762\u672a\u52a0\u5bc6\u6570\u636e\n     *\n     * @param encryptedData\n     * @return\n     * @throws Exception\n     *\/\n    public static Pair&lt;byte[], byte[]&gt; calcEncryptedData(String encryptedData) {\n        byte[] fullData = Base64.getDecoder().decode(encryptedData);\n        byte[] iv = new byte[16];\n        System.arraycopy(fullData, 0, iv, 0, 16); \/\/ \u524d 16 \u5b57\u8282\u4e3a IV\n        byte[] encryptedBytes = new byte[fullData.length - 16];\n        System.arraycopy(fullData, 16, encryptedBytes, 0, fullData.length - 16);\n        return Pair.of(iv, encryptedBytes);\n    }\n\n    \/**\n     * AES\/CBC\/PKCS5Padding \u52a0\u5bc6\n     *\n     * @param data   \u5f85\u52a0\u5bc6\u6570\u636e\n     * @param aesKey AES\u5bc6\u94a5\uff0816\/24\/32\u5b57\u8282\uff09\n     * @param iv     \u504f\u79fb\u91cf\uff0816\u5b57\u8282\uff09\n     * @return \u52a0\u5bc6\u540e\u7684\u6570\u636e\n     *\/\n    public static byte[] encryptAesData(byte[] data, byte[] aesKey, byte[] iv) throws NoSuchPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException {\n        SecretKeySpec secretKey = new SecretKeySpec(aesKey, &quot;AES&quot;);\n        IvParameterSpec ivSpec = new IvParameterSpec(iv);\n        Cipher cipher = Cipher.getInstance(AES_CIPHER_ALGORITHM);\n        cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);\n        byte[] cipherText = cipher.doFinal(data);\n        byte[] result = new byte[iv.length + cipherText.length];\n        System.arraycopy(iv, 0, result, 0, iv.length);\n        System.arraycopy(cipherText, 0, result, iv.length, cipherText.length);\n        return result;\n    }\n\n    \/**\n     * \u89e3\u5bc6\u6570\u636e\n     *\n     * @param encryptDataPair\n     * @param aesKey\n     * @return\n     * @throws Exception\n     *\/\n    public static byte[] decryptAesData(Pair&lt;byte[], byte[]&gt; encryptDataPair, byte[] aesKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {\n        Cipher aesCipher = Cipher.getInstance(AES_CIPHER_ALGORITHM);\n        aesCipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(aesKey, &quot;AES&quot;),\n                new IvParameterSpec(encryptDataPair.getLeft()));\n        return aesCipher.doFinal(encryptDataPair.getRight());\n    }\n\n    \/**\n     * \u52a0\u5bc6key\n     *\n     * @return\n     *\/\n    public static String getEncryptedAesKey() {\n        HttpServletRequest request = HttpRequestUtils.getCurrentRequest();\n        if (request != null) {\n            return request.getHeader(ENCRYPTED_KEY_HEADER);\n        }\n        return null;\n    }\n\n    \/**\n     * \u662f\u5426\u652f\u6301\n     *\n     * @return\n     *\/\n    public static boolean isSupported() {\n        return isSupported(ENCRYPTED_BODY_HEADER);\n    }\n\n    \/**\n     * \u662f\u5426\u652f\u6301\n     *\n     * @return\n     *\/\n    public static boolean isSupported(String headerKey) {\n        HttpServletRequest request = HttpRequestUtils.getCurrentRequest();\n        return request != null &amp;&amp; &quot;true&quot;.equals(request.getHeader(headerKey));\n    }\n\n    \/**\n     * \u662f\u5426\u652f\u6301\u54cd\u5e94\n     *\n     * @return\n     *\/\n    public static boolean isSupportedResponse() {\n        return isSupported(ENCRYPTED_RESPONSE_HEADER) &amp;&amp; getEncryptedAesKey() != null;\n    }\n\n    \/**\n     * \u6dfb\u52a0\u52a0\u5bc6\u76f8\u5173\u53c2\u6570\n     *\n     * @param ccMap\n     *\/\n    public static void addEncryptConfigParam(Map&lt;String, Object&gt; ccMap) {\n        ccMap.put(JS_ENCRYPT_BODY_PUBLIC_KEY, ENCRYPT_BODY_PUBLIC_KEY);\n    }\n}<\/code><\/pre>\n<p>\u540e\u7aef\u8bf7\u6c42\u62e6\u622a\u4ee3\u7801\uff1a<\/p>\n<pre><code class=\"language-java\">@ControllerAdvice\npublic class EncryptedRequestBodyAdvice extends RequestBodyAdviceAdapter {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(EncryptedRequestBodyAdvice.class);\n    private ObjectMapper objectMapper;\n    private PrivateKey privateKey;\n\n    public EncryptedRequestBodyAdvice() throws NoSuchAlgorithmException, InvalidKeySpecException {\n        this.objectMapper = EncryptBodyUtils.getDefaultMapper();\n        this.privateKey = EncryptBodyUtils.parsePrivateKey(EncryptBodyUtils.ENCRYPT_BODY_PRIVATE_KEY);\n    }\n\n    @Override\n    public boolean supports(MethodParameter methodParameter, Type targetType, Class&lt;? extends HttpMessageConverter&lt;?&gt;&gt; converterType) {\n        return EncryptBodyUtils.isSupported();\n    }\n\n    @Override\n    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class&lt;? extends HttpMessageConverter&lt;?&gt;&gt; converterType) throws IOException {\n        if (EncryptBodyUtils.isSupported()) { \/\/ \u524d\u53f0\u5df2\u52a0\u5bc6\n            byte[] body = inputMessage.getBody().readAllBytes();\n            \/\/ \u524d\u7aef\u53d1\u9001\u683c\u5f0f\u5b9a\u4e49 { &quot;encryptedKey&quot;: &quot;\u52a0\u5bc6\u79d8\u94a5&quot;, &quot;encryptedData&quot;: &quot;base64\u5bc6\u6587&quot; }\n            try {\n                String encryptedAesKey = EncryptBodyUtils.getEncryptedAesKey();\n                if (StringUtils.isBlank(encryptedAesKey)) {\n                    throw new IllegalArgumentException(&quot;Missing header x-encrypted-key&quot;);\n                }\n                JsonNode jsonNode = objectMapper.readTree(body);\n                if (!jsonNode.has(EncryptBodyUtils.ENCRYPTED_DATA_KEY) &amp;&amp; jsonNode.get(EncryptBodyUtils.ENCRYPTED_DATA_KEY) != null) {\n                    throw new IllegalArgumentException(&quot;Missing encryptedData&quot;);\n                }\n                \/\/ \u89e3\u5bc6 AES \u5bc6\u94a5\n                byte[] aesKey = EncryptBodyUtils.decryptAesKey(encryptedAesKey, privateKey);\n                \/\/ \u89e3\u5bc6 AES \u6570\u636e\n                String encryptedData = jsonNode.get(EncryptBodyUtils.ENCRYPTED_DATA_KEY).asText();\n                body = EncryptBodyUtils.decryptAesData(EncryptBodyUtils.calcEncryptedData(encryptedData), aesKey);\n            } catch (Exception e) {\n                LOGGER.error(&quot;Decryption failed&quot;, e);\n            }\n            final byte[] bodyBytes = body;\n            return new HttpInputMessage() {\n                @Override\n                public InputStream getBody() {\n                    return new ByteArrayInputStream(bodyBytes);\n                }\n\n                @Override\n                public HttpHeaders getHeaders() {\n                    return inputMessage.getHeaders();\n                }\n            };\n        }\n        return inputMessage; \/\/ \u6ca1\u6709\u52a0\u5bc6\u5934\uff0c\u76f4\u63a5\u8fd4\u56de\u539f\u59cb\u8f93\u5165\u6d41\n    }\n}<\/code><\/pre>\n<p>\u540e\u7aef\u54cd\u5e94\u62e6\u622a\u4ee3\u7801\uff1a<\/p>\n<pre><code class=\"language-java\">@ControllerAdvice\npublic class EncryptedResponseBodyAdvice implements ResponseBodyAdvice&lt;Object&gt; {\n    private static final Logger LOGGER = LoggerFactory.getLogger(EncryptedResponseBodyAdvice.class);\n    private ObjectMapper objectMapper;\n    private PrivateKey privateKey;\n\n    public EncryptedResponseBodyAdvice() throws NoSuchAlgorithmException, InvalidKeySpecException {\n        this.objectMapper = EncryptBodyUtils.getDefaultMapper();\n        this.privateKey = EncryptBodyUtils.parsePrivateKey(EncryptBodyUtils.ENCRYPT_BODY_PRIVATE_KEY);\n    }\n\n    @Override\n    public boolean supports(MethodParameter returnType, Class&lt;? extends HttpMessageConverter&lt;?&gt;&gt; converterType) {\n        return EncryptBodyUtils.isSupportedResponse();\n    }\n\n    @Override\n    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class&lt;? extends HttpMessageConverter&lt;?&gt;&gt; selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {\n        try {\n            byte[] bodyBytes = objectMapper.writeValueAsBytes(body);\n            String encryptedAesKey = EncryptBodyUtils.getEncryptedAesKey();\n            \/\/ \u89e3\u5bc6 AES \u5bc6\u94a5\n            byte[] aesKey = EncryptBodyUtils.decryptAesKey(encryptedAesKey, privateKey);\n            byte[] iv = EncryptBodyUtils.generateIv();\n            String encryptedData = Base64.getEncoder().encodeToString(EncryptBodyUtils.encryptAesData(bodyBytes, aesKey, iv));\n            if (StringUtils.isNotBlank(encryptedData)) {\n                response.getHeaders().add(EncryptBodyUtils.ENCRYPTED_RESPONSE_HEADER, &quot;true&quot;);\n            }\n            return Map.of(EncryptBodyUtils.ENCRYPTED_DATA_KEY, encryptedData);\n        } catch (Exception e) {\n            LOGGER.error(&quot;\u6267\u884c\u52a0\u5bc6\u5931\u8d25&quot;, e);\n        }\n        return body;\n    }\n}<\/code><\/pre>\n<h3>\u8bf7\u6c42\u622a\u56fe<\/h3>\n<p>\u8bf7\u6c42\u793a\u4f8b\uff1a<\/p>\n<p><div class='fancybox-wrapper' data-fancybox='post-images' href='https:\/\/git.mengqingpo.com:8888\/fugary\/blogpic\/uploads\/9cc330c30683bbb36be059ce5ec4dddf\/image-20251021172102049.png'><img src=\"https:\/\/git.mengqingpo.com:8888\/fugary\/blogpic\/uploads\/9cc330c30683bbb36be059ce5ec4dddf\/image-20251021172102049.png\" alt=\"image-20251021172102049\" \/><\/div><\/p>\n<p>\u54cd\u5e94\u793a\u4f8b\uff1a<\/p>\n<p><div class='fancybox-wrapper' data-fancybox='post-images' href='https:\/\/git.mengqingpo.com:8888\/fugary\/blogpic\/uploads\/11ed716aafc5ec4de32b9604f3b53ec9\/image-20251021172121644.png'><img src=\"https:\/\/git.mengqingpo.com:8888\/fugary\/blogpic\/uploads\/11ed716aafc5ec4de32b9604f3b53ec9\/image-20251021172121644.png\" alt=\"image-20251021172121644\" \/><\/div><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u6700\u8fd1\u516c\u53f8\u6709\u5ba2\u6237\u63d0\u51fa\u6765\u4fe1\u7528\u5361\u5728\u63d0\u4ea4\u5230\u540e\u53f0\u4fdd\u5b58\u65f6\u901a\u8fc7F12\u770b\u5230\u4e86\u660e\u6587\u7684\u8bf7\u6c42\u6570\u636e\uff0c\u5176\u5b9e\u7f51\u7ad9\u5df2\u7ecf\u6709https\u4fdd\u5b58\uff0c\u8bf7\u6c42 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[4,14,2],"tags":[],"_links":{"self":[{"href":"https:\/\/fugary.com\/index.php?rest_route=\/wp\/v2\/posts\/661"}],"collection":[{"href":"https:\/\/fugary.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fugary.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fugary.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fugary.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=661"}],"version-history":[{"count":6,"href":"https:\/\/fugary.com\/index.php?rest_route=\/wp\/v2\/posts\/661\/revisions"}],"predecessor-version":[{"id":667,"href":"https:\/\/fugary.com\/index.php?rest_route=\/wp\/v2\/posts\/661\/revisions\/667"}],"wp:attachment":[{"href":"https:\/\/fugary.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=661"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fugary.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=661"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fugary.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=661"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}