| 模式 | 消息處理 | 功能範圍 | 適用場景 |
|---|---|---|---|
| 編輯模式 | 微信服務器內置邏輯 | 自定義菜單、關鍵字自動回覆 | 簡單營銷、無開發能力 |
| 開發模式 | 第三方服務器 (回調 URL) | 全接口能力:消息收發、通訊錄、應用、素材、OA | 企業級應用、定制化開發 |
在企業號管理後台 → 我的企業 → 應用中心 → 設置 → API 接口:
https://api.yourcorp.com/wx/callback),需公網可訪問、HTTPS、80/443 端口weixinCourse)DjlyZxgKiWRESIW2VnV9dSr7HsS7usWDfnwA8Q1ove1)
微信服務器 → GET /wx/callback?msg_signature=xxx×tamp=xxx&nonce=xxx&echostr=xxx
↓
企業服務器
1. 解析參數
2. 計算簽名驗證
3. 解密 echostr
4. 原文返回 echostr
↓
微信服務器收到 echostr 明文 → 驗證通過 → 啟用成功
@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
);
}
}
EncodingAESKey + "=" → Base64 解碼 → 32 字節 Key16字節隨機 + 4字節網絡序長度 + XML內容 + CorpId| 現象 | 原因 | 解決 |
|---|---|---|
| URL驗證失敗 | Token/EncodingAESKey 不一致、未安裝 JCE、簽名算法錯 | 核對配置、安裝 JCE、使用官方 Demo 驗證 |
| 解密報 IllegalBuffer | 消息體損壞、CorpId 不匹配、Base64 解碼錯 | 檢查 CorpId、確保請求體完整讀取 |
| 簽名驗證失敗 | 參數排序錯、編碼不一致、timestamp 過期 | 嚴格按字典序排序、UTF-8、5分鐘有效期 |
| 回覆微信不顯示 | 未加密回覆、XML 格式錯、CDATA 缺失 | 必須 EncryptMsg、CDATA 包裹、5秒內返回 |
@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 無限制權限。生產環境建議:配置中心管理密鑰、異步處理業務邏輯、完整記錄收發日誌、監控回調成功率。