Java SpringBoot 集成微信公众号

2023-05-16

微信公众号

  • 申请公众号
  • 引入依赖
  • yml配置
  • 读取配置文件类WxMaProperties
  • 配置文件加载
  • json返回工具类
  • 控制层
  • 测试号配置
  • 以上就完成了哦,简单教程
  • Gitee开源地址

申请公众号

注册公众测试号点我

引入依赖

<!-- 主要依赖 -->
<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哦

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java SpringBoot 集成微信公众号 的相关文章

随机推荐

  • 浙大PTA平台上的题目题解

    记载一些题目的代码 xff0c 之后想要在b站讲题目 xff0c 到时候会把录的视频上传b站 不是大佬 xff0c 是蒟蒻 xff0c 大佬勿喷 xff0c 仅供参考 xff0c 欢迎大家star xff0c qwq 浙大版 C语言程序设计
  • git 入门教程

    想要将一个项目托管到github xff0c 需要进入项目所在文件夹进行git init命令初始化 Git提交代码的基本流程 xff1a 创建或修改 本地文件使用 git add 命令 xff0c 将创建或修改的文件添加到本地的暂存区 xf
  • 博客搬家

    谢谢大家对我的blog的支持 现在本科毕业 xff0c 准备读研 方向大概是机器学习这一块 又是一个新的开始 我想将我读研学习Python以及机器学习 深度学习 以及数据分析处理 大数据等学习教程放在新的blog上 xff1a blog 欢
  • Python多线程笔记(Python_MultiThread)

    4 MultiThreading 多线程 使用 xff1a a 什么是多线程 xff1f 简单明了 xff0c 让计算机在同一时间内同时运行多个程序 xff0c 并且每个程序的计算互不干扰 xff0c 我们称这样的操作为多线程运算 b ad
  • Python多进程笔记(Python_MultiProcess)

    1 MutiProcessing 多进程 使用 xff1a a 什么是多进程 xff1f 在上面我们使用多线程去分别处理不同的事情 xff0c 看起来 xff0c 多线程处理并不比单线程循环处理的效率看起来那么的高 多进程是在利用我们电脑C
  • Python自带的GUI(Tkinter)教程

    1 Python Tkinter xff08 GUI图形界面 xff09 xff1a a What s Tkinter Tkinter 是什么 xff1f Tkinter是Python自带的一个GUI库 xff0c 他可以将我们在comma
  • Python科学计算包NumPy教程

    在我的Github上有一份代码与教程结合的jupyter Notebook文件 xff0c 大家可以clone下来看一看 下面会用实例的方式给出一些examples xff1a Tutorial教程 官方中文文档 span class to
  • 区块链入门的几个概念

    区块链入门的几个简单概念 1 What s Block Chain 区块链是一门软件技术 xff0c 从本质上来看就像是一个分布式的DataBase xff0c 是一个去中心化 xff0c 分布式技术 由于是分布式的 xff0c 所以区块链
  • Mysql GROUP_CONCAT与CONCAT_WS配合使用单选、多选拼接

    举例1 可以使用IF函数将单选和多选的值分别拼接 xff0c 并在最后的结果中使用CONCAT WS函数将它们合并 xff1a idcolors11223344 551 2 362 3 4 5 我们想要的结果为1 2 3 4 5 下面开始测
  • A Survey on Concept Drift Adaptation Note

    A Survey on Concept Drift Adaptation Abstract Concept drift primarily refers to an online supervised learning scenario w
  • Learning under Concept Drift:A Review

    Learning under Concept Drift A Review Abstract Concept drift describes unforeseeable changes in the underlying distribut
  • Note for Understanding Neural Networks Through Deep Visualization

    Note for Understanding Neural Networks Through Deep Visualization Abstract 近年来 xff0c 在训练大型深度神经网络方面取得了巨大进展 xff0c 其中包括训练卷积
  • ML-Leaks Note

    ML Leaks Model and Data IndependentMembership Inference Attacks and Defenses onMachine Learning Models xff08 机器学习模型上与模型和
  • C++中读取字符串的方式

    这里写自定义目录标题 C 43 43 读取字符串的两种方式1 getline 读取行的输入2 get 读取行的输入 C 43 43 读取字符串的两种方式 1 getline 读取行的输入 getline函数读取整行 xff0c 它使用通过回
  • 二叉树的基本操作

    二叉树 先序和中序确定二叉树 后序以及中序确定二叉树 span class token comment 指针版本 span span class token keyword struct span node span class token
  • 2023秋招面试准备

    2022 秋招资料 算法数据结构 螺旋矩阵问题 螺旋矩阵I 将 1 n m 个数按蛇形方向填入数组中 记录蛇形矩阵偏移量方法 xff0c 四个不同方向右下左上 xff0c 判断什么情况下矩阵遍历完 xff0c 1 要么出界 2 要么走完 s
  • KingbaseES V8R6 维护管理案例之---Kstudio在CentOS 7启动故障

    案例说明 xff1a 在CentOS 7上安装KingbaseES V8R6C006数据库后 xff0c 启动Kstudio图形界面启动失败 xff0c gtk动态库加载失败 xff0c 安装gtk相关动态库后 xff0c 问题解决 适用版
  • python 发送post请求

    背景 浏览器在访问网页时会发送很多http请求 xff08 request xff09 xff0c 服务器返回响应 response xff0c 浏览器拿到响应数据后渲染出来 xff0c 当然我们可以尝试使用python模拟浏览器发送出这些
  • iOS 如何在Label中显示html的文本

    if self messageModel NSString htmlString 61 self messageModel contentText NSAttributedString attrStr1 61 NSAttributedStr
  • Java SpringBoot 集成微信公众号

    微信公众号 申请公众号引入依赖yml配置读取配置文件类WxMaProperties配置文件加载json返回工具类控制层测试号配置以上就完成了哦 xff0c 简单教程Gitee开源地址 申请公众号 注册公众测试号点我 引入依赖 span cl