申请公众号
注册公众测试号点我
https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index
引入依赖
<!-- 主要依赖 --> <dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-mp</artifactId> <version>4.4.0</version> </dependency>
yml配置
wx: mp: useRedis: false redisConfig: host: 127.0.0.1 port: 6379 configs: - appId: 1111 # 第一个公众号的appid secret: 11 # 公众号的appsecret token: 111 # 接口配置里的Token值 aesKey: 111 # 接口配置里的EncodingAESKey值 - appId: 2222 # 第二个公众号的appid,以下同上 secret: 1111 token: 111 aesKey: 111
读取配置文件类WxMaProperties
/** * wechat mp properties * * Astar(一颗星) */ @Data @ConfigurationProperties(prefix = "wx.mp") public class WxMpProperties { /** * 是否使用redis存储access token */ private boolean useRedis; /** * redis 配置 */ private RedisConfig redisConfig; @Data public static class RedisConfig { /** * redis服务器 主机地址 */ private String host; /** * redis服务器 端口号 */ private Integer port; /** * redis服务器 密码 */ private String password; /** * redis 服务连接超时时间 */ private Integer timeout; } /** * 多个公众号配置信息 */ private List<MpConfig> configs; @Data public static class MpConfig { /** * 设置微信公众号的appid */ private String appId; /** * 设置微信公众号的app secret */ private String secret; /** * 设置微信公众号的token */ private String token; /** * 设置微信公众号的EncodingAESKey */ private String aesKey; } @Override public String toString() { return JsonUtils.toJson(this); } }
配置文件加载
/** * wechat mp configuration * * Astar(一颗星) */ @AllArgsConstructor @Configuration @EnableConfigurationProperties(WxMpProperties.class) public class WxMpConfiguration { private final LogHandler logHandler; private final NullHandler nullHandler; private final KfSessionHandler kfSessionHandler; private final StoreCheckNotifyHandler storeCheckNotifyHandler; private final LocationHandler locationHandler; private final MenuHandler menuHandler; private final MsgHandler msgHandler; private final UnsubscribeHandler unsubscribeHandler; private final SubscribeHandler subscribeHandler; private final ScanHandler scanHandler; private final WxMpProperties properties; @Bean public WxMpService wxMpService() { // 代码里 getConfigs()处报错的同学,请注意仔细阅读项目说明,你的IDE需要引入lombok插件!!!! final List<WxMpProperties.MpConfig> configs = this.properties.getConfigs(); if (configs == null) { throw new RuntimeException("大哥,拜托先看下项目首页的说明(readme文件),添加下相关配置,注意别配错了!"); } WxMpService service = new WxMpServiceImpl(); service.setMultiConfigStorages(configs .stream().map(a -> { WxMpDefaultConfigImpl configStorage; if (this.properties.isUseRedis()) { final WxMpProperties.RedisConfig redisConfig = this.properties.getRedisConfig(); JedisPoolConfig poolConfig = new JedisPoolConfig(); JedisPool jedisPool = new JedisPool(poolConfig, redisConfig.getHost(), redisConfig.getPort(), redisConfig.getTimeout(), redisConfig.getPassword()); configStorage = new WxMpRedisConfigImpl(new JedisWxRedisOps(jedisPool), a.getAppId()); } else { configStorage = new WxMpDefaultConfigImpl(); } configStorage.setAppId(a.getAppId()); configStorage.setSecret(a.getSecret()); configStorage.setToken(a.getToken()); configStorage.setAesKey(a.getAesKey()); return configStorage; }).collect(Collectors.toMap(WxMpDefaultConfigImpl::getAppId, a -> a, (o, n) -> o))); return service; } @Bean public WxMpMessageRouter messageRouter(WxMpService wxMpService) { final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService); // 记录所有事件的日志 (异步执行) newRouter.rule().handler(this.logHandler).next(); // 接收客服会话管理事件 newRouter.rule().async(false).msgType(EVENT).event(KF_CREATE_SESSION) .handler(this.kfSessionHandler).end(); newRouter.rule().async(false).msgType(EVENT).event(KF_CLOSE_SESSION) .handler(this.kfSessionHandler).end(); newRouter.rule().async(false).msgType(EVENT).event(KF_SWITCH_SESSION) .handler(this.kfSessionHandler).end(); // 门店审核事件 newRouter.rule().async(false).msgType(EVENT).event(POI_CHECK_NOTIFY).handler(this.storeCheckNotifyHandler).end(); // 自定义菜单事件 newRouter.rule().async(false).msgType(EVENT).event(EventType.CLICK).handler(this.menuHandler).end(); // 点击菜单连接事件 newRouter.rule().async(false).msgType(EVENT).event(EventType.VIEW).handler(this.nullHandler).end(); // 关注事件 newRouter.rule().async(false).msgType(EVENT).event(SUBSCRIBE).handler(this.subscribeHandler).end(); // 取消关注事件 newRouter.rule().async(false).msgType(EVENT).event(UNSUBSCRIBE).handler(this.unsubscribeHandler).end(); // 上报地理位置事件 newRouter.rule().async(false).msgType(EVENT).event(EventType.LOCATION).handler(this.locationHandler).end(); // 接收地理位置消息 newRouter.rule().async(false).msgType(XmlMsgType.LOCATION).handler(this.locationHandler).end(); // 扫码事件 newRouter.rule().async(false).msgType(EVENT).event(EventType.SCAN).handler(this.scanHandler).end(); // 默认 newRouter.rule().async(false).handler(this.msgHandler).end(); return newRouter; } }
json返回工具类
public class JsonUtils { public static String toJson(Object obj) { Gson gson = new GsonBuilder() .setPrettyPrinting() .disableHtmlEscaping() .create(); return gson.toJson(obj); } }
控制层
主要控制层,认证信息
@Slf4j @AllArgsConstructor @RestController @RequestMapping("/wx/portal/{appid}") public class WxPortalController { private final WxMpService wxService; private final WxMpMessageRouter messageRouter; @GetMapping(produces = "text/plain;charset=utf-8") public String authGet(@PathVariable 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("\n接收到来自微信服务器的认证消息:[{}, {}, {}, {}]", signature, timestamp, nonce, echostr); if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) { throw new IllegalArgumentException("请求参数非法,请核实!"); } if (!this.wxService.switchover(appid)) { throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid)); } if (wxService.checkSignature(timestamp, nonce, signature)) { return echostr; } return "非法请求"; } @PostMapping(produces = "application/xml; charset=UTF-8") public String post(@PathVariable String appid, @RequestBody String requestBody, @RequestParam("signature") String signature, @RequestParam("timestamp") String timestamp, @RequestParam("nonce") String nonce, @RequestParam("openid") String openid, @RequestParam(name = "encrypt_type", required = false) String encType, @RequestParam(name = "msg_signature", required = false) String msgSignature) { log.info("\n接收微信请求:[openid=[{}], [signature=[{}], encType=[{}], msgSignature=[{}]," + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ", openid, signature, encType, msgSignature, timestamp, nonce, requestBody); if (!this.wxService.switchover(appid)) { throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid)); } if (!wxService.checkSignature(timestamp, nonce, signature)) { throw new IllegalArgumentException("非法请求,可能属于伪造的请求!"); } String out = null; if (encType == null) { // 明文传输的消息 WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody); WxMpXmlOutMessage outMessage = this.route(inMessage); 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); if (outMessage == null) { return ""; } out = outMessage.toEncryptedXml(wxService.getWxMpConfigStorage()); } log.debug("\n组装回复信息:{}", out); return out; } private WxMpXmlOutMessage route(WxMpXmlMessage message) { try { return this.messageRouter.route(message); } catch (Exception e) { log.error("路由消息时出现异常!", e); } return null; } }
测试号配置
配置微信公众号中的接口地址:http://公网可访问域名/wx/portal/xxxxx (注意,xxxxx为对应公众号的appid值)
以上就完成了哦,简单教程
Gitee开源地址
点我下载项目https://gitee.com/wy521a/astar-weixin-mp
记得点个star哦