|
|
@ -38,8 +38,13 @@ import org.springframework.beans.factory.annotation.Value; |
|
|
import org.springframework.stereotype.Controller; |
|
|
import org.springframework.stereotype.Controller; |
|
|
import org.springframework.web.bind.annotation.*; |
|
|
import org.springframework.web.bind.annotation.*; |
|
|
|
|
|
|
|
|
|
|
|
import javax.servlet.http.HttpServletRequest; |
|
|
|
|
|
import java.io.ByteArrayOutputStream; |
|
|
|
|
|
import java.io.IOException; |
|
|
|
|
|
import java.io.InputStream; |
|
|
import java.io.UnsupportedEncodingException; |
|
|
import java.io.UnsupportedEncodingException; |
|
|
import java.net.URLEncoder; |
|
|
import java.net.URLEncoder; |
|
|
|
|
|
import java.nio.charset.StandardCharsets; |
|
|
import java.util.ArrayList; |
|
|
import java.util.ArrayList; |
|
|
import java.util.List; |
|
|
import java.util.List; |
|
|
import java.util.concurrent.TimeUnit; |
|
|
import java.util.concurrent.TimeUnit; |
|
|
@ -231,39 +236,95 @@ public class WechatController { |
|
|
* 公众号回调接口(验证成功之后) |
|
|
* 公众号回调接口(验证成功之后) |
|
|
* @author 王富康 |
|
|
* @author 王富康 |
|
|
* @date 2025/1/15 |
|
|
* @date 2025/1/15 |
|
|
* @param requestBody |
|
|
|
|
|
* @param signature |
|
|
* @param signature |
|
|
* @param timestamp |
|
|
* @param timestamp |
|
|
* @param nonce |
|
|
* @param nonce |
|
|
* @return String |
|
|
* @return String |
|
|
*/ |
|
|
*/ |
|
|
@RequestMapping("echo") |
|
|
@RequestMapping(value = "/echo", method = {RequestMethod.GET, RequestMethod.POST}) |
|
|
@ResponseBody |
|
|
@ResponseBody |
|
|
public String configAccess(@RequestBody String requestBody, @RequestParam("signature") String signature, @RequestParam("timestamp") String timestamp, @RequestParam("nonce") String nonce) { |
|
|
public String configAccess(HttpServletRequest request, |
|
|
// 校验签名
|
|
|
@RequestParam(value = "signature", required = false) String signature, |
|
|
|
|
|
@RequestParam(value = "timestamp", required = false) String timestamp, |
|
|
|
|
|
@RequestParam(value = "nonce", required = false) String nonce, |
|
|
|
|
|
@RequestParam(value = "echostr", required = false) String echostr) { |
|
|
|
|
|
|
|
|
|
|
|
String method = request.getMethod(); |
|
|
|
|
|
log.info("====== 收到微信请求 [{}] ======", method); |
|
|
|
|
|
|
|
|
|
|
|
// 1. 处理 GET 请求 (验证)
|
|
|
|
|
|
if ("GET".equalsIgnoreCase(method)) { |
|
|
|
|
|
if (!wxMpService.checkSignature(timestamp, nonce, signature)) { |
|
|
|
|
|
log.error("签名校验失败 (GET)"); |
|
|
|
|
|
return "error"; |
|
|
|
|
|
} |
|
|
|
|
|
log.info("验证成功,返回 echostr: {}", echostr); |
|
|
|
|
|
return echostr; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 2. 处理 POST 请求 (消息推送)
|
|
|
|
|
|
if ("POST".equalsIgnoreCase(method)) { |
|
|
if (!wxMpService.checkSignature(timestamp, nonce, signature)) { |
|
|
if (!wxMpService.checkSignature(timestamp, nonce, signature)) { |
|
|
log.error("签名校验 ===》 非法请求"); |
|
|
log.error("签名校验失败 (POST)"); |
|
|
// 消息签名不正确,说明不是公众平台发过来的消息
|
|
|
return ""; |
|
|
return null; |
|
|
|
|
|
} |
|
|
} |
|
|
log.error("签名校验 ===》 验证成功"); |
|
|
|
|
|
|
|
|
|
|
|
// 解析消息体,封装为对象
|
|
|
// 【核心修复】兼容 Java 8 的流读取方式
|
|
|
final WxMpXmlMessage xmlMessage = WxMpXmlMessage.fromXml(requestBody); |
|
|
String requestBody = ""; |
|
|
|
|
|
try { |
|
|
|
|
|
InputStream inputStream = request.getInputStream(); |
|
|
|
|
|
ByteArrayOutputStream result = new ByteArrayOutputStream(); |
|
|
|
|
|
byte[] buffer = new byte[1024]; |
|
|
|
|
|
int length; |
|
|
|
|
|
while ((length = inputStream.read(buffer)) != -1) { |
|
|
|
|
|
result.write(buffer, 0, length); |
|
|
|
|
|
} |
|
|
|
|
|
// 微信默认 UTF-8 编码
|
|
|
|
|
|
requestBody = result.toString(StandardCharsets.UTF_8.name()); |
|
|
|
|
|
} catch (IOException e) { |
|
|
|
|
|
log.error("读取请求体 IO 异常", e); |
|
|
|
|
|
return "success"; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
log.info(">>> 原始 XML 内容:\n{}", requestBody); |
|
|
|
|
|
|
|
|
|
|
|
if (requestBody == null || requestBody.trim().isEmpty()) { |
|
|
|
|
|
log.error("!!! 请求体为空,无法解析 !!!"); |
|
|
|
|
|
return "success"; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 解析 XML
|
|
|
|
|
|
WxMpXmlMessage xmlMessage; |
|
|
|
|
|
try { |
|
|
|
|
|
xmlMessage = WxMpXmlMessage.fromXml(requestBody); |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
log.error("XML 解析异常", e); |
|
|
|
|
|
log.error("原始内容可能是: {}", requestBody.substring(0, Math.min(100, requestBody.length()))); |
|
|
|
|
|
return "success"; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
log.info("解析结果 -> MsgType: {}, Event: {}, FromUser: {}", |
|
|
|
|
|
xmlMessage.getMsgType(), xmlMessage.getEvent(), xmlMessage.getFromUser()); |
|
|
|
|
|
|
|
|
|
|
|
// 路由
|
|
|
WxMpXmlOutMessage outMessage = null; |
|
|
WxMpXmlOutMessage outMessage = null; |
|
|
try { |
|
|
try { |
|
|
// 将消息路由给对应的处理器,获取响应
|
|
|
|
|
|
outMessage = wxMpMessageRouter.route(xmlMessage); |
|
|
outMessage = wxMpMessageRouter.route(xmlMessage); |
|
|
} catch (Exception e) { |
|
|
} catch (Exception e) { |
|
|
log.error("消息路由异常", e); |
|
|
log.error("消息路由异常", e); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (outMessage != null) { |
|
|
if (outMessage != null) { |
|
|
log.info("到了返回了========" + outMessage.toXml()); |
|
|
String xmlStr = outMessage.toXml(); |
|
|
|
|
|
log.info("返回消息: {}", xmlStr); |
|
|
|
|
|
return xmlStr; |
|
|
} else { |
|
|
} else { |
|
|
log.info("到了返回了========outMessage为空!"); |
|
|
log.info("路由未匹配,返回 success"); |
|
|
|
|
|
return "success"; |
|
|
} |
|
|
} |
|
|
// 将响应消息转换为xml格式返回
|
|
|
} |
|
|
return outMessage == null ? null : outMessage.toXml(); |
|
|
|
|
|
|
|
|
return ""; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
|