处理微信回调事件

一、背景

在​​上一节​​​中,我们知道如何接入微信公众号,但是之后公众号会与我们进行交互,那么微信公众号如何通知到我们自己的服务器呢?我们知道我们接入的时候提供的url是 ​​GET​​​ ​​/mp/entry​​​,那么公众号之后产生的事件将会以 ​​POST​​​ ​​/mp/entry​​ 发送到我们自己的服务器上。

二、代码实现,此处还是使用 weixin-java-mp 这个框架实现

1、引入 weixin-java-mp

<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>4.0.0</version>
</dependency>

2、编写 配置

@Configuration
public class WxMpConfiguration {

@Autowired
private WxMpProperties wxMpProperties;
@Autowired
private SubscribeHandler subscribeHandler;
@Autowired
private UnsubscribeHandler unsubscribeHandler;

@Bean
public WxMpService wxMpService() {
WxMpServiceImpl wxMpService = new WxMpServiceImpl();
wxMpService.setWxMpConfigStorage(wxMpConfigStorage());
// 设置多个微信公众号的配置
// wxMpService.setMultiConfigStorages();
return wxMpService;
}

/**
* 这个地方的配置是保存在本地,生产环境需要自己扩展,可以保存在Redis中等等
*
* @return WxMpConfigStorage
*/
public WxMpConfigStorage wxMpConfigStorage() {
WxMpDefaultConfigImpl storage = new WxMpDefaultConfigImpl();
storage.setAppId(wxMpProperties.getAppId());
storage.setSecret(wxMpProperties.getAppSecret());
storage.setAesKey(wxMpProperties.getAesKey());
storage.setToken(wxMpProperties.getToken());
return storage;
}

@Bean
public WxMpMessageRouter messageRouter(WxMpService wxMpService) {
WxMpMessageRouter router = new WxMpMessageRouter(wxMpService);
// 消息去重
router.setMessageDuplicateChecker(new WxMessageInMemoryDuplicateChecker());

// 关注事件
router.rule().async(false).msgType(EVENT).event(SUBSCRIBE)
.handler(this.subscribeHandler)
.end();
// 取消关注事件
router.rule().async(false).msgType(EVENT).event(UNSUBSCRIBE)
.handler(this.unsubscribeHandler)
.end();
return router;
}
}

1、公众号回调给我们的消息会通过 ​​WxMpMessageRouter​​​ 来处理。
2、配置的时候需要做好消息的去重处理,如果我们的服务器​​​5s​​​种没给微信服务器响应,微信服务器会重试 ​​3次​​​。
3、处理消息实现​​​WxMpMessageHandler​​接口

3、回调入口

/**
* 微信回调
*
* @param requestBody
* @param signature
* @param timestamp
* @param nonce
* @param openid
* @param encType
* @param msgSignature
* @return
*/
@PostMapping("/mp/entry")
public String entryCallback(@RequestBody String requestBody,
@RequestParam("signature") String signature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce,
@RequestParam("openid") String openid,
@RequestParam(name = "encrypt_type", required = false) String encType,
@RequestParam(name = "msg_signature", required = false) String msgSignature) {
log.info("\n接收微信请求:[openid=[{}], [signature=[{}], encType=[{}], msgSignature=[{}],"
+ " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
openid, signature, encType, msgSignature, timestamp, nonce, requestBody);

if (!wxMpService.checkSignature(timestamp, nonce, signature)) {
throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
}

String out = null;
if (encType == null) {
// 明文传输的消息
WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody);
WxMpXmlOutMessage outMessage = this.wxMpMessageRouter.route(inMessage);
if (outMessage == null) {
return "";
}
out = outMessage.toXml();
} else if ("aes".equalsIgnoreCase(encType)) {
// aes加密的消息
WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, wxMpService.getWxMpConfigStorage(),
timestamp, nonce, msgSignature);
log.info("\n消息解密后内容为:\n[{}] ", inMessage.toString());
WxMpXmlOutMessage outMessage = this.wxMpMessageRouter.route(inMessage);
if (outMessage == null) {
return "";
}
out = outMessage.toEncryptedXml(wxMpService.getWxMpConfigStorage());
}
log.info("\n组装回复信息:[{}]", out);
return out;
}

三、参考文档

​1、公众号消息加解密​​​​2、接收普通消息​​​​3、接收事件消息​

四、代码实现

​https://gitee.com/huan1993/wechat-development​