首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

微信企業號開發模式啟用全流程:從回調配置到消息加解密

     編輯:Java 相關知識

開發模式 vs 編輯模式

模式消息處理功能範圍適用場景
編輯模式微信服務器內置邏輯自定義菜單、關鍵字自動回覆簡單營銷、無開發能力
開發模式第三方服務器 (回調 URL)全接口能力:消息收發、通訊錄、應用、素材、OA企業級應用、定制化開發

開發模式啟用配置

在企業號管理後台 → 我的企業 → 應用中心 → 設置 → API 接口:

  • URL:回調接口地址 (如 https://api.yourcorp.com/wx/callback),需公網可訪問、HTTPS、80/443 端口
  • Token:隨機字符串,用於簽名驗證 (如 weixinCourse)
  • EncodingAESKey:43 位 Base64 字符串,用於消息體加密 (如 DjlyZxgKiWRESIW2VnV9dSr7HsS7usWDfnwA8Q1ove1)

URL 驗證流程 (GET 請求)


微信服務器 → GET /wx/callback?msg_signature=xxx×tamp=xxx&nonce=xxx&echostr=xxx
                    ↓
            企業服務器
            1. 解析參數
            2. 計算簽名驗證
            3. 解密 echostr
            4. 原文返回 echostr
                    ↓
微信服務器收到 echostr 明文 → 驗證通過 → 啟用成功

核心 Servlet 實現


@WebServlet("/wx/callback")
public class WxCallbackServlet extends HttpServlet {
    
    private static final String TOKEN = "weixinCourse";
    private static final String CORP_ID = "wxe510946434680dab";
    private static final String ENCODING_AES_KEY = "DjlyZxgKiWRESIW2VnV9dSr7HsS7usWDfnwA8Q1ove1";
    
    private final WXBizMsgCrypt wxCrypt = new WXBizMsgCrypt(TOKEN, ENCODING_AES_KEY, CORP_ID);
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String msgSig = req.getParameter("msg_signature");
        String timestamp = req.getParameter("timestamp");
        String nonce = req.getParameter("nonce");
        String echostr = req.getParameter("echostr");
        
        try {
            // 驗證簽名並解密 echostr
            String plainEchostr = wxCrypt.VerifyURL(msgSig, timestamp, nonce, echostr);
            // 原樣返回明文 (不加引號、不加空格)
            resp.getWriter().write(plainEchostr);
            log.info("URL驗證成功");
        } catch (AesException e) {
            log.error("URL驗證失敗", e);
            resp.setStatus(403);
        }
    }
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        // 1. 讀取加密 XML
        String reqData = IOUtils.toString(req.getInputStream(), StandardCharsets.UTF_8);
        
        // 2. 解析 XML 獲取加密字段
        Document doc = DocumentHelper.parseText(reqData);
        String encrypt = doc.selectSingleNode("//Encrypt").getText();
        String msgSig = req.getParameter("msg_signature");
        String timestamp = req.getParameter("timestamp");
        String nonce = req.getParameter("nonce");
        
        // 3. 解密
        String plainXml;
        try {
            plainXml = wxCrypt.DecryptMsg(msgSig, timestamp, nonce, encrypt);
        } catch (AesException e) {
            log.error("解密失敗", e);
            resp.setStatus(403);
            return;
        }
        
        // 4. 解析明文 XML 處理業務
        Document plainDoc = DocumentHelper.parseText(plainXml);
        String msgType = plainDoc.selectSingleNode("//MsgType").getText();
        String fromUser = plainDoc.selectSingleNode("//FromUserName").getText();
        
        String replyXml = handleMessage(msgType, fromUser, plainDoc);
        
        // 5. 加密回覆
        String encryptReply;
        try {
            encryptReply = wxCrypt.EncryptMsg(replyXml, timestamp, nonce);
        } catch (AesException e) {
            log.error("加密回覆失敗", e);
            return;
        }
        
        // 6. 返回加密 XML
        resp.setContentType("application/xml;charset=UTF-8");
        resp.getWriter().write(encryptReply);
    }
    
    private String handleMessage(String msgType, String fromUser, Document doc) {
        // 文本消息回覆示例
        if ("text".equals(msgType)) {
            String content = doc.selectSingleNode("//Content").getText();
            return buildTextReply(fromUser, "收到: " + content);
        }
        // 事件推薦:subscribe, click, view, scan 等
        return buildTextReply(fromUser, "不支持的消息類型");
    }
    
    private String buildTextReply(String toUser, String content) {
        return String.format(
            "" +
            "" +
            "%d" +
            "" +
            "",
            toUser, CORP_ID, System.currentTimeMillis() / 1000, content
        );
    }
}

加解密核心類 (WXBizMsgCrypt) 關鍵點

  • 算法:AES-256-CBC + PKCS7Padding + SHA1 簽名
  • 密鑰派生:EncodingAESKey + "=" → Base64 解碼 → 32 字節 Key
  • IV:Key 前 16 字節
  • 消息結構:16字節隨機 + 4字節網絡序長度 + XML內容 + CorpId
  • JCE 無限制權限策略文件必須安裝 (JDK 8 需手動,JDK 9+ 內置)

常見問題排查

現象原因解決
URL驗證失敗Token/EncodingAESKey 不一致、未安裝 JCE、簽名算法錯核對配置、安裝 JCE、使用官方 Demo 驗證
解密報 IllegalBuffer消息體損壞、CorpId 不匹配、Base64 解碼錯檢查 CorpId、確保請求體完整讀取
簽名驗證失敗參數排序錯、編碼不一致、timestamp 過期嚴格按字典序排序、UTF-8、5分鐘有效期
回覆微信不顯示未加密回覆、XML 格式錯、CDATA 缺失必須 EncryptMsg、CDATA 包裹、5秒內返回

Spring Boot 整合最佳實踐


@Configuration
public class WxConfig {
    @Value("${wx.token}") String token;
    @Value("${wx.encoding-aes-key}") String encodingAesKey;
    @Value("${wx.corp-id}") String corpId;
    
    @Bean
    public WXBizMsgCrypt wxBizMsgCrypt() throws AesException {
        return new WXBizMsgCrypt(token, encodingAesKey, corpId);
    }
}

@RestController
@RequestMapping("/wx")
public class WxController {
    @Autowired WXBizMsgCrypt wxCrypt;
    
    @GetMapping("/callback")
    public String verifyUrl(@RequestParam("msg_signature") String msgSig,
                            @RequestParam("timestamp") String timestamp,
                            @RequestParam("nonce") String nonce,
                            @RequestParam("echostr") String echostr) throws AesException {
        return wxCrypt.VerifyURL(msgSig, timestamp, nonce, echostr);
    }
    
    @PostMapping("/callback")
    public String handleMessage(HttpServletRequest request) throws Exception {
        // 同 Servlet 邏輯,返回加密 XML 字符串
    }
}

總結

微信企業號開發模式啟用核心三步:配置回調 URL/Token/EncodingAESKey → 實現 GET 驗證邏輯 (VerifyURL) → 實現 POST 消息收發 (DecryptMsg/EncryptMsg)。加解密使用官方 WXBizMsgCrypt,務必安裝 JCE 無限制權限。生產環境建議:配置中心管理密鑰、異步處理業務邏輯、完整記錄收發日誌、監控回調成功率。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved