朱浩明 發表於 2019-12-1 17:03:00

微信公众号开发(一)

<p>上一篇文章大致解读了官方文档给出的开发概述,本文正式开始开发步骤的记录。</p>
<p>1. 为了配合微信请求只能使用域名的要求,可以使用natapp搭建外网服务器,模拟域名访问,详细的步骤可参考文章:搭建外网传送门。主要就是配置一个免费隧道,并下载对应的natapp插件,按照免费隧道中的authtoken,配置config.ini文件放在natapp根目录下,双击启动即可。</p>
<p><img src="https://img2018.cnblogs.com/i-beta/1449452/201911/1449452-20191122150209197-10500604.png"></p>
<p>&nbsp;</p>
<p>&nbsp;启动natapp见下列这样即说明配置成功,可通过域名访问</p>
<p><img src="https://img2018.cnblogs.com/i-beta/1449452/201911/1449452-20191122150629979-541463740.png"></p>
<p>域名设置成功就可以进行公众号开发了.</p>
<p>step1 引包</p>
<div class="cnblogs_code">
<pre>&lt;!--微信封装类--&gt;
&lt;dependency&gt;
    &lt;groupId&gt;com.github.binarywang&lt;/groupId&gt;
    &lt;artifactId&gt;weixin-java-mp&lt;/artifactId&gt;
    &lt;version&gt;3.2.0&lt;/version&gt;
&lt;/dependency&gt;
&lt;!--用于进行配置文件的注入--&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-configuration-processor&lt;/artifactId&gt;
    &lt;optional&gt;<span style="color: rgba(0, 0, 255, 1)">true</span>&lt;/optional&gt;
&lt;/dependency&gt;</pre>
</div>
<p>step2 微信相关配置信息的设置</p>
<div class="cnblogs_Highlighter">
<pre class="brush:java;gutter:true;">server:
port: 8803
# 测试公众号
hwy.wx.mp:
configs:
    appId: 你的公众号appid
    secret: 你的公众号appsecret
    token: 自定义设置一个token,会在公众号配置中使用,要保持一致
    aesKey: 公众号中的
    template1:
      alarm: 告警推送信息的模板id</pre>
</div>
<p>step3 代码开发</p>
<p>①&nbsp; 微信公众号相关配置信息注入到<span style="font-family: &quot;Courier New&quot;; font-size: 12px">WxMpProperties类中,支持多公众号的注入</span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:java;gutter:true;">package com.iris.wechat.config;

import lombok.Data;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.List;

/**
* 注入微信公众号的配置信息
*/
@Data
@ConfigurationProperties(prefix = "hwy.wx.mp")
public class WxMpProperties {
    private List&lt;MpConfig&gt; configs;

    @Data
    @ToString
    public static class MpConfig {
      /**
         * 设置微信公众号的appid
         */
      private String appId;

      /**
         * 设置微信公众号的app secret
         */
      private String secret;

      /**
         * 设置微信公众号的token
         */
      private String token;

      /**
         * 设置微信公众号的EncodingAESKey
         */
      private String aesKey;
    }
}
</pre>
</div>
<p>② 注入配置文件对象,公众号常见事件的路由层WxMpConfiguration</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">package</span><span style="color: rgba(0, 0, 0, 1)"> com.iris.wechat.config;

</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> com.google.common.collect.Maps;
</span><span style="color: rgba(0, 0, 255, 1)">import</span> com.iris.wechat.handler.*<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> me.chanjar.weixin.common.api.WxConsts.EventType;
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> me.chanjar.weixin.common.api.WxConsts.MenuButtonType;
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> me.chanjar.weixin.common.api.WxConsts.XmlMsgType;
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> me.chanjar.weixin.mp.api.WxMpMessageRouter;
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> me.chanjar.weixin.mp.api.WxMpService;
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> me.chanjar.weixin.mp.constant.WxMpEventConstants;
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> org.springframework.beans.factory.annotation.Autowired;
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> org.springframework.boot.context.properties.EnableConfigurationProperties;
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> org.springframework.context.annotation.Configuration;

</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> javax.annotation.PostConstruct;
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> java.util.Map;
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> java.util.stream.Collectors;

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* wechat mp configuration
*
* </span><span style="color: rgba(128, 128, 128, 1)">@author</span><span style="color: rgba(0, 128, 0, 1)"> Binary Wang(</span><span style="color: rgba(0, 128, 0, 1); text-decoration: underline">https://github.com/binarywang</span><span style="color: rgba(0, 128, 0, 1)">)
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Configuration
@EnableConfigurationProperties(WxMpProperties.</span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> WxMpConfiguration {
    </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> LogHandler logHandler;
    </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> NullHandler nullHandler;
    </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> KfSessionHandler kfSessionHandler;
    </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> StoreCheckNotifyHandler storeCheckNotifyHandler;
    </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> LocationHandler locationHandler;
    </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> MenuHandler menuHandler;
    </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> MsgHandler msgHandler;
    </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> UnsubscribeHandler unsubscribeHandler;
    </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> SubscribeHandler subscribeHandler;

    </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> WxMpProperties properties;

    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> Map&lt;String, WxMpMessageRouter&gt; routers =<span style="color: rgba(0, 0, 0, 1)"> Maps.newHashMap();
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> Map&lt;String, WxMpService&gt; mpServices =<span style="color: rgba(0, 0, 0, 1)"> Maps.newHashMap();

    @Autowired
    </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> WxMpConfiguration(LogHandler logHandler, NullHandler nullHandler, KfSessionHandler kfSessionHandler,
                           StoreCheckNotifyHandler storeCheckNotifyHandler, LocationHandler locationHandler,
                           MenuHandler menuHandler, MsgHandler msgHandler, UnsubscribeHandler unsubscribeHandler,
                           SubscribeHandler subscribeHandler, WxMpProperties properties) {
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.logHandler =<span style="color: rgba(0, 0, 0, 1)"> logHandler;
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.nullHandler =<span style="color: rgba(0, 0, 0, 1)"> nullHandler;
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.kfSessionHandler =<span style="color: rgba(0, 0, 0, 1)"> kfSessionHandler;
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.storeCheckNotifyHandler =<span style="color: rgba(0, 0, 0, 1)"> storeCheckNotifyHandler;
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.locationHandler =<span style="color: rgba(0, 0, 0, 1)"> locationHandler;
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.menuHandler =<span style="color: rgba(0, 0, 0, 1)"> menuHandler;
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.msgHandler =<span style="color: rgba(0, 0, 0, 1)"> msgHandler;
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.unsubscribeHandler =<span style="color: rgba(0, 0, 0, 1)"> unsubscribeHandler;
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.subscribeHandler =<span style="color: rgba(0, 0, 0, 1)"> subscribeHandler;
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.properties =<span style="color: rgba(0, 0, 0, 1)"> properties;
    }

    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> Map&lt;String, WxMpMessageRouter&gt;<span style="color: rgba(0, 0, 0, 1)"> getRouters() {
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> routers;
    }
   
    @PostConstruct
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> init() {
      services();
    }

    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> Map&lt;String, WxMpService&gt;<span style="color: rgba(0, 0, 0, 1)"> getMpServices() {
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> mpServices;
    }

    </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Object services() {
      mpServices </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.properties.getConfigs()
            .stream()
            .map(a </span>-&gt;<span style="color: rgba(0, 0, 0, 1)"> {
                WxMpInMemoryConfigStorage configStorage </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> WxMpInMemoryConfigStorage();
                configStorage.setAppId(a.getAppId());
                configStorage.setSecret(a.getSecret());
                configStorage.setToken(a.getToken());
                configStorage.setAesKey(a.getAesKey());
                WxMpService service </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> WxMpServiceImpl();
                service.setWxMpConfigStorage(configStorage);
                routers.put(a.getAppId(), </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.newRouter(service));
                </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> service;
            }).collect(Collectors.toMap(s </span>-&gt; s.getWxMpConfigStorage().getAppId(), a -&gt;<span style="color: rgba(0, 0, 0, 1)"> a));
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Boolean.TRUE;
    }

    </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> WxMpMessageRouter newRouter(WxMpService wxMpService) {
      </span><span style="color: rgba(0, 0, 255, 1)">final</span> WxMpMessageRouter newRouter = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> WxMpMessageRouter(wxMpService);

      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 记录所有事件的日志 (异步执行)</span>
      newRouter.rule().handler(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.logHandler).next();

      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 接收客服会话管理事件</span>
      newRouter.rule().async(<span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">).msgType(XmlMsgType.EVENT)
            .event(WxMpEventConstants.CustomerService.KF_CREATE_SESSION)
            .handler(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.kfSessionHandler).end();
      newRouter.rule().async(</span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">).msgType(XmlMsgType.EVENT)
            .event(WxMpEventConstants.CustomerService.KF_CLOSE_SESSION)
            .handler(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.kfSessionHandler)
            .end();
      newRouter.rule().async(</span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">).msgType(XmlMsgType.EVENT)
            .event(WxMpEventConstants.CustomerService.KF_SWITCH_SESSION)
            .handler(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.kfSessionHandler).end();

      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 门店审核事件</span>
      newRouter.rule().async(<span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">).msgType(XmlMsgType.EVENT)
            .event(WxMpEventConstants.POI_CHECK_NOTIFY)
            .handler(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.storeCheckNotifyHandler).end();

      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 自定义菜单事件</span>
      newRouter.rule().async(<span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">).msgType(XmlMsgType.EVENT)
            .event(MenuButtonType.CLICK).handler(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.menuHandler).end();

      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 点击菜单连接事件</span>
      newRouter.rule().async(<span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">).msgType(XmlMsgType.EVENT)
            .event(MenuButtonType.VIEW).handler(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.nullHandler).end();

      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 关注事件</span>
      newRouter.rule().async(<span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">).msgType(XmlMsgType.EVENT)
            .event(EventType.SUBSCRIBE).handler(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.subscribeHandler)
            .end();

      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 取消关注事件</span>
      newRouter.rule().async(<span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">).msgType(XmlMsgType.EVENT)
            .event(EventType.UNSUBSCRIBE)
            .handler(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.unsubscribeHandler).end();

      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 上报地理位置事件</span>
      newRouter.rule().async(<span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">).msgType(XmlMsgType.EVENT)
            .event(EventType.LOCATION).handler(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.locationHandler)
            .end();

      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 接收地理位置消息</span>
      newRouter.rule().async(<span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">).msgType(XmlMsgType.LOCATION)
            .handler(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.locationHandler).end();

      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 扫码事件</span>
      newRouter.rule().async(<span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">).msgType(XmlMsgType.EVENT)
            .event(EventType.SCAN).handler(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.nullHandler).end();

      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 默认</span>
      newRouter.rule().async(<span style="color: rgba(0, 0, 255, 1)">false</span>).handler(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.msgHandler).end();

      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> newRouter;
    }
}</span></pre>
</div>
<p>③ 根据WxMpConfiguration类中的handler完成各个handler的开发,根据自己的业务场景作相应的开发,</p>
<p><img src="https://img2018.cnblogs.com/i-beta/1449452/201911/1449452-20191126180236596-1152862553.png"></p>
<p>&nbsp;</p>
<p>④&nbsp; 完成验证接口的开发,分两个接口,接口的路径一致只是方法不同,一个get方法:用于用户界面上域名接口验证,一个post方法:用于关注,取关,自定义菜单等事件的触发。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:java;gutter:true;">package com.iris.wechat.controller;

import com.iris.wechat.config.WxMpConfiguration;
import com.iris.wechat.log.XlyLogger;
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.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;

/**
* 与微信交互的API文件
* @Date 2019-11-22 16:41:00
* @author muruan.lt
* 配合华为云公众号绑定
*/
@RestController
@RequestMapping("/wx/{appid}")
public class WechatController {

    private static Logger log = XlyLogger.get();

    // 接口配置信息调用接口(GET)
    @GetMapping(produces = "text/plain;charset=utf-8")
    public String doGet(@PathVariable String appid, HttpServletRequest request) {
      // 微信加密签名
      String signature = request.getParameter("signature");
      // 时间戳
      String timestamp = request.getParameter("timestamp");
      // 随机数
      String nonce = request.getParameter("nonce");
      // 随机字符串
      String echostr = request.getParameter("echostr");
      log.info("\n接收到来自微信服务器的认证消息:[{}, {}, {}, {}]", signature, timestamp, nonce, echostr);
      if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
            log.info("signature " + signature + "timestamp " + timestamp + " nonce " + nonce
                  + " echostr " + echostr);
            throw new IllegalArgumentException("请求参数非法,请核实!");
      }

      final WxMpService wxService = WxMpConfiguration.getMpServices().get(appid);
      if (wxService == null) {
            throw new IllegalArgumentException(String.format("未找到对应appid=[%d]的配置,请核实!", appid));
      }

      // 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
      if (wxService.checkSignature(timestamp, nonce, signature)) {
            log.info("weixin get success...."+echostr);
            return echostr;
      }else {
            log.error("weixin get failed....");
            return "weixin get failed....";
      }
    }

    // 关注,取关,客服,菜单等调用接口(POST)
    @PostMapping(produces = "application/xml; charset=UTF-8")
    public String doPost(HttpServletRequest request, @PathVariable String appid, @RequestBody String requestBody) {
      log.debug("weixin login get...");
      // 获取微信公众号传输过来的code,通过code可获取access_token,进而获取用户信息
      String code = request.getParameter("code");
      // 微信加密签名
      String signature = request.getParameter("signature");
      // 时间戳
      String timestamp = request.getParameter("timestamp");
      // 随机数
      String nonce = request.getParameter("nonce");
      // openid
      String openid = request.getParameter("openid");
      // encType--可空
      String encType = request.getParameter("encType");
      // msgSignature--可空
      String msgSignature = request.getParameter("msgSignature");

      final WxMpService wxService = WxMpConfiguration.getMpServices().get(appid);
      if (!wxService.checkSignature(timestamp, nonce, signature)) {
            log.info(
                  "\n接收微信请求:, , encType=[{}], msgSignature=[{}],"
                            + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
                  openid, signature, encType, msgSignature, timestamp, nonce, requestBody);
            throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
      }

      String out = null;
      if (StringUtils.isBlank(encType)) {
            // 明文传输的消息
            WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody);
            WxMpXmlOutMessage outMessage = this.route(inMessage, appid);
            if (outMessage == null) {
                return "";
            }
            out = outMessage.toXml();
      } else if ("aes".equalsIgnoreCase(encType)) {
            // aes加密的消息
            WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody,
                  wxService.getWxMpConfigStorage(), timestamp, nonce, msgSignature);
            log.debug("\n消息解密后内容为:\n{} ", inMessage.toString());
            WxMpXmlOutMessage outMessage = this.route(inMessage, appid);
            if (outMessage == null) {
                return "";
            }
            out = outMessage.toEncryptedXml(wxService.getWxMpConfigStorage());
      }

      log.debug("\n组装回复信息:{}", out);
      return out;
    }

    private WxMpXmlOutMessage route(WxMpXmlMessage message, String appid) {
      try {
            return WxMpConfiguration.getRouters().get(appid).route(message);
      } catch (Exception e) {
            log.error("路由消息时出现异常!", e);
      }
      return null;
    }

}</pre>
</div>
<p>其中doGet方法用于接口配置信息的调用接口,doPost方法用于关注,取关等事件的触发。</p>
<p>开发阶段若服务器,域名已经准备好,则可将代码打包上线,进行下一步开发调试,由于上线的代码并不是很方便调试故而可使用上文中natapp产生的域名代替,进行本地调试,详细配置如下,</p>
<p>①&nbsp;&nbsp;接口配置信息修改:URL是验证接口,Token是自己定义的,务必与服务器配置文件中的token一致</p>
<p>登陆公众号测试账号,找到如下位置,</p>
<p><img src="https://img2018.cnblogs.com/i-beta/1449452/201912/1449452-20191201163304556-456692236.png"></p>
<p>&nbsp;</p>
<p>将上图中appID,appsecret信息添加到配置文件中,并启动服务。</p>
<p>&nbsp;<img src="https://img2018.cnblogs.com/i-beta/1449452/201912/1449452-20191201163747011-966444276.png"></p>
<p>&nbsp;</p>
<p>&nbsp;点击“修改“,将"接口配置信息"接口填在URL处,将上图中的token填在下图的Token处。其中URL的格式为natapp生成的外网访问地址+/wx/{appid},如下图所示</p>
<p>&nbsp;<img src="https://img2018.cnblogs.com/i-beta/1449452/201912/1449452-20191201164440040-2045848509.png"></p>
<p>&nbsp;</p>
<p>&nbsp;点击提交,在本地如下方法中打断点,发现点击提交会进入下面方法,若没有进入则说明配置URL,token,appId,secret等出现错误,若进入该方法则说明配置没有问题。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:java;gutter:true;">// 接口配置信息调用接口(GET)
    @GetMapping(produces = "text/plain;charset=utf-8")
    public String doGet(@PathVariable String appid, HttpServletRequest request) {
      // 微信加密签名
      String signature = request.getParameter("signature");
      // 时间戳
      String timestamp = request.getParameter("timestamp");
      // 随机数
      String nonce = request.getParameter("nonce");
      // 随机字符串
      String echostr = request.getParameter("echostr");
      log.info("\n接收到来自微信服务器的认证消息:[{}, {}, {}, {}]", signature, timestamp, nonce, echostr);
      if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
            log.info("signature " + signature + "timestamp " + timestamp + " nonce " + nonce
                  + " echostr " + echostr);
            throw new IllegalArgumentException("请求参数非法,请核实!");
      }

      final WxMpService wxService = WxMpConfiguration.getMpServices().get(appid);
      if (wxService == null) {
            throw new IllegalArgumentException(String.format("未找到对应appid=[%d]的配置,请核实!", appid));
      }

      // 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
      if (wxService.checkSignature(timestamp, nonce, signature)) {
            log.info("weixin get success...."+echostr);
            return echostr;
      }else {
            log.error("weixin get failed....");
            return "weixin get failed....";
      }
    }</pre>
</div>
<p>&nbsp;②&nbsp; 配置JS接口安全域名:点击修改将外网访问地址填好提交就可以了。</p>
<p><img src="https://img2018.cnblogs.com/i-beta/1449452/201912/1449452-20191201165434985-366935260.png"></p>
<p>&nbsp;</p>
<p>&nbsp;③ 检查关注公众号,取消关注是否生效</p>
<p><img src="https://img2018.cnblogs.com/i-beta/1449452/201912/1449452-20191201165757103-369191583.png"></p>
<p>&nbsp;</p>
<p>&nbsp;首先在下面方法上打断点,再扫码关注,会进入该方法,同样操作取关也会进入该方法,再需要自己根据实际需求进行业务代码的开发。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:java;gutter:true;">// 关注,取关,客服,菜单等调用接口(POST)
    @PostMapping(produces = "application/xml; charset=UTF-8")
    public String doPost(HttpServletRequest request, @PathVariable String appid, @RequestBody String requestBody) {
      log.debug("weixin login get...");
      // 获取微信公众号传输过来的code,通过code可获取access_token,进而获取用户信息
      String code = request.getParameter("code");
      // 微信加密签名
      String signature = request.getParameter("signature");
      // 时间戳
      String timestamp = request.getParameter("timestamp");
      // 随机数
      String nonce = request.getParameter("nonce");
      // openid
      String openid = request.getParameter("openid");
      // encType--可空
      String encType = request.getParameter("encType");
      // msgSignature--可空
      String msgSignature = request.getParameter("msgSignature");

      final WxMpService wxService = WxMpConfiguration.getMpServices().get(appid);
      if (!wxService.checkSignature(timestamp, nonce, signature)) {
            log.info(
                  "\n接收微信请求:, , encType=[{}], msgSignature=[{}],"
                            + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
                  openid, signature, encType, msgSignature, timestamp, nonce, requestBody);
            throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
      }

      String out = null;
      if (StringUtils.isBlank(encType)) {
            // 明文传输的消息
            WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody);
            WxMpXmlOutMessage outMessage = this.route(inMessage, appid);
            if (outMessage == null) {
                return "";
            }
            out = outMessage.toXml();
      } else if ("aes".equalsIgnoreCase(encType)) {
            // aes加密的消息
            WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody,
                  wxService.getWxMpConfigStorage(), timestamp, nonce, msgSignature);
            log.debug("\n消息解密后内容为:\n{} ", inMessage.toString());
            WxMpXmlOutMessage outMessage = this.route(inMessage, appid);
            if (outMessage == null) {
                return "";
            }
            out = outMessage.toEncryptedXml(wxService.getWxMpConfigStorage());
      }

      log.debug("\n组装回复信息:{}", out);
      return out;
    }
</pre>
</div>
<p>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/Iris1998/p/11926448.html
頁: [1]
查看完整版本: 微信公众号开发(一)