{"id":1667,"date":"2026-05-16T11:25:28","date_gmt":"2026-05-16T03:25:28","guid":{"rendered":"https:\/\/blog.ttwow.top\/?p=1667"},"modified":"2026-06-19T22:01:44","modified_gmt":"2026-06-19T14:01:44","slug":"%e5%be%ae%e4%bf%a1%e6%9c%8d%e5%8a%a1%e5%8f%b7%e6%a8%a1%e6%9d%bf%e6%b6%88%e6%81%af%e5%8f%91%e9%80%81%e5%92%8c%e8%87%aa%e5%8a%a8%e5%9b%9e%e5%a4%8d","status":"publish","type":"post","link":"https:\/\/blog.ttwow.top\/?p=1667","title":{"rendered":"\u5fae\u4fe1\u670d\u52a1\u53f7\u6a21\u677f\u6d88\u606f\u53d1\u9001\u548c\u81ea\u52a8\u56de\u590d"},"content":{"rendered":"<h2>\u5fae\u4fe1\u670d\u52a1\u53f7\u5e38\u89c1\u4e09\u5927\u914d\u7f6e\u533a\u522b<\/h2>\n<pre><code>        \u914d\u7f6e\u9879             \u7528\u9014                  \u4f5c\u7528\u5bf9\u8c61        \u662f\u5426\u5fc5\u987b\u516c\u7f51          \u662f\u5426\u9650\u523680\/443\n    API IP\u767d\u540d\u5355       \u540e\u7aef\u8c03\u7528\u5fae\u4fe1API       \u670d\u52a1\u5668IP               \u662f               \u5426\n    JS\u63a5\u53e3\u5b89\u5168\u57df\u540d        \u5fae\u4fe1\u7f51\u9875JS-SDK          \u524d\u7aef\u7f51\u9875\u57df\u540d      \u662f               \u57df\u540d\u5373\u53ef\n    \u6d88\u606f\u63a8\u9001URL         \u5fae\u4fe1\u4e8b\u4ef6\/\u6d88\u606f\u56de\u8c03       \u540e\u7aef\u63a5\u53e3\u5730\u5740          \u662f               \u5fc5\u987b80\/443\n<\/code><\/pre>\n<h2>\u5f00\u53d1\u65f6\u5019\u53d1\u9001\u6a21\u677f\u6d88\u606f\u4fee\u6539 \u5fc5\u987b\u8bbe\u7f6e ip\u767d\u540d\u5355<\/h2>\n<p>\u6ce8\u610f\u5f00\u542f\u6d88\u606f\u63a8\u9001\u540e\uff0c\u81ea\u5b9a\u4e49\u83dc\u5355\u4f1a\u88ab\u5173\u95ed\u3002\u9700\u8981\u81ea\u5df1\u751f\u6210\u4e00\u4e0b<\/p>\n<pre><code class=\"language-java\">package cn.hgsl.pp.hgslwxservice;\n\nimport cn.hgsl.pp.hgslwxservice.configuration.WechatParamConfig;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.context.SpringBootTest;\n\nimport javax.annotation.Resource;\nimport java.io.OutputStream;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\nimport java.util.Scanner;\n\n@SpringBootTest\npublic class WechatTestMenu {\n\n    @Resource\n    WechatParamConfig wechatParamConfig;\n\n    \/**\n     * 1. \u83b7\u53d6 Access Token\n     *\/\n    public String getAccessToken() throws Exception {\n        String urlStr = &quot;https:\/\/api.weixin.qq.com\/cgi-bin\/token?grant_type=client_credential&amp;appid=&quot; + wechatParamConfig.getAppId() + &quot;&amp;secret=&quot; + wechatParamConfig.getAppSecret();\n        URL url = new URL(urlStr);\n        HttpURLConnection conn = (HttpURLConnection) url.openConnection();\n        conn.setRequestMethod(&quot;GET&quot;);\n\n        Scanner scanner = new Scanner(conn.getInputStream(), &quot;UTF-8&quot;);\n        String response = scanner.useDelimiter(&quot;\\\\A&quot;).next();\n        scanner.close();\n\n        \/\/ \u89e3\u6790 JSON \u83b7\u53d6 access_token\n        ObjectMapper mapper = new ObjectMapper();\n        return mapper.readTree(response).get(&quot;access_token&quot;).asText();\n    }\n\n    @Test\n    public void testToken() throws Exception {\n        System.out.println(&quot;\u5f53\u524daccessToken:&quot; +  getAccessToken());\n    }\n\n    @Test\n    public void createMenu() {\n        try {\n            System.out.println(&quot;\u6b63\u5728\u83b7\u53d6 Access Token...&quot;);\n            String accessToken = getAccessToken();\n            System.out.println(&quot;\u83b7\u53d6\u6210\u529f, Token: &quot; + accessToken);\n            org.springframework.core.io.ClassPathResource resource =\n                    new org.springframework.core.io.ClassPathResource(&quot;wechat-menu.json&quot;);\n            String menuJson = org.springframework.util.StreamUtils.copyToString(\n                    resource.getInputStream(),\n                    java.nio.charset.StandardCharsets.UTF_8\n            );\n            String urlStr = &quot;https:\/\/api.weixin.qq.com\/cgi-bin\/menu\/create?access_token=&quot; + accessToken;\n            URL url = new URL(urlStr);\n            HttpURLConnection conn = (HttpURLConnection) url.openConnection();\n            conn.setRequestMethod(&quot;POST&quot;);\n            conn.setDoOutput(true);\n            conn.setRequestProperty(&quot;Content-Type&quot;, &quot;application\/json; charset=utf-8&quot;);\n\n            try (OutputStream os = conn.getOutputStream()) {\n                byte[] input = menuJson.getBytes(&quot;utf-8&quot;);\n                os.write(input, 0, input.length);\n            }\n\n            Scanner scanner = new Scanner(conn.getInputStream(), &quot;UTF-8&quot;);\n            String response = scanner.useDelimiter(&quot;\\\\A&quot;).next();\n            scanner.close();\n            System.out.println(&quot;\u5fae\u4fe1\u521b\u5efa\u83dc\u5355\u54cd\u5e94\u7ed3\u679c: &quot; + response);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    \/**\n     * 3. \u6839\u636e\u6587\u7ae0\u6807\u9898\u67e5\u8be2\u5df2\u53d1\u5e03\u6587\u7ae0\u7684 media_id\n     *\/\n    @Test\n    public void getMediaIdByTitle() {\n        String targetTitle = &quot;\u6df1\u84dd\u6570\u5b57\u79d1\u6280\u6709\u9650\u516c\u53f8&quot;;\n\/\/        String targetTitle = &quot;\u6210\u529f\u6848\u4f8b&quot;;\n        try {\n            String accessToken = getAccessToken();\n            String urlStr = &quot;https:\/\/api.weixin.qq.com\/cgi-bin\/freepublish\/batchget?access_token=&quot; + accessToken;\n            URL url = new URL(urlStr);\n            HttpURLConnection conn = (HttpURLConnection) url.openConnection();\n            conn.setRequestMethod(&quot;POST&quot;);\n            conn.setDoOutput(true);\n            conn.setRequestProperty(&quot;Content-Type&quot;, &quot;application\/json; charset=utf-8&quot;);\n\n            String requestBody = &quot;{\\&quot;offset\\&quot;: 0, \\&quot;count\\&quot;: 20, \\&quot;no_content\\&quot;: 1}&quot;;\n            try (OutputStream os = conn.getOutputStream()) {\n                byte[] input = requestBody.getBytes(&quot;utf-8&quot;);\n                os.write(input, 0, input.length);\n            }\n\n            Scanner scanner = new Scanner(conn.getInputStream(), &quot;UTF-8&quot;);\n            String response = scanner.useDelimiter(&quot;\\\\A&quot;).next();\n            scanner.close();\n\n            ObjectMapper mapper = new ObjectMapper();\n            com.fasterxml.jackson.databind.JsonNode rootNode = mapper.readTree(response);\n            com.fasterxml.jackson.databind.JsonNode itemArray = rootNode.get(&quot;item&quot;);\n            if (itemArray != null &amp;&amp; itemArray.isArray()) {\n                boolean found = false;\n                for (com.fasterxml.jackson.databind.JsonNode item : itemArray) {\n                    com.fasterxml.jackson.databind.JsonNode newsItem = item.get(&quot;content&quot;).get(&quot;news_item&quot;);\n                    if (newsItem != null &amp;&amp; newsItem.isArray() &amp;&amp; newsItem.size() &gt; 0) {\n                        String title = newsItem.get(0).get(&quot;title&quot;).asText();\n                        if (targetTitle.equals(title) || title.contains(targetTitle)) {\n                            String article_id = item.get(&quot;article_id&quot;).asText();\n                            System.out.println(&quot;==================================================&quot;);\n                            System.out.println(&quot;\ud83c\udf89 \u6210\u529f\u627e\u5230\u5339\u914d\u7684\u6587\u7ae0\uff01&quot;);\n                            System.out.println(&quot;\u6587\u7ae0\u6807\u9898: &quot; + title);\n                            System.out.println(&quot;\u6587\u7ae0\u94fe\u63a5: &quot; + newsItem.get(0).get(&quot;url&quot;).asText());\n                            System.out.println(&quot;\ud83c\udfaf \u8be5\u6587\u7ae0\u7684 article_id: &quot; + article_id);\n                            System.out.println(&quot;==================================================&quot;);\n                            found = true;\n                            break;\n                        }\n                    }\n                }\n                if (!found) {\n                    System.out.println(&quot;\u274c \u5728\u6700\u8fd1\u53d1\u5e03\u768420\u7bc7\u6587\u7ae0\u4e2d\uff0c\u672a\u627e\u5230\u5305\u542b\u6807\u9898\u3010&quot; + targetTitle + &quot;\u3011\u7684\u6587\u7ae0\u3002&quot;);\n                }\n            } else {\n                System.out.println(&quot;\u5fae\u4fe1\u54cd\u5e94\u5f02\u5e38\u6216\u6ca1\u6709\u4efb\u4f55\u53d1\u5e03\u8bb0\u5f55: &quot; + response);\n            }\n\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n}\n<\/code><\/pre>\n<h2>\u5982\u679c\u8981\u76d1\u542c\u6d88\u606f\uff0c\u81ea\u52a8\u56de\u590d\u6d88\u606f\uff1a<\/h2>\n<p>\u5fae\u4fe1\u5f00\u53d1\u8005\u5e73\u53f0<br \/>\n1\u3002\u542f\u7528\u6d88\u606f\u63a8\u9001,\u9700\u8981\u914d\u7f6e URL \uff0cToken\uff0cEncodingAESKey<\/p>\n<h3>URL\uff1a\u9700\u8981\u5fae\u4fe1\u8ba4\u8bc1\u8fc7\u7684\u57df\u540d\uff0c\u6709\u516c\u7f51ip\u3002<\/h3>\n<p>\u5f00\u53d1\u8005\u7528\u6765\u63a5\u6536\u5fae\u4fe1\u6d88\u606f\u548c\u4e8b\u4ef6\u7684\u63a5\u53e3 URL\uff0c\u5fc5\u987b\u4ee5 http:\/\/ \u6216 https:\/\/ \u5f00\u5934\uff0c\u5206\u522b\u652f\u6301 80 \u7aef\u53e3\u548c 443 \u7aef\u53e3<\/p>\n<p>\u4f8b\u5982\uff1a<a href=\"http:\/\/baidu.com\/wx\/wx-event\">http:\/\/baidu.com\/wx\/wx-event<\/a><br \/>\n\u5176\u4e2d wx \u53ef\u4ee5\u81ea\u5b9a\u4e49\u3002\u4e3a\u4e86\u4e0d\u5f71\u54cd\u4e3b\u7ad9http:\/\/baidu.com\u7684\u4f7f\u7528\uff0c<a href=\"http:\/\/baidu.com\/wx\">http:\/\/baidu.com\/wx<\/a> \u901a\u8fc7nginx\u6307\u5411\u90e8\u7f72\u7684\u670d\u52a1\u5730\u5740\u3002wx-event \u662f\u76d1\u542c\u7684\u548c\u63a5\u53d7\u5fae\u4fe1\u6d88\u606f\u7684\u5730\u5740<\/p>\n<pre><code>@RequestMapping(&quot;\/wx-event&quot;)\npublic class WechatEventController {<\/code><\/pre>\n<h2>Token<\/h2>\n<p>Token\u4ee4\u724c\uff1a\u7528\u4e8e\u7b7e\u540d\u5904\u7406<\/p>\n<h2>EncodingAESKey<\/h2>\n<p>\u5c06\u7528\u4f5c\u6d88\u606f\u4f53\u52a0\u89e3\u5bc6\u5bc6\u94a5\u3002<br \/>\n\u6d88\u606f\u52a0\u89e3\u5bc6\u65b9\u5f0f\uff1a<br \/>\n\u660e\u6587\u6a21\u5f0f\uff1a\u4e0d\u4f7f\u7528\u6d88\u606f\u52a0\u89e3\u5bc6\uff0c\u660e\u6587\u53d1\u9001\uff0c\u5b89\u5168\u7cfb\u6570\u8f83\u4f4e\uff0c\u4e0d\u5efa\u8bae\u4f7f\u7528\u3002<br \/>\n\u517c\u5bb9\u6a21\u5f0f\uff1a\u660e\u6587\u3001\u5bc6\u6587\u5171\u5b58\uff0c\u4e0d\u5efa\u8bae\u4f7f\u7528\u3002<br \/>\n\u5b89\u5168\u6a21\u5f0f\uff1a\u4f7f\u7528\u6d88\u606f\u52a0\u89e3\u5bc6\uff0c\u7eaf\u5bc6\u6587\uff0c\u5b89\u5168\u7cfb\u6570\u9ad8\uff0c\u5f3a\u70c8\u63a8\u8350\u4f7f\u7528\u3002<br \/>\n\u6570\u636e\u683c\u5f0f\uff1a\u6d88\u606f\u4f53\u7684\u683c\u5f0f\uff0c\u4ec5\u652f\u6301 XML<br \/>\n\u5f00\u53d1\u8005\u53ef\u9009\u62e9\u6d88\u606f\u52a0\u89e3\u5bc6\u65b9\u5f0f\uff1a\u660e\u6587\u6a21\u5f0f\u3001\u517c\u5bb9\u6a21\u5f0f\u548c\u5b89\u5168\u6a21\u5f0f\u3002<\/p>\n<p>\u6a21\u5f0f\u7684\u9009\u62e9\u4e0e\u670d\u52a1\u5668\u914d\u7f6e\u5728\u63d0\u4ea4\u540e\u90fd\u4f1a\u7acb\u5373\u751f\u6548\uff0c\u8bf7\u5f00\u53d1\u8005\u8c28\u614e\u586b\u5199\u53ca\u9009\u62e9<\/p>\n<p>\u6d88\u606f\u52a0\u5bc6\u65b9\u5f0f<br \/>\n\u5b89\u5168\u6a21\u5f0f<\/p>\n<pre><code class=\"language-java\">package cn.hgsl.pp.hgslwxservice.controller;\n\nimport cn.hgsl.pp.hgslwxservice.configuration.WechatParamConfig;\nimport cn.hgsl.pp.hgslwxservice.utils.TempDbService;\nimport cn.hgsl.pp.hgslwxservice.wx.WeChatCryptoUtil;\nimport cn.hutool.core.date.DateUtil;\nimport cn.hutool.core.io.FileUtil;\nimport org.springframework.web.bind.annotation.*;\nimport javax.annotation.Resource;\nimport java.io.File;\nimport java.security.MessageDigest;\nimport java.util.Arrays;\n\n@RestController\n@RequestMapping(&quot;\/wx-event&quot;)\npublic class WechatEventController {\n    static {\n        try {\n            Class&lt;?&gt; jceSecurity = Class.forName(&quot;javax.crypto.JceSecurity&quot;);\n            java.lang.reflect.Field isRestrictedField = jceSecurity.getDeclaredField(&quot;isRestricted&quot;);\n            isRestrictedField.setAccessible(true);\n            if (java.lang.reflect.Modifier.isFinal(isRestrictedField.getModifiers())) {\n                java.lang.reflect.Field modifiersField = java.lang.reflect.Field.class.getDeclaredField(&quot;modifiers&quot;);\n                modifiersField.setAccessible(true);\n                modifiersField.setInt(isRestrictedField, isRestrictedField.getModifiers() &amp; ~java.lang.reflect.Modifier.FINAL);\n            }\n            isRestrictedField.set(null, java.lang.Boolean.FALSE);\n            System.out.println(&quot;====== \u6210\u529f\u901a\u8fc7\u53cd\u5c04\u89e3\u9664 JDK AES-256 \u52a0\u5bc6\u9650\u5236 ======&quot;);\n        } catch (Exception e) {\n            System.err.println(&quot;\u901a\u8fc7\u53cd\u5c04\u89e3\u9664 AES \u9650\u5236\u5931\u8d25: &quot; + e.getMessage());\n        }\n    }\n\n    @Resource\n    private TempDbService tempDbService;\n\n    @Resource\n    private WechatParamConfig wechatParamConfig;\n\n    @GetMapping\n    public String checkWxServer(String signature, String timestamp, String nonce, String echostr) {\n        String[] array = new String[]{wechatParamConfig.getMsgToken(), timestamp, nonce};\n        Arrays.sort(array);\n        String concatStr = String.join(&quot;&quot;, array);\n        String sha1Hex = sha1Encode(concatStr);\n        if (sha1Hex.equals(signature)) {\n            return echostr;\n        }\n        return &quot;\u6821\u9a8c\u5931\u8d25&quot;;\n    }\n\n    \/**\n     * \u63a5\u6536\u5fae\u4fe1\u63a8\u9001\u6d88\u606f\n     *\/\n    @PostMapping\n    public String listenUserMessage(\n            @RequestBody String xmlData,\n            @RequestParam(value = &quot;msg_signature&quot;, required = false) String msgSignature,\n            @RequestParam(value = &quot;timestamp&quot;, required = false) String timestamp,\n            @RequestParam(value = &quot;nonce&quot;, required = false) String nonce,\n            @RequestParam(value = &quot;encrypt_type&quot;, required = false) String encryptType) {\n        System.out.println(&quot;========= \u63a5\u6536\u5fae\u4fe1\u539f\u59cb\u6570\u636e =========&quot;);\n        System.out.println(xmlData);\n        boolean isAesMode = &quot;aes&quot;.equals(encryptType);\n        String plainXml = xmlData;\n        if (isAesMode) {\n            try {\n                plainXml = WeChatCryptoUtil.decryptMsg(\n                        wechatParamConfig.getMsgToken(),\n                        wechatParamConfig.getEncodingAesKey(),\n                        wechatParamConfig.getAppId(),\n                        msgSignature, timestamp, nonce, xmlData\n                );\n                System.out.println(&quot;========= \u89e3\u5bc6\u540e\u7684\u660e\u6587 XML =========&quot;);\n                System.out.println(plainXml);\n            } catch (Exception e) {\n                System.err.println(&quot;\u5fae\u4fe1\u6d88\u606f\u89e3\u5bc6\u5931\u8d25: &quot; + e.getMessage());\n                return &quot;fail&quot;;\n            }\n        }\n        String openid = getXmlValue(plainXml, &quot;FromUserName&quot;);\n        String toUser = getXmlValue(plainXml, &quot;ToUserName&quot;);\n        String msgType = getXmlValue(plainXml, &quot;MsgType&quot;);\n        if (!&quot;text&quot;.equals(msgType)) {\n            return &quot;success&quot;;\n        }\n        String content = getXmlValue(plainXml, &quot;Content&quot;);\n        String time = DateUtil.now();\n        String logLine = openid + &quot; | &quot; + time + &quot; | &quot; + msgType + &quot; | &quot; + content + &quot;\\n&quot;;\n        String userDir = System.getProperty(&quot;user.dir&quot;);\n        FileUtil.appendUtf8String(logLine, userDir + File.separator + &quot;wx-msg.log&quot;);\n        content = content.trim();\n        System.out.println(&quot;toUser\uff1a&quot; + toUser);\n        System.out.println(&quot;openid\uff1a&quot; + openid);\n        System.out.println(&quot;\u5185\u5bb9\uff1a&quot; + content);\n        String replyContent;\n        if (content.startsWith(&quot;\u7ed1\u5b9a&quot;)) {\n            String phone = content.replaceFirst(&quot;^\u7ed1\u5b9a&quot;, &quot;&quot;).trim();\n            String phoneRegex = &quot;^1[3-9]\\\\d{9}$&quot;;\n            if (phone.matches(phoneRegex)) {\n                tempDbService.put(phone, openid);\n                replyContent = &quot;\u7ed1\u5b9a\u6210\u529f \u2705\\n\\n\u624b\u673a\u53f7\uff1a&quot; + phone + &quot;\\n\u7ed1\u5b9a\u65f6\u95f4\uff1a&quot; + time;\n            } else {\n                replyContent = &quot;\u624b\u673a\u53f7\u683c\u5f0f\u9519\u8bef \u274c\\n\\n\u6b63\u786e\u683c\u5f0f\uff1a\u7ed1\u5b9a13800138000&quot;;\n            }\n        } else {\n            replyContent = &quot;\u5730\u5740\uff1aXXXXX-529&quot;;\n        }\n\n        String replyXml = buildTextMessage(openid, toUser, replyContent);\n        if (isAesMode) {\n            try {\n                return WeChatCryptoUtil.encryptMsg(\n                        wechatParamConfig.getMsgToken(),\n                        wechatParamConfig.getEncodingAesKey(),\n                        wechatParamConfig.getAppId(),\n                        replyXml, timestamp, nonce\n                );\n            } catch (Exception e) {\n                System.err.println(&quot;\u5fae\u4fe1\u6d88\u606f\u52a0\u5bc6\u5931\u8d25: &quot; + e.getMessage());\n                return &quot;success&quot;;\n            }\n        }\n        return replyXml;\n    }\n\n    private String getXmlValue(String xml, String tag) {\n        String startTag = &quot;&lt;&quot; + tag + &quot;&gt;&lt;![CDATA[&quot;;\n        String endTag = &quot;]]&gt;&lt;\/&quot; + tag + &quot;&gt;&quot;;\n        int startIndex = xml.indexOf(startTag);\n        int endIndex = xml.indexOf(endTag);\n\n        if (startIndex == -1 || endIndex == -1) {\n            return &quot;&quot;;\n        }\n        startIndex += startTag.length();\n        if (startIndex &gt; endIndex) {\n            return &quot;&quot;;\n        }\n        return xml.substring(startIndex, endIndex);\n    }\n\n    private String sha1Encode(String data) {\n        try {\n            MessageDigest md = MessageDigest.getInstance(&quot;SHA-1&quot;);\n            md.update(data.getBytes());\n            byte[] digest = md.digest();\n            StringBuilder sb = new StringBuilder();\n            for (byte b : digest) {\n                String hex = Integer.toHexString(b &amp; 0xFF);\n                if (hex.length() == 1) sb.append(&quot;0&quot;);\n                sb.append(hex);\n            }\n            return sb.toString();\n        } catch (Exception e) {\n            return &quot;&quot;;\n        }\n    }\n\n    private String buildTextMessage(String toUser, String fromUser, String content) {\n        return &quot;&lt;xml&gt;&quot; +\n                &quot;&lt;ToUserName&gt;&lt;![CDATA[&quot; + toUser + &quot;]]&gt;&lt;\/ToUserName&gt;&quot; +\n                &quot;&lt;FromUserName&gt;&lt;![CDATA[&quot; + fromUser + &quot;]]&gt;&lt;\/FromUserName&gt;&quot; +\n                &quot;&lt;CreateTime&gt;&quot; + System.currentTimeMillis() \/ 1000 + &quot;&lt;\/CreateTime&gt;&quot; +\n                &quot;&lt;MsgType&gt;&lt;![CDATA[text]]&gt;&lt;\/MsgType&gt;&quot; +\n                &quot;&lt;Content&gt;&lt;![CDATA[&quot; + content + &quot;]]&gt;&lt;\/Content&gt;&quot; +\n                &quot;&lt;\/xml&gt;&quot;;\n    }\n}<\/code><\/pre>\n<pre><code class=\"language-java\">package cn.hgsl.pp.hgslwxservice.wx;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.spec.IvParameterSpec;\nimport javax.crypto.spec.SecretKeySpec;\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.concurrent.ThreadLocalRandom;\n\npublic class WeChatCryptoUtil {\n\n    \/**\n     * \u89e3\u5bc6\u5fae\u4fe1\u6d88\u606f\n     *\/\n    public static String decryptMsg(String token, String encodingAesKey, String appId,\n                                    String msgSignature, String timestamp, String nonce, String postData) throws Exception {\n\n        String encrypt = getXmlValue(postData, &quot;Encrypt&quot;);\n        if (encrypt.isEmpty()) {\n            throw new IllegalArgumentException(&quot;\u5bc6\u6587\u5185\u5bb9\u4e0d\u5b58\u5728&quot;);\n        }\n        String[] array = new String[]{token, timestamp, nonce, encrypt};\n        Arrays.sort(array);\n        String sha1Hex = sha1Encode(String.join(&quot;&quot;, array));\n        if (!sha1Hex.equals(msgSignature)) {\n            throw new SecurityException(&quot;\u5fae\u4fe1\u7b7e\u540d\u9a8c\u8bc1\u5931\u8d25&quot;);\n        }\n        byte[] aesKey = Base64.getDecoder().decode(encodingAesKey + &quot;=&quot;);\n        byte[] iv = Arrays.copyOfRange(aesKey, 0, 16);\n        Cipher cipher = Cipher.getInstance(&quot;AES\/CBC\/NoPadding&quot;);\n        SecretKeySpec keySpec = new SecretKeySpec(aesKey, &quot;AES&quot;);\n        IvParameterSpec ivSpec = new IvParameterSpec(iv);\n        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);\n\n        byte[] encryptedBytes = Base64.getDecoder().decode(encrypt);\n        byte[] decryptedBytes = cipher.doFinal(encryptedBytes);\n        byte[] bytes = decodePkcs7(decryptedBytes);\n\n        byte[] networkOrderUuidBytes = Arrays.copyOfRange(bytes, 16, 20);\n        int xmlLength = recoverNetworkBytesOrderToInt(networkOrderUuidBytes);\n\n        String xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), StandardCharsets.UTF_8);\n        String fromAppId = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), StandardCharsets.UTF_8);\n\n        if (!fromAppId.equals(appId)) {\n            throw new SecurityException(&quot;AppId \u6821\u9a8c\u4e0d\u5339\u914d\uff0c\u53ef\u80fd\u906d\u53d7\u975e\u6cd5\u8bf7\u6c42&quot;);\n        }\n        return xmlContent;\n    }\n\n    \/**\n     * \u52a0\u5bc6\u56de\u590d\u6d88\u606f\u5e76\u7ec4\u88c5\u6210\u56de\u590d XML\n     *\/\n    public static String encryptMsg(String token, String encodingAesKey, String appId,\n                                    String replyXml, String timestamp, String nonce) throws Exception {\n        byte[] aesKey = Base64.getDecoder().decode(encodingAesKey + &quot;=&quot;);\n        byte[] iv = Arrays.copyOfRange(aesKey, 0, 16);\n        byte[] randomBytes = new byte[16];\n        ThreadLocalRandom.current().nextBytes(randomBytes);\n\n        byte[] xmlBytes = replyXml.getBytes(StandardCharsets.UTF_8);\n        byte[] networkOrderUuidBytes = getNetworkBytesOrder(xmlBytes.length);\n        byte[] appIdBytes = appId.getBytes(StandardCharsets.UTF_8);\n\n        int totalLength = randomBytes.length + networkOrderUuidBytes.length + xmlBytes.length + appIdBytes.length;\n        byte[] plainBytes = new byte[totalLength];\n\n        System.arraycopy(randomBytes, 0, plainBytes, 0, 16);\n        System.arraycopy(networkOrderUuidBytes, 0, plainBytes, 16, 4);\n        System.arraycopy(xmlBytes, 0, plainBytes, 20, xmlBytes.length);\n        System.arraycopy(appIdBytes, 0, plainBytes, 20 + xmlBytes.length, appIdBytes.length);\n\n        byte[] paddedBytes = encodePkcs7(plainBytes);\n        Cipher cipher = Cipher.getInstance(&quot;AES\/CBC\/NoPadding&quot;);\n        SecretKeySpec keySpec = new SecretKeySpec(aesKey, &quot;AES&quot;);\n        IvParameterSpec ivSpec = new IvParameterSpec(iv);\n        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);\n\n        byte[] encryptedBytes = cipher.doFinal(paddedBytes);\n        String encryptBase64 = Base64.getEncoder().encodeToString(encryptedBytes);\n\n        String[] array = new String[]{token, timestamp, nonce, encryptBase64};\n        Arrays.sort(array);\n        String sha1Hex = sha1Encode(String.join(&quot;&quot;, array));\n\n        return &quot;&lt;xml&gt;&quot; +\n                &quot;&lt;Encrypt&gt;&lt;![CDATA[&quot; + encryptBase64 + &quot;]]&gt;&lt;\/Encrypt&gt;&quot; +\n                &quot;&lt;MsgSignature&gt;&lt;![CDATA[&quot; + sha1Hex + &quot;]]&gt;&lt;\/MsgSignature&gt;&quot; +\n                &quot;&lt;TimeStamp&gt;&quot; + timestamp + &quot;&lt;\/TimeStamp&gt;&quot; +\n                &quot;&lt;Nonce&gt;&lt;![CDATA[&quot; + nonce + &quot;]]&gt;&lt;\/Nonce&gt;&quot; +\n                &quot;&lt;\/xml&gt;&quot;;\n    }\n\n    private static byte[] decodePkcs7(byte[] decrypted) {\n        int pad = decrypted[decrypted.length - 1];\n        if (pad &lt; 1 || pad &gt; 32) pad = 0;\n        return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);\n    }\n\n    private static byte[] encodePkcs7(byte[] count) {\n        int blockSize = 32;\n        int amountToPad = blockSize - (count.length % blockSize);\n        if (amountToPad == 0) amountToPad = blockSize;\n        byte padChr = (byte) (amountToPad &amp; 0xFF);\n        byte[] padding = new byte[amountToPad];\n        Arrays.fill(padding, padChr);\n        byte[] updated = new byte[count.length + amountToPad];\n        System.arraycopy(count, 0, updated, 0, count.length);\n        System.arraycopy(padding, 0, updated, count.length, amountToPad);\n        return updated;\n    }\n\n    private static int recoverNetworkBytesOrderToInt(byte[] orderBytes) {\n        int sourceNumber = 0;\n        for (int i = 0; i &lt; 4; i++) {\n            sourceNumber &lt;&lt;= 8;\n            sourceNumber |= (orderBytes[i] &amp; 0xff);\n        }\n        return sourceNumber;\n    }\n\n    private static byte[] getNetworkBytesOrder(int sourceNumber) {\n        byte[] orderBytes = new byte[4];\n        orderBytes[3] = (byte) (sourceNumber &amp; 0xFF);\n        orderBytes[2] = (byte) ((sourceNumber &gt;&gt; 8) &amp; 0xFF);\n        orderBytes[1] = (byte) ((sourceNumber &gt;&gt; 16) &amp; 0xFF);\n        orderBytes[0] = (byte) ((sourceNumber &gt;&gt; 24) &amp; 0xFF);\n        return orderBytes;\n    }\n\n    private static String getXmlValue(String xml, String tag) {\n        String startTag = &quot;&lt;&quot; + tag + &quot;&gt;&lt;![CDATA[&quot;;\n        String endTag = &quot;]]&gt;&lt;\/&quot; + tag + &quot;&gt;&quot;;\n        int startIndex = xml.indexOf(startTag);\n        int endIndex = xml.indexOf(endTag);\n        if (startIndex == -1 || endIndex == -1) return &quot;&quot;;\n        return xml.substring(startIndex + startTag.length(), endIndex);\n    }\n\n    private static String sha1Encode(String data) throws Exception {\n        MessageDigest md = MessageDigest.getInstance(&quot;SHA-1&quot;);\n        byte[] digest = md.digest(data.getBytes(StandardCharsets.UTF_8));\n        StringBuilder sb = new StringBuilder();\n        for (byte b : digest) {\n            String hex = Integer.toHexString(b &amp; 0xFF);\n            if (hex.length() == 1) sb.append(&quot;0&quot;);\n            sb.append(hex);\n        }\n        return sb.toString();\n    }\n}\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>\u5fae\u4fe1\u670d\u52a1\u53f7\u5e38\u89c1\u4e09\u5927\u914d\u7f6e\u533a\u522b \u914d\u7f6e\u9879 \u7528\u9014 \u4f5c\u7528\u5bf9\u8c61 \u662f\u5426\u5fc5\u987b\u516c\u7f51 \u662f\u5426\u9650\u523680\/443 API IP\u767d\u540d\u5355 \u540e [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[135],"tags":[],"class_list":["post-1667","post","type-post","status-publish","format-standard","hentry","category-code-snippets"],"_links":{"self":[{"href":"https:\/\/blog.ttwow.top\/index.php?rest_route=\/wp\/v2\/posts\/1667","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.ttwow.top\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.ttwow.top\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.ttwow.top\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.ttwow.top\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1667"}],"version-history":[{"count":8,"href":"https:\/\/blog.ttwow.top\/index.php?rest_route=\/wp\/v2\/posts\/1667\/revisions"}],"predecessor-version":[{"id":1676,"href":"https:\/\/blog.ttwow.top\/index.php?rest_route=\/wp\/v2\/posts\/1667\/revisions\/1676"}],"wp:attachment":[{"href":"https:\/\/blog.ttwow.top\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1667"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.ttwow.top\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1667"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.ttwow.top\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1667"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}