This commit is contained in:
2025-08-31 00:50:01 +08:00
parent ec9416e390
commit 3960baa105
3 changed files with 260 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
package cn.van333.wxsend.business.controller;
import cn.hutool.core.util.StrUtil;
import cn.van333.wxsend.business.model.R;
import cn.van333.wxsend.util.WeComCallbackCrypto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/wecom/callback")
public class WeComCallbackController {
private static final Logger logger = LoggerFactory.getLogger(WeComCallbackController.class);
@Value("${qywx.app.corpId:}")
private String corpId;
@Value("${qywx.app.token:}")
private String token;
@Value("${qywx.app.encodingAESKey:}")
private String encodingAESKey;
@GetMapping(produces = MediaType.TEXT_PLAIN_VALUE)
public String verify(@RequestParam("msg_signature") String msgSignature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce,
@RequestParam("echostr") String echostr) {
logger.info("WeCom callback verify: ts={}, nonce={}", timestamp, nonce);
WeComCallbackCrypto crypto = new WeComCallbackCrypto(token, encodingAESKey, corpId);
String plain = crypto.verifyURL(msgSignature, timestamp, nonce, echostr);
return plain;
}
@PostMapping(consumes = MediaType.TEXT_XML_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
public String receiveXml(@RequestParam("msg_signature") String msgSignature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce,
HttpServletRequest request) throws IOException {
String xml = readBody(request);
logger.info("WeCom callback received xml: {}", xml);
String encrypt = parseEncrypt(xml);
WeComCallbackCrypto crypto = new WeComCallbackCrypto(token, encodingAESKey, corpId);
String plainXml = crypto.decryptMsg(msgSignature, timestamp, nonce, encrypt);
logger.info("WeCom callback plain xml: {}", plainXml);
// TODO: 在此解析 plainXml 的 MsgType/Event/Content 等,进行业务处理
return "success";
}
private static String readBody(HttpServletRequest request) throws IOException {
StringBuilder sb = new StringBuilder();
try (BufferedReader reader = request.getReader()) {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
}
return sb.toString();
}
private static String parseEncrypt(String xml) {
// 简单提取 <Encrypt><![CDATA[...]]></Encrypt>
String start = "<Encrypt><![CDATA[";
String end = "]]></Encrypt>";
int i = xml.indexOf(start);
int j = xml.indexOf(end);
if (i >= 0 && j > i) {
return xml.substring(i + start.length(), j);
}
// 兼容无 CDATA 的情况
start = "<Encrypt>";
end = "</Encrypt>";
i = xml.indexOf(start);
j = xml.indexOf(end);
if (i >= 0 && j > i) {
return xml.substring(i + start.length(), j);
}
throw new RuntimeException("Encrypt not found");
}
}