zoukankan      html  css  js  c++  java
  • 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码云
  • 相关阅读:
    容器跨主机网络通信学习笔记(以Flannel为例)
    Kubernetes控制器Job和CronJob
    记一次使用Flannel插件排错历程
    Kubernetes控制器Deployment
    Kubernetes如何通过StatefulSet支持有状态应用?
    react18 来了,我 get 到...
    gojs 实用高级用法
    vuecli3 vue2 保留 webpack 支持 vite 成功实践
    calibre 报错 This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem. 解决
    unable to recognize "*.yaml": no matches for kind "RoleBinding" in version "rbac.authorization.k8s.io/v1beta1"
  • 原文地址:https://www.cnblogs.com/phpdragon/p/12193495.html
Copyright © 2011-2022 走看看