微信公众号(小程序)验证URL和事件推送

文章的内容适用小程序和公众号

微信官方示例代码demo下载地址:微信官方demo代码 必须下载,后面要用到

把微信官方示例代码也就是下图中除了  WXBizMsgCryptTest  文件外的所有文件复制到项目中

下面是全部代码,有问题请留言

代码中只提到了关注事件作为示例,也可以处理其他事件,在于你的逻辑处理

 其中的 token ,encodingAesKey,来自于微信公众号后台的消息推送服务器配置信息,详情请点击下方官方文档链接查看,这里就不叙述了

微信消息推送服务器配置官方文档说明

代码中请求路径是一样的,对应微信公众号后台配置的服务器URL

GET请求用来验证服务器的,POST请求用来处理所有事件的

Controller

import cn.hutool.core.util.StrUtil;
import com.mrc.backend.account.constant.RedisKeyConstant;
import com.mrc.willbe.saas.mall.backend.account.param.FollowEventParam;
import com.mrc.willbe.saas.mall.backend.account.service.PublicAccountService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

@Slf4j
@RestController
@RequestMapping("/opr/public/account")
public class PublicAccountController {

    public final static String PUSH_SUCCESS = "success";

    @Autowired
    private PublicAccountService publicAccountService;

    @Autowired
    private RedissonClient redissonClient;


    /**
     * 验证URL
     * @param map
     * @return
     * @throws Exception
     */
    @GetMapping("/msg")
    public String serviceCheck(@RequestParam Map<String, String> map) throws Exception {

        String signature = map.get("signature");
        String timestamp = map.get("timestamp");
        String nonce = map.get("nonce");
        String echostr = map.get("echostr");

        // 验证微信服务器URL
        if (StrUtil.isNotBlank(signature) && StrUtil.isNotBlank(timestamp) && StrUtil.isNotBlank(nonce) && StrUtil.isNotBlank(echostr)) {
            Boolean result = publicAccountService.verifyWechatServiceUrl(signature, timestamp, nonce);
            if (result) {
                return echostr;
            } else {
                log.error("验证公众号消息推送失败");
            }
        }else{
            log.error("验证公众号消息推送失败");
        }
        return "";

    }

    /**
     * 处理消息推送事件
     * @param map
     * @param request
     * @return
     * @throws Exception
     */
    @PostMapping(value = "/msg")
    public String handleEvent(@RequestParam Map<String, String> map,HttpServletRequest request) throws Exception {
        // 验证消息并解密
        FollowEventParam followEventParam = publicAccountService.verifyMsgAndDecrypt(map, request);
        // 上锁 (上锁看情况,不需要就去除)
        String str = followEventParam.getEvent() + followEventParam.getCreateTime();
        String redissonLockKey = RedisKeyConstant.PUBLIC_ACCOUNT_MESSAGE_PUSH_LOCK + str;
        RLock lock = redissonClient.getLock(redissonLockKey);
        lock.lock();
        try {
            // 关注事件
            Boolean followResult = publicAccountService.followEvent(followEventParam);
            if(followResult){
                return PUSH_SUCCESS;
            }

            log.error("本次消息推送无法处理");
            return "";
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(e.getMessage());
        } finally {
            // 解锁
            lock.unlock();
        }
    }

}

Service

import cn.hutool.core.util.StrUtil;
import cn.mrc.backend.account.aes.AesException;
import cn.mrc.backend.account.aes.WXBizMsgCrypt;
import cn.mrc.backend.account.param.FollowEventParam;
import cn.mrc.backend.account.service.PublicAccountService;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;

import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.Map;


@Slf4j
@Service
public class PublicAccountServiceImpl implements PublicAccountService {

    String token = "你的token";
    String encodingAesKey = "你的encodingAesKey ";
    String appId = "你的公众号appId";

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public Boolean verifyWechatServiceUrl(String signature, String timestamp, String nonce) throws AesException {
        WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);
        return pc.verifyUrl(signature, timestamp, nonce);
    }

    

    @Override
    public FollowEventParam verifyMsgAndDecrypt(Map<String, String> map, HttpServletRequest request) throws Exception {

        String encryptData = formatXMLData(request);

        String signature = map.get("signature");
        String timestamp = map.get("timestamp");
        String nonce = map.get("nonce");
        String msgSignature = map.get("msg_signature");
        String openid = map.get("openid");
        String encryptType = map.get("encrypt_type");

        WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);
        // 验签并解密得到XML数据包
        String xmlData = pc.decryptMsg(signature, timestamp, nonce, encryptData);
        // XML数据包转为JSON
        XmlMapper xmlMapper = new XmlMapper();
        Object jsonObject = xmlMapper.readValue(xmlData, Object.class);
        String jsonData = objectMapper.writeValueAsString(jsonObject);
        // JSON转对象
        return objectMapper.readValue(jsonData, new TypeReference<FollowEventParam>() {
        });
    }


    @Override
    public Boolean followEvent(FollowEventParam followEventParam){

        if(StrUtil.isBlank(followEventParam.getMsgType()) || StrUtil.isBlank(followEventParam.getEvent())){
            return false;
        }

        if (followEventParam.getMsgType().equals("event") && (followEventParam.getEvent().equals("subscribe"))) {
            // 关注
            log.info(followEventParam.getToUserName() + "::" + followEventParam.getFromUserName() + ",关注");


            return true;
        } else if (followEventParam.getMsgType().equals("event") && (followEventParam.getEvent().equals("unsubscribe"))) {
            // 取消关注

            log.info(followEventParam.getToUserName() + "::" + followEventParam.getFromUserName() + ",取消关注");
            return true;
        } else {
            return false;
        }

    }


    private String formatXMLData(HttpServletRequest request) {
        DocumentBuilderFactory xmlFactory = DocumentBuilderFactory.newInstance();
        TransformerFactory strFactory = TransformerFactory.newInstance();

        try {
            DocumentBuilder builder = xmlFactory.newDocumentBuilder();
            InputStream inputStream = request.getInputStream();
            Document document = builder.parse(inputStream);

            Transformer transformer = strFactory.newTransformer();
            StringWriter writer = new StringWriter();
            transformer.transform(new DOMSource(document), new StreamResult(writer));
            return writer.getBuffer().toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

linux防护与集群——系统安全及应用

一、账号安全控制&#xff1a; 用户账号是计算机使用者的身份凭证或标识&#xff0c;每个要访问系统资源的人&#xff0c;必须凭借其用户账号才能进入计算机。在Linux系统中&#xff0c;提供了多种机制来确保用户账号的正当、安全使用 1.1 基本安全措施&#xff1a; 在Linux…

业务项目中Echarts图表组件的封装实践方案

背景&#xff1a;如果我们的项目是一个可视化类/营销看板类/大屏展示类业务项目&#xff0c;不可避免的会使用到各种图表展示。那在一个项目中如何封装一个图表组件既能够快速复用、UI统一&#xff0c;又可以灵活扩充Echarts的各种复杂配置项配置就变得极为重要。 封装目标 符…

vmware workstation的三种网络模式通俗理解

一、前言 workstations想必很多童鞋都在用&#xff0c;经常会用来在本机创建不同的虚拟机来做各种测试&#xff0c;那么对于它支持的网络模式&#xff0c;在不同的测试场景下应该用哪种网络模式&#xff0c;你需要做下了解&#xff0c;以便可以愉快的继续测&#xff08;搬&…

1.5C语言 双曲正弦函数(*) 优化麦克劳林公式

一.传统算法 #include<stdio.h> #include<math.h> int jc(int x); int main(){double x,eps,y0.0;scanf("%lf%lf",&x,&eps);int de1,i1;double item1.0;while(fabs(item)>eps){itempow(x,i)/jc(de);i2;yitem;}printf("%.6f\n",y); …

H5 - - - - - 获取图片exif相关信息

1. EXIF是什么 【可交换图像文件格式】&#xff1a;&#xff08;英语&#xff1a;Exchangeable image file format&#xff0c;官方简称Exif&#xff09;,是专门为数码相机的照片设定的&#xff0c;可以记录数码照片的属性信息和拍摄数据。 2. EXIF 相关标识 { ApertureValu…

科研上新 | 第6期:优化LLM数学推理;深度学习建模基因表达调控;基于深度学习的近实时海洋碳汇估算

编者按&#xff1a;欢迎阅读“科研上新”栏目&#xff01;“科研上新”汇聚了微软亚洲研究院最新的创新成果与科研动态。在这里&#xff0c;你可以快速浏览研究院的亮点资讯&#xff0c;保持对前沿领域的敏锐嗅觉&#xff0c;同时也能找到先进实用的开源工具。 本期内容速览 …

redisson作为分布式锁的底层实现

1. redisson如何实现尝试获取锁的逻辑 如何实现在一段的时间内不断的尝试获取锁 其实就是搞了个while循环&#xff0c;不断的去尝试获取锁资源。但是因为latch的存在会在给定的时间内处于休眠状态。这个事件&#xff0c;监听的是解锁动作&#xff0c;如果解锁动作发生。会调用…

webrtc报文记录

tcp.port 10443 || tcp.port 6080 || udp.port 8000 https://download.csdn.net/download/dualvencsdn/88706745

flutter学习-day23-使用extended_image处理图片的加载和操作

文章目录 1. 介绍2. 属性介绍3. 使用 1. 介绍 在 Flutter 的开发过程中&#xff0c;经常会遇到图片的显示和加载处理&#xff0c;通常显示一个图片&#xff0c;都有很多细节需要处理&#xff0c;比如图片的加载、缓存、错误处理、图片的压缩、图片的格式转换等&#xff0c;如果…

面试算法97:子序列的数目

题目 输入字符串S和T&#xff0c;请计算字符串S中有多少个子序列等于字符串T。例如&#xff0c;在字符串"appplep"中&#xff0c;有3个子序列等于字符串"apple" 分析 为了解决这个问题&#xff0c;每步从字符串S中取出一个字符判断它是否和字符串T中的…

《MySQL系列-InnoDB引擎04》MySQL表相关介绍

文章目录 第四章 表1 索引组织表2 InnoDB逻辑存储结构2.1 表空间2.2 段2.3 区2.4 页2.5 行2.6 拓展&#xff1a;MySQL的varchar(n)能存储几个字符&#xff1f;占多少字节&#xff1f; 3 InnoDB行记录格式4 文件格式5 约束5.1 数据完整性5.2 约束的创建和查找5.3 约束和索引的区…

决策树--分类决策树

1、介绍 ① 定义 分类决策树通过树形结构来模拟决策过程&#xff0c;决策树由结点和有向边组成。结点有两种类型&#xff1a;内部结 点和叶结点。内部结点表示一个特征或属性&#xff0c;叶子节点表示一个类。 ② 生成过程 用决策树分类&#xff0c;从根结点开始&#xff…

UI5与后端的文件交互(三)

文章目录 前言一、开发Action1. 修改Table2. BDEF中新增Action3. 新建结构&#xff0c;用于接收uuid以及附件数据4. 实现Method逻辑 二、UI5项目修改1. 添加表格行2. 事件处理函数3. 点击文件名时的事件 三、测试 前言 这系列文章详细记录在Fiori应用中如何在前端和后端之间使…

GB∕T 33171-2016 城市交通运行状况评价规范

免登陆免积分下载地址 标准号&#xff1a;GB/T 33171-2016 中文标准名称&#xff1a;城市交通运行状况评价规范 英文标准名称&#xff1a;Specification for urban traffic performance evaluation 中国标准分类号&#xff08;CCS&#xff09;R85 国际标准分类号&#xff08;…

python的课后练习总结3(字典)

1&#xff0c;创建空字典 空字典的创建 名字 { } 1&#xff0c;字典的查 变量名 {星期一:上课,星期二:休息,星期三:吃晚饭} print(变量名[星期一]) &#xff08;1&#xff09;get( ) 语法&#xff1a; 字典序列名.get(键&#xff0c;随便写) 如果键存在&#xff0c;返回值…

CTFHub | 存储型

0x00 前言 CTFHub 专注网络安全、信息安全、白帽子技术的在线学习&#xff0c;实训平台。提供优质的赛事及学习服务&#xff0c;拥有完善的题目环境及配套 writeup &#xff0c;降低 CTF 学习入门门槛&#xff0c;快速帮助选手成长&#xff0c;跟随主流比赛潮流。 0x01 题目描述…

linux 浅练一下哈

1.新建用户test不建家目录不允许登录&#xff0c;uid为10086_____________________ useradd -u 10086 -M -s /sbin/nologin 2.将 /opt 文件夹中所有文件的属主&#xff0c;属组改成&#xff0c;test_______________________ chown -R test.test /opt chown -R …

年度最整洁的海盗3.0版本

在修改海盗3.0客户端源码的时候&#xff0c;一直都存在这样的一个问题&#xff1a; 客户端在某些特定的情况下&#xff0c;会报内存错误导致程序崩溃。 经过调试&#xff0c;发现是那个MindPower3D的dll&#xff0c;在跳转地图等情况下卸载清理内存的时候&#xff0c;会偶发出…

科普:嵌入式多核并行仿真

自信息技术革命以来&#xff0c;计算机一直被应用在各种复杂的数据处理中&#xff0c;如火箭弹道&#xff0c;高能物理和生物学数据等。随着嵌入式领域的多样化需求的不断丰富&#xff0c;多核CPU的应用也越来越广泛&#xff1a;嵌入式系统通常需要同时处理多个任务和实时数据&…