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=[\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碼雲

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM