• SpringCloud : 接入 微信公众号平台(一)、接入微信请求(支持多公众号)


    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_消息的加解密 ,更多使用技巧

    消息路由: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:

    公众号开发文档wiki

    Java开发微信公众号之整合weixin-java-tools框架开发微信公众号

    从零实现 Spring Boot 2.0 整合 weixin-java-mp(weixin-java-tools) 获取 openId,用于微信授权

    Demo 列表

    1. 微信支付 Demo:GitHub码云
    2. 企业号/企业微信 Demo:GitHub码云
    3. 微信小程序 Demo:GitHub码云
    4. 开放平台 Demo:GitHub码云
    5. 公众号 Demo:
      • 使用 Spring MVC 实现的公众号 Demo:GitHub码云
      • 使用 Spring Boot 实现的公众号 Demo(支持多公众号):GitHub码云
      • 含公众号和部分微信支付代码的 Demo:GitHub码云
  • 相关阅读:
    关于Linux测试题
    Linux常用命令按功能统一总结
    关于Eclipse的Save时的自定义操作
    关于产品版本英语缩写
    关于location.href几种用法的区别
    关于Java多态的总结.
    关于JDK中正则表达式
    关于JDK中的集合总结(三)
    关于JDK中的集合总结(二)
    关于JDK中的集合总结(一)
  • 原文地址:https://www.cnblogs.com/phpdragon/p/12193495.html
Copyright © 2020-2023  润新知