微信公众号关注/取消订阅事件一文详解

news2024/11/17 15:28:01

一、背景介绍

这周期的项目需求中需要做一个引导用户关注微信公众号的功能,但是引导用户关注的前提是需要实时获取当前用户是否已经关注微信公众号,如果光看官方文档还是对于一些小伙伴来说比较无从下手,所以我来分享以下我做的过程中遇到的问题以及解决思路。

二、思路&方案

站在巨人的肩膀上:

做之前先阅读了微信公众号官方文档:关注/取消关注事件 | 微信开放文档

前提条件:

  1. 公网域名或IP
  2. 开通80或443端口
  3. 微信公众号的AppId和AppSecret必须认证

流程:

  1. 编写一个验签的接口,这个接口的目的是让微信服务器调用这个接口进行服务校验
  2. 公众号后台进行服务器配置
  3. 关注/取消关注事件接口。当用户对公众号进行关注或取消关注的时候会把这个事件推送到开发者在后台配置的url中,把消息返回给用户进行自定义的使用

三、过程

第一步、编写服务端与微信端建立连接的接口(GET请求)

思想:开发者提交信息后微信服务器会发功一个GET请求到你配置的这个接口,微信用你设置的token和你代码中的token进行对比,验证服务器的可用性

①、接收服务器请求

@Api("公众号")
@RestController
public class Controller {

    @Autowired
    private GzhService gzhService;

    private static final Logger log = LogManager.getLogger(Controller.class);
    private static final String TOKEN = "szbk";

    public Controller() {
    }

    @GetMapping({"/serverCheck"})
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException,
            NoSuchAlgorithmException {
        //接收微信服务器发送请求时传递过来的参数
        //signature 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
        //timestamp 时间戳
        //nonce 随机数
        //echostr 随机字符串
        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);

        //校验signature,timestamp,nonce,echostr这四个参数是否字符串为空或为null,是返回true,反之
        if (StringUtils.isAnyBlank(new CharSequence[]{signature, timestamp, nonce, echostr})) {
            throw new IllegalArgumentException("请求参数非法,请核实!");
        } else {
            String signatureCheck = getSHA1("szbk", timestamp, nonce);
            log.info("\n加密后的signatureCheck = {}", signatureCheck);
            if (signatureCheck.equals(signature)) {
                log.info("\n接入成功");
                PrintWriter out = response.getWriter();
                out.print(echostr);
                out.flush();
                out.close();
            }
        }
    }
}

②、SHA1加密

将token、timestamp、nonce三个参数拼接成一个字符串进行sha1加密,之后会将加密后的字符串和singnature进行对比,一样则标识请求时来自微信,否则接入失败

//SHA1加密
public static String getSHA1(String token, String timestamp, String nonce) {
        try {
            String[] array = new String[]{token, timestamp, nonce};
            StringBuffer sb = new StringBuffer();
            Arrays.sort(array);

            for (int i = 0; i < 3; ++i) {
                sb.append(array[i]);
            }

            String str = sb.toString();

            //获取加密对象
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            //获取字符串字节数组
            md.update(str.getBytes());
            //加密
            byte[] digest = md.digest();
            StringBuffer hexstr = new StringBuffer();
            String shaHex = "";

            //处理加密结果
            for (int i = 0; i < digest.length; ++i) {
                shaHex = Integer.toHexString(digest[i] & 255);
                if (shaHex.length() < 2) {
                    hexstr.append(0);
                }

                hexstr.append(shaHex);
            }

            return hexstr.toString();
        } catch (Exception var11) {
            var11.printStackTrace();
            return token;
        }
}

第二步、编写关注/取消关注事件接口(POST请求)

思想:微信会实时监控公众号用户的指定操作,然后通过这个接口通知(调用这个接口返回信息)给你(服务器)

@PostMapping("/checkToken")
public void checkTokenPost(HttpServletRequest request, HttpServletResponse response) {
        try {
            request.setCharacterEncoding("UTF-8");
            response.setCharacterEncoding("UTF-8");

            Map<String, String> requestMap = WxMessageUtil.parseXml(request);
            log.info("解析====>{}", request);
            String messageType = requestMap.get("MsgType");
            log.info("微信类型===>{}", messageType);
            String eventType = requestMap.get("Event");
            // 发送方帐号(open_id)
            String openid = requestMap.get("FromUserName");
            // 公众帐号
            String toUserName = requestMap.get("ToUserName");
            // 消息类型
            String msgType = requestMap.get("MsgType");

            if (messageType.equals(WxMessageType.EVENT.getCode())) {
                //判断消息类型是否是事件消息类型
                log.info("公众号====>事件消息");
                log.info("openid:" + openid);
                log.info("Event:" + eventType);
                if (eventType.equals(WxEeventType.SUBSCRIBE.getCode())) {
                    log.info("公众号====>新用户关注");
                    // 获取接口调用凭证
                    String accessTokenStr = "https://api.weixin.qq.com/cgi-bin/token?" +
                            "grant_type=client_credential" + "&appid=" + "wxXXXXXXXX" + "&secret=" + "f97XXXXXXXX";
                    String tokenStr = HttpUtil.get(accessTokenStr);
                    cn.hutool.json.JSONObject object = JSONUtil.parseObj(tokenStr);
                    String accessToken = object.get("access_token").toString();

                    // 构建获取用户的基本信息
                    String buffer = "https://api.weixin.qq.com/cgi-bin/user/info?" +
                            "access_token=" + accessToken +
                            "&openid=" + openid + "&lang=zh_CN";
                    String wxUserString = HttpUtil.get(buffer);
                    log.info("获取用户信息===>{}", wxUserString);
                    JSONObject jsonObject = JSONUtil.parseObj(wxUserString);
                } else if (eventType.equals(WxEeventType.UNSUBSCRIBE.getCode())) {
                    log.info("公众号====>用户取消关注");
                } else {
                    log.info("微信类型===>{}", messageType);
                    log.info("公众号===>其他");
                }
            } else if (messageType.equals(WxMessageType.TEXT.getCode())) {
                log.info("用户输入文本信息");
                // 响应消息
                PrintWriter out = response.getWriter();
                TextMessage textMessage = new TextMessage();
                textMessage.setFromUserName(openid);
                textMessage.setToUserName(toUserName);
                textMessage.setMsgType(msgType);
                textMessage.setCreateTime(System.currentTimeMillis());
                textMessage.setContent("欢迎您");
                String message = WxMessageUtil.textMessageToXml(textMessage);
                log.info("message==>{}",message);
                out.println(message);
                // 关闭流
                out.close();
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
}

注意:两个接口对外提供的接口名要保持一致!!!

第三步、微信公众平台接口调试工具测试

为了方便测试先使用了微信提供的【微信公众平台接口调试工具】,如果测试的没有问题了在进行公众号后台服务器的配置

 

 URL(服务器接口地址):确保接口能够调通,端口地址可以是80或443,我在做的过程中给接口服务原本配置的是6688接口,但是放到服务器上一直出现访问失败的原因,后来把服务器的80端口开通了就能访问通了

第四步、验证结果

  1. 把写的接口项目打成jar包放在服务器上
  2. 在公众号进行关注或取消关注操作
  3. 查看log文件

在log文件我们发现刚才对公众号的操作都被实时打印到了文件中,这样我们就可以根据这个事件完成对应的业务了

通过第四步的测试说明我们写的接口没有问题,此时我们就可以在公众号后台进行服务器配置了,当然如果你确保你写的接口没有问题也可以直接在公众号后台配置,省略生面测试步骤,下面是配置流程

第五步、填写公众号后台服务器配置

 URL(服务器接口地址):确保接口能够调通,端口地址可以是80或443,我在做的过程中给接口服务原本配置的是6688接口,但是放到服务器上一直出现访问失败的原因,后来把服务器的80端口开通了就能访问通了

Token(令牌):用户可以自定义设置,但是需要和上一步在接口中设置的token保持一致,否则验证也无法通过。(注意:这里的Token不是微信的Access_Token

EncodingAESKey(消息加密密钥):可以自定义生成

消息加密方式:为了测试方便我先选择了明文模式,但是如果你们实际项目中用为了保证安全性可以设置加密级别更好的模式

我们点击提交,如果显示成功则说明验证通过,微信就是实时监听微信公众号用户关注/取消关注的操作,并且将结果转发到我们刚才配置的服务器地址上

四、总结

在接触没有做过的事物之前站在巨人肩膀上(官方文档)可以帮助我们更快速了解,网上资料千千万,我们还要具备选择的能力,否则就是大海捞针。希望这篇文章能帮助到你!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/920587.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Ansible 创建分区

实例&#xff1a; 创建一个名为/home/greg/ansible/partition.yml 的剧本&#xff0c;它在所有托管节点上运行&#xff0c;执行以下操作&#xff1a; 在设备 VDB 上创建大小为 1500 MB 的单个主分区 1 使用 ext4 文件系统格式化分区 在/n…

无涯教程-PHP - 性能优化

根据Zend小组的说明,以下插图显示了PHP 7与PHP 5.6和基于流行的基于PHP的应用程序上的HHVM 3.7。 Magento 1.9 与执行Magento事务的PHP 5.6相比&#xff0c;PHP 7的运行速度证明是其两倍。 Drupal 7 在执行Drupal事务时&#xff0c;与PHP 5.6相比&#xff0c;PHP 7的运行速度…

【Gitee提交pr】

Gitee提交pr 什么是pr怎样提交一个pr嘞&#xff1f; 什么是pr pr:指的是将自己的修改从自己的账号仓库dev下提交到官方账号仓库master下&#xff1b; 通俗来讲就是Gitee线上有属于自己的分支&#xff0c;然后本地在自己地分支修改完代码之后&#xff0c;提交到自己的线上分支&a…

十一、hadoop应用

1.上传数据集 27.19.74.143,2015/3/30 17:38,/static/image/common/faq.gif 110.52.250.126,2015/3/30 17:38,/data/cache/style_1_widthauto.css?y7a 27.19.74.143,2015/3/30 17:38,/static/image/common/hot_1.gif 27.19.74.143,2015/3/30 17:38,/static/image/common/hot_2…

【ESP32】启动电流不足——调试问题记录

【ESP32】启动电流不足——调试问题记录 本文主要记录基于esp32 自开发设备硬件调试过程&#xff0c;解决供电不足的问题&#xff0c;用于新手小白记录 &#x1f4cb; 个人简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是喜欢记录零碎知识点的小菜鸟。&#x…

善于打仗的人,创造高势能,行动节奏恰当

善于打仗的人&#xff0c;创造高势能&#xff0c;行动节奏恰当 【安志强趣讲《孙子兵法》第18讲】 【原文】 激水之疾&#xff0c;至于漂石者&#xff0c;势也&#xff1b;鸷鸟之疾&#xff0c;至于毁折者&#xff0c;节也。 【注释】 激&#xff0c;阻截水流 节&#xff0c;时…

SensorService中Binder案例

SensorService中Binder案例 1、FWK实际操作在Native层2、Native层中代码实现Bn/Bp端2.1 代码实现Bn端2.2 代码实现Bp端2.2.1 模板interface_cast android12-release 1、FWK实际操作在Native层 SensorService.java实际操作Native层SensorService.cpp&#xff1b;对应Bn服务端。 …

人机界面通过RJ45口无线连接多台PLC

人机界面是系统和用户之间进行交互和信息交换的媒介&#xff0c;它实现信息的内部形式与人类可以接受形式之间的转换。人机界面产品由硬件和软件两部分组成&#xff0c;硬件部分包括处理器、显示单元、输入单元、通讯接口、数据存贮单元等&#xff0c;HMI软件一般分为两部分&am…

Ensp+Wireshark+VirtualBox+WinPcap

软件下载 [名称]&#xff1a;Ensp及辅助程序 [大小]&#xff1a;830.65MB [语言]&#xff1a;Chinese [安装环境]&#xff1a;Win7/Win8/Win10 [下载链接]&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1KbypgxAGQy07ijSAj3SvsQ 提取码&#xff1a;ly88 软件介…

秋招算法面经集合 | 华为、百度、腾讯、美团等

面试锦囊之面经分享系列&#xff0c;持续更新中 欢迎后台回复"面试"加入讨论组交流噢 写在前面 秋招告一段落&#xff0c;整理文件的时候发现之前记录的面经问题&#xff0c;主要是秋招前期的一些面试&#xff0c;后期由于实习比较忙没花时间整理。希望自己的面经可…

文件/文件夹加密:Newsoftwares Folder Lock 7.9.0 Crack

Newsoftwares Folder Lock 7.9文件夹锁 版本7 防弹数据加密 - 在几秒钟内锁定文件夹 - 即时加密文件 - 密码保护 USB/外部驱动器 - 粉碎并永久删除文件等等...... 视窗 向量 受到数百万人的信赖 82,283,016次下载并且还在增加中... 什么为什么如何 奖项常问问题特征丢失登记感…

BaiqiSoft MstHtmlEditor for .NET Crack

BaiqiSoft MstHtmlEditor for .NET Crack BaiqiSoft MstHtmlEditor获取.NET for win表单被认为是一个可以被用户轻松灵活地集成到C#、VB.NET甚至WPF软件中的元素。负责编辑的控制器&#xff0c;用于.NET Win Forms的MstHtmlEditor&#xff0c;允许用户和开发人员&#xff0c;甚…

专升本的开发历程

这个专升本项目是最近一个搞教育的朋友委托我的&#xff0c;从域名、服务器、设计开发、一直到项目上线都是我团队一手包办的。和大家浅聊一下&#xff1a; “专升本”小程序提供了报考查询、升本资讯、招生院校和考试报名等功能&#xff0c;为考生提供了更加全面和便捷的服务…

标签打印小工具 选择图片打印,按实际尺寸打印。可旋转图片

您可以尝试使用以下标签打印工具&#xff1a; 柯尼卡美能达标签打印机&#xff1a;功能齐全、易于使用的打印机&#xff0c;支持各种标签尺寸和类型。 赛门铁克标签打印机&#xff1a;高速打印、可靠性强的打印机&#xff0c;支持多种操作系统和软件。 齐柏林标签打印机&…

Code Lab - 2

pip install torch-scatter -f https://pytorch-geometric.com/whl/torch-1.10.2cu102.html pip install torch-sparse -f https://pytorch-geometric.com/whl/torch-1.10.2cu102.html pip install torch-geometric pip install ogb 1. PyG Datasets PyG有两个类&#xff0c;用…

资金借道股票ETF狂买1200亿

近日A股市场调整&#xff0c;资金借道股票ETF再现“越跌越买&#xff0c;大跌大买&#xff01;” 昨天的股市出现V字反转&#xff0c;资金借道股票ETF同样“抄底”汹涌&#xff0c;单日净买入资金超过50亿元&#xff0c;今年8月份以来已经狂买接近1200亿元。其中&#xff0c;沪…

ClickHouse进阶(一):ClickHouse 使用场景及分布式集群安装

进入正文前&#xff0c;感谢宝子们订阅专题、点赞、评论、收藏&#xff01;关注IT贫道&#xff0c;获取高质量博客内容&#xff01; &#x1f3e1;个人主页&#xff1a;含各种IT体系技术&#xff0c;IT贫道_Apache Doris,大数据OLAP体系技术栈,Kerberos安全认证-CSDN博客 &…

error while loading shared libraries错误的原因及解決方法——通用解决办法,错误加载xxxx共享库(.so)

在linux下整合log4cpp日志框架时&#xff0c;出现了下面的错误&#xff1a;当加载共享库时出错&#xff0c;找不到共享库 XXXX [roothecs-207177 cworkspace]# ./Log4cppTest ./Log4cppTest: error while loading shared libraries: liblog4cpp.so.5: cannot open shared obj…

svn下载

Download | VisualSVN for Visual Studio svn下载

MySQL数据库——函数-字符串函数、数值函数、日期函数、流程函数

目录 字符串函数 常用函数 练习 数值函数 常用函数 练习 日期函数 常用函数 练习 流程函数 常用函数 练习 函数 是指一段可以直接被另一段程序调用的程序或代码。 MySQL内置函数&#xff0c;主要分为四类&#xff1a; 字符串函数数值函数日期函数流程函数 字符串函…