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

时间:2020-03-24 13:18:21   收藏:0   阅读:104

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=[\n{}\n] ",
                signature, encryptType, msgSignature, timestamp, nonce, requestBody);

        String outMsg = "";
        try {
            outMsg = this.handleMsg(appId, encryptType, signature, timestamp, nonce, msgSignature, requestBody);
            log.info("返回响应消息,outMsg:\n{}", 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("\n接收到事件请求,内容:【{}】", 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码云

 

评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!