Maven:
Feign 版本10.1.0
Spring 版本 5.1.5.RELEASE
SpringBoot 版本 2.1.5.RELEASE
SpringCloud 版本 2.1.1.RELEASE
Weixin-java 版本 3.7.0,链接
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.60</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-mp</artifactId> <version>3.7.0</version> </dependency>
公共网关接口: https://你的域名/gate/微信公众号appId
其中 validAuth 方法负责校验签名,微信服务器会发送一个 普通的GET请求,命中该方法处理。
msgForward 方法负责消息的转发,微信服务器会发送一个xml协议的POST请求,命中该方法。
消息路由:MP_微信消息路由器。
源码参考:WxPortalController.java 。
import com.phpdragon.wechat.proxy.config.WeChatConfig; import com.phpdragon.wechat.proxy.config.WechatEventConfig; import com.phpdragon.wechat.proxy.handler.EventHandler; import com.phpdragon.wechat.proxy.handler.LogHandler; import com.phpdragon.wechat.proxy.handler.MsgHandler; import com.phpdragon.wechat.proxy.handler.TplMsgFeedbackHandler; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.mp.api.WxMpMessageRouter; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; import me.chanjar.weixin.mp.util.crypto.WxMpCryptUtil; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.lang.Nullable; import org.springframework.web.bind.annotation.*; import java.util.Objects; /** * 参考:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html */ @Slf4j @RestController @RequestMapping("/gate/") public class GateController { @Autowired private WeChatConfig weChatConfig; @Autowired private LogHandler logHandler; @Autowired private EventHandler eventHandler; @Autowired private TplMsgFeedbackHandler tplMsgFeedbackHandler; @Autowired private MsgHandler msgHandler; /** * 微信验签 * * @param signature * @param timestamp * @param nonce * @param echostr * @return */ @ResponseBody @GetMapping(value = "/{app_id}", produces = "text/plain;charset=utf-8") public String validAuth(@PathVariable("app_id") String appId, @RequestParam(name = "signature", required = false) String signature, @RequestParam(name = "timestamp", required = false) String timestamp, @RequestParam(name = "nonce", required = false) String nonce, @RequestParam(name = "echostr", required = false) String echostr) { log.info("接收到来自微信服务器的认证请求:[appId:{},{}, {}, {}, {}]", appId, signature, timestamp, nonce, echostr); if (StringUtils.isAnyBlank(appId, signature, timestamp, nonce, echostr)) { throw new IllegalArgumentException("请求参数非法!"); } WxMpService wxMpService = weChatConfig.getWxMpService(appId); if (wxMpService.checkSignature(timestamp, nonce, signature)) { return echostr; } return "请求非法"; } /** * 消息转发---中转站 * 每次微信端的消息都会来到这里进行分发 * 对微信公众号相关的一些动作,都以报文形式推送到该接口, * 根据请求的类型,进行路由分发处理 */ @PostMapping(value = "/{app_id}", produces = "application/xml; charset=UTF-8") public String msgForward(@PathVariable("app_id") String appId, @RequestParam(name = "encrypt_type", required = false) String encryptType, @RequestParam(name = "signature", required = false) String signature, @RequestParam(name = "timestamp", required = false) String timestamp, @RequestParam(name = "nonce", required = false) String nonce, @RequestParam(name = "msg_signature", required = false) String msgSignature, @RequestBody String requestBody) { log.info("接收微信服务器请求:[signature=[{}], encType=[{}], msgSignature=[{}]," + " timestamp=[{}], nonce=[{}], requestBody=[ {} ] ", signature, encryptType, msgSignature, timestamp, nonce, requestBody); String outMsg = ""; try { outMsg = this.handleMsg(appId, encryptType, signature, timestamp, nonce, msgSignature, requestBody); log.info("返回响应消息,outMsg: {}", outMsg); } catch (Exception e) { log.error("响应请求异常,error:{},{}", e.getMessage(), e); } return outMsg; } /** * 处理消息的入口 * * @param appId * @param signature * @param encryptType * @param msgSignature * @param timestamp * @param nonce * @param requestBody * @return */ @Nullable private String handleMsg(String appId, String encryptType, String signature, String timestamp, String nonce, String msgSignature, String requestBody) { WxMpService wxMpService = weChatConfig.getWxMpService(appId); if ("aes".equals(encryptType)) { if (!wxMpService.checkSignature(timestamp, nonce, signature)) { throw new IllegalArgumentException("非法请求,可能属于伪造的请求!"); } } boolean isEncrypt = "aes".equals(encryptType); WxMpXmlMessage inMessage; if (isEncrypt) { inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, wxMpService.getWxMpConfigStorage(), timestamp, nonce, msgSignature); } else { inMessage = WxMpXmlMessage.fromXml(requestBody); } WxMpXmlOutMessage outMessage = this.buildMsgRouter(wxMpService).route(inMessage); if (Objects.isNull(outMessage)) { return ""; } String outMsg = outMessage.toXml(); if (isEncrypt) { WxMpCryptUtil cryptUtil = new WxMpCryptUtil(wxMpService.getWxMpConfigStorage()); outMsg = cryptUtil.encrypt(outMsg); } return outMsg; } /** * 构造消息路由处理器 * * @param wxMpService */ private WxMpMessageRouter buildMsgRouter(final WxMpService wxMpService) { final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService); // 记录所有事件的日志 newRouter.rule().handler(this.logHandler).next(); // 处理事件请求 for (String eventKey : WechatEventConfig.ALL_EVENTS) { newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) .event(eventKey).handler(this.eventHandler) .end(); } //处理模版消息的发送反馈 newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) .event(WxConsts.EventType.TEMPLATE_SEND_JOB_FINISH) .handler(this.tplMsgFeedbackHandler) .end(); // 默认,转发消息给客服人员 newRouter.rule().async(false).handler(this.msgHandler).end(); return newRouter; } }
LogHandler:
import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.mp.api.WxMpMessageHandler; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; import org.springframework.stereotype.Component; import java.util.Map; /** * 对所有接收到的消息输出日志,也可进行持久化处理 * <p> * Created by FirenzesEagle on 2016/7/27 0027. * Email:liumingbo2008@gmail.com */ @Slf4j @Component public class LogHandler implements WxMpMessageHandler { @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) { //TODO: 保存消息日志 //wXLogService.doSaveReceiveLog(inMessage); log.info(" 接收到事件请求,内容:【{}】", wxMessage.toString()); return null; } }
TplMsgFeedbackHandler:
import com.phpdragon.wechat.proxy.logic.MsgTplLogic; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.mp.api.WxMpMessageHandler; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.Map; /** * 处理事件 */ @Slf4j @Component public class TplMsgFeedbackHandler implements WxMpMessageHandler { @Autowired private MsgTplLogic msgTplLogic; @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> map, WxMpService wxMpService, WxSessionManager wxSessionManager) throws WxErrorException { msgTplLogic.updateLog(wxMessage); return null; } }
MsgHandler:
import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.mp.api.WxMpMessageHandler; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; import org.springframework.stereotype.Component; import java.util.Map; /** * 处理客户消息 */ @Slf4j @Component public class MsgHandler implements WxMpMessageHandler { @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) { return WxMpXmlOutMessage.TEXT().fromUser(wxMessage.getToUser()) .toUser(wxMessage.getFromUser()).content("您好,正在处理您的请求").build(); //转发给客服 // return WxMpXmlOutMessage // .TRANSFER_CUSTOMER_SERVICE().fromUser(wxMessage.getToUser()) // .toUser(wxMessage.getFromUser()).build(); } }
EventHandler:
import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.mp.api.WxMpMessageHandler; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; import org.springframework.stereotype.Component; import java.util.Map; /** * 处理事件 */ @Slf4j @Component public class EventHandler implements WxMpMessageHandler { @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) { return WxMpXmlOutMessage.TEXT().fromUser(wxMessage.getToUser()) .toUser(wxMessage.getFromUser()).content("您好,正在处理您的请求").build(); } }
WeChatConfig:
import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import redis.clients.jedis.JedisPool; @Configuration public class WeChatConfig { //TODO: 你的域名 public static final String CURRENT_HOST = "你的域名"; @Autowired private JedisPool jedisPool; /** * 取mp SDK * * @param appId * @return */ public WxMpService getWxMpService(String appId) { WxMpRedisConfigImpl mpConfig = new WxMpRedisConfigImpl(jedisPool); //TODO: 用数据库进行保存 mpConfig.setAppId("微信ID"); mpConfig.setSecret("微信密钥"); mpConfig.setToken("微信通讯token"); mpConfig.setAesKey("加密密钥"); WxMpService wxMpService = new WxMpServiceImpl(); wxMpService.setWxMpConfigStorage(mpConfig); return wxMpService; } }
接入到此完毕,使用官方推荐工具调试一下看看是否可通,微信公众平台接口调试工具!。
PS:
Java开发微信公众号之整合weixin-java-tools框架开发微信公众号
从零实现 Spring Boot 2.0 整合 weixin-java-mp(weixin-java-tools) 获取 openId,用于微信授权