微信公众号接收事件推送XML数据包解析成实体对象

news2024/12/24 8:55:47

1.从request获取推送xml包

String callBackXml = testNoticeService.formatNoticeParams(request);
public static String formatNoticeParams(HttpServletRequest request){
    
    try(ByteArrayOutputStream output = new ByteArrayOutputStream();
         InputStream input = request.getInputStream()){    
        byte[] by = new byte[1024];
        int length = 0;
        while((length = input .read(by)) != -1){
             output.write(by, 0, length);
        
        }
        return new String(output.toByteArray(), "UTF-8");

    }catch(IOException e){
         throw new BusinessException(ConstCode.PARAM.getCode(), "回调参数解析异常");
    }

}

2.转换成map

Map<String, String> baseMap = new Parser(callBackXml).xmlToMap();
public final class Parser {

     private String xmlString;

    
     public Parser(String xmlString) {
        if (StringUtils.isEmpty(xmlString)) {
            throw new BusinessException(ConstCode.FAILED.getCode(), "解析内容为空");
        }
        if (!isXML(xmlString)) {
            throw new BusinessException(ConstCode.FAILED.getCode(), "解析内容不是合法的xml格式");
        }
        this.xmlString = xmlString;
    }

}


private boolean isXML(String value) {
        try {
            DocumentHelper.parseText(value);
        } catch (DocumentException e) {
            return false;
        }
        return true;
    }

  private static InputStream getStringStream(String sInputString) {
        ByteArrayInputStream tInputStringStream = null;
        if (sInputString != null && !"".equals(sInputString.trim())) {
            tInputStringStream = new ByteArrayInputStream(sInputString.getBytes());
        }
        return tInputStringStream;
    }


 public synchronized Map<String, String> xmlToMap() throws ParserConfigurationException, IOException, SAXException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        InputStream is = getStringStream(xmlString);
        Document document = builder.parse(is);
        NodeList allNodes = document.getFirstChild().getChildNodes();
        Node node;
        Map<String, String> map = new HashMap<>(0);
        int i = 0;
        while (i < allNodes.getLength()) {
            node = allNodes.item(i);
            if (node instanceof Element) {
                map.put(node.getNodeName(), node.getTextContent());
            }
            i++;
        }
        return map;
    }

3.初始化并获得解密消息内容,appId,token,encodingAesKey从微信公众号平台基本配置获取到

String xmlContent = new WxMsgCryptUtils(appId,token,encodingAesKey).decrypt(baseMap.get("Encrypt"));
public class WxMsgCryptUtils {

    /**
     * 公众平台上,开发者设置的 token
     */
    private static String token;

    /**
     * 公众平台 appID
     */
    private static String appID;

    /**
     * 公众平台上,开发者设置的 EncodingAESKey
     */
    private static byte[] aesKey;

    /**
     * UTF_8 字符集
     */
    private static final Charset UTF_8 = StandardCharsets.UTF_8;

     /**
     * 初始化
     *
     * @param appID  公众平台 appID
     * @param token  公众平台上,开发者设置的 token
     * @param aesKey 公众平台上,开发者设置的 EncodingAESKey
     */
    public WxMsgCryptUtils(String appID, String token, String aesKey) {
        if (StrUtil.hasBlank(appID, token, aesKey) || aesKey.length() != 43) {
            throw new IllegalArgumentException("微信公众号配置信息有误");
        }
        this.appID = appID;
        this.token = token;
        this.aesKey = Base64.decode(aesKey);
    }


      /**
     * 4 个字节的网络字节序 bytes 数组还原成一个数字.
     */
    public static int bytesNetworkOrder2Number(byte[] bytesInNetworkOrder) {
        int sourceNumber = 0;
        for (int i = 0; i < 4; i++) {
            sourceNumber <<= 8;
            sourceNumber |= bytesInNetworkOrder[i] & 0xff;
        }
        return sourceNumber;
    }

   /**
     * 删除解密后明文的补位字符.
     *
     * @param decrypted 解密后的明文
     * @return 删除补位字符后的明文
     */
    public static byte[] decode(byte[] decrypted) {
        int pad = decrypted[decrypted.length - 1];
        if (pad < 1 || pad > 32) {
            pad = 0;
        }
        return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
    }


    /**
     * 对密文进行解密.
     *
     * @param cipherText 需要解密的密文
     * @return 解密得到的明文
     */

 public String decrypt(String cipherText) throws Exception  {

        // 设置解密模式为AES的CBC模式
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
        IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
        cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);

        // 使用BASE64对密文进行解码
        byte[] encrypted = Base64.decode(cipherText);

        // 解密
        byte[] original = cipher.doFinal(encrypted);
        byte[] bytes = decode(original);

        // 分离16位随机字符串,网络字节序和AppId
        byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);
        int xmlLength = bytesNetworkOrder2Number(networkOrder);

        String xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), UTF_8);

        return xmlContent;
}
      


}

4.解析成实体对象

WechatFollowVo wechatFollowVo = (WechatFollowVo) XmlUtil.fromXML(xmlContent, WechatFollowVo.class);
public class XmlUtil {

    /**
     * xml⽂档解析为对象
     * @param xml   xml文档
     * @param clazz 要转换的类
     * @return
     */

     public static Object fromXML(String xml, Class clazz){

        XStream xmlStream = new XStream(new XppDriver(new XmlFriendlyNameCoder("_-", "_")));
        XStream.setupDefaultSecurity(xmlStream);
        xmlStream.processAnnotations(new Class[]{clazz});
        xmlStream.allowTypes(new Class[]{clazz});
        xmlStream.ignoreUnknownElements();
        Object result = xmlStream.fromXML(xml);

        return result;
    }


}
@Data
@Accessors(chain = true)
@XStreamAlias(value = "xml")
public class WechatFollowVo implements Serializable {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty("开发者微信号")
    @XStreamAlias(value = "ToUserName")
    private String toUserName;

    @ApiModelProperty("发送?帐号(?个OpenID)")
    @XStreamAlias(value = "FromUserName")
    private String fromUserName;

    @ApiModelProperty("消息创建时间 (整型)")
    @XStreamAlias(value = "CreateTime")
    private Long createTime;

    @ApiModelProperty("消息类型,event")
    @XStreamAlias(value = "MsgType")
    private String messageType;

    @ApiModelProperty("事件类型,subscribe(订阅)、unsubscribe(取消订阅)")
    @XStreamAlias(value = "Event")
    private String event;

    @ApiModelProperty("事件 KEY 值,未关注:qrscene_为前缀,后面为二维码的参数值,以关注:是一个32位无符号整数,即创建二维码时的二维码scene_id")
    @XStreamAlias(value = "EventKey")
    private String eventKey;

    @ApiModelProperty("二维码的ticket,可用来换取二维码图片")
    @XStreamAlias(value = "Ticket")
    private String ticket;

    @ApiModelProperty("消息内容")
    @XStreamAlias(value = "Content")
    private String content;

}

        以上步骤已经完成了xml数据包转换成实体对象。当时写到try()里的声明,有些不明,也去查看了百度。好似有些懂了。Java7的新特性,作用相当于finally手动释放被占用的资源,如果使用之后,就会在程序块结束时自动释放掉占用的资源,代码也更简洁美观。前提是括号声明的资源实现类实现了Closeable或AutoCloseable接口。

注意:多个资源需要分号隔开

 

 

 

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

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

相关文章

微服务 springcloud 11 sleuth 链路跟踪,sleuth + zipkin 链路分析

随着系统规模越来越大&#xff0c;微服务之间调用关系变得错综复杂&#xff0c;一条调用链路中可能调用多个微服务&#xff0c;任何一个微服务不可用都可能造整个调用过程失败 spring cloud sleuth 可以跟踪调用链路&#xff0c;分析链路中每个节点的执行情况 01.微服务中添加 …

如何使用UpdraftPlus迁移备份网站到新网站?

这个问题是关于将您的站点克隆或迁移到新的网站URL的问题。如果您想克隆一个站点进行测试&#xff0c;或者在新版本上线之前移动到新主机并进行测试&#xff0c;以及许多其他类似情况&#xff0c;迁移尤其有用。 在下面的示例中&#xff0c;我们迁移到一个完全新的 WordPress …

搭建Hadoop高可用框架分布式集群

搭建Hadoop高可用框架分布式集群 一.基础配置 1.创建虚拟机&#xff0c;修改虚拟机的主机名 2.修改网络配置 master:192.168.6.200 slave1:192.168.6.201 slave2:192.168.6.202 3.互ping测试 4.sudo授权 5.安装vim编辑器 6.配置网络映射 master配置映射 master向slave1传递映…

MATLAB 之 Simulink系统的仿真与分析

这里写目录标题 一、Simulink 系统的仿真与分析1. 设置仿真参数1.1 Solver 参数设置1.2 Data lmport/Export 参数设置 2. 运行仿真与仿真结果分析2.1 运行仿真2.2 仿真结果分析 一、Simulink 系统的仿真与分析 系统的模型建立之后&#xff0c;选择仿真参数和数值算法&#xff…

LabVIEW自适应屏幕分辨率的两种方法

文章目录 前言一、方案1&#xff1a;组合缩放1、举例2、验证 二、方案2&#xff1a;分隔栏匹配窗格1、举例2、验证 前言 前阵子做的一个项目是在显示器分辨率为 2560*1600&#xff0c;缩放选项为 150% 的笔记本上开发的&#xff0c;但是当 vi 文件在另一台显示器分辨率为 1920…

ThingsBoard IoT Gateway 专栏 分享 前言

最近有朋友询问tb-gateway相关的问题&#xff0c;于是抽业余时间查看了一下官方文档&#xff0c;并做了一些尝试和案例。接下来我会将这些分享给大家。这也是一个系列&#xff0c;将会和我的ThingsBoard专栏放在一起。 ThingsBoard IoT Gateway 能够帮助你将连接到传统和第三方…

一次Android APK打包的报错

打包的时候报了一个错 没有记下来 但是解决方法是 打开Window-> Package Manager 移除这个&#xff08;这个是Unity的广告组件

杰西·利弗莫尔股票大作手操盘术策略

文章目录 上升趋势演变多次回撤回升&#xff0c;区间震荡向上突破&#xff0c;恢复上升趋势前期回升高点下挫&#xff0c;趋势逆转警告信号向下突破&#xff0c;确认下降趋势 下降趋势演变多次回升回撤&#xff0c;区间震荡向下突破&#xff0c;恢复下降趋势前期回撤低点反弹&a…

mysql-大数据的上传(load data)

文章目录 1. 大批量数据的上传如果使用insert语句就会很慢&#xff0c;可以使用load data的方式 1. 大批量数据的上传如果使用insert语句就会很慢&#xff0c;可以使用load data的方式 如下&#xff0c;我建立了一个这样的数据表&#xff0c; 2. 我要将以下数据插入数据表&am…

基于FPGA的RC滤波器设计实现

目录 简介&#xff1a; 传递函数 FPGA代码实现 总结 简介&#xff1a; RC滤波器的特性基本情况介绍 RC一阶低通滤波介绍&#xff1b;RC滤波器电路简单&#xff0c;抗干扰性强&#xff0c;有较好的低频性能&#xff0c;并且选用标准的阻容元件易得&#xff0c;所以在工程测…

【滤波】平滑

%matplotlib inline#format the book import book_format book_format.set_style()简介 当你考虑未来的数据时&#xff0c;卡尔曼滤波器的性能并不是最优的。例如&#xff0c;假设我们在跟踪飞行器&#xff0c;最新的观测值突然偏离的很离谱&#xff0c;就像这样&#xff08;我…

PHP的pack/unpack

前言&#xff1a;直接参照官网。 PHP: pack - Manual PHP中文手册 PHP中国镜像 php 国内镜像 PHP官方网站 PHP: unpack - Manual PHP中文手册 PHP中国镜像 php 国内镜像 PHP官方网站 1、作用 &#xff08;1&#xff09;pack&#xff1a;将数据打包成二进制字符串。将输入数据…

链表刷题(9-11)

目录 相交链表 环形链表 环形链表Ⅱ 相交链表 力扣 第一种思路&#xff1a;判断尾节点地址是否相同&#xff0c;时间复杂度为O(N^2)。 第二种思路&#xff1a;(节点对齐)记录两个链表节点个数&#xff0c;再根据节点差设置两个快慢指针进行next节点比对。时间复杂度O(N)(3N)…

PHP 税务申报征收系统mysql数据库web结构apache计算机软件工程网页wamp

一、源码特点 PHP 税务申报征收系统 是一套完善的WEB设计系统&#xff0c;对理解php编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 代码下载 https://download.csdn.net/download/qq_41221322/87959340https://downl…

spring boot security之前后端分离配置

前言 spring boot security默认配置有一个登录页面&#xff0c;当采用前后端分离的场景下&#xff0c;需要解决两个问题&#xff1a; 前端有自己的登录页面&#xff0c;不需要使用spring boot security默认的登录页面登录相关接口允许匿名访问 因此需要自定义相关实现。 自…

【C51 --- 单片机学习历程与分享】

51单片机学习历程与分享 开篇 --- 认识单片机1、什么是单片机&#xff1f;2、51单片机主要资源3、STC89C51 芯片简介4、单片机脚位判断5、51单片机的应用领域6、如何学好51单片机&#xff1f;7、参考文献 开篇 — 认识单片机 前言&#xff1a; 1.本专栏适合有一定C语言功底的读…

数据结构与算法:数组和字符串

1 数组 1.1 集合、列表、数组的联系与区别 集合&#xff1a;由一个或多个确定的元素所构成的整体。类型不一定相同、确定、无序、互异。 列表&#xff08;又称线性列表&#xff09;&#xff1a;按照一定的线性顺序&#xff0c;排列而成的数据项的集合。类型不一定相同、有序…

所有独立站都适合做谷歌推广吗?怎么做好谷歌推广?

大家有没有这种困扰&#xff1a;是不是所有的独立站都适合用谷歌来打广告呢&#xff1f;我的行业能不能用Google Ads来推广&#xff1f;如果我刚刚起步&#xff0c;我应该开启哪种类型的广告呢&#xff1f;让我们一起来揭秘吧&#xff01; 如果你是一个独立站卖家&#xff0c;…

用异或计算只出现一次的数字

因为与0异或的都是数字本身&#xff0c;数字本身和数字本身异或是等于0&#xff0c;应用这个定理&#xff0c;我们来做这个题 链接: leetcode用异或计算只出现一次的数字 class Solution { public:int singleNumber(vector<int>& nums) {size_t v 0;for(size_t i …

Git指南 - 刚提的commit 怎么找不到了(游离分支)?

在有一次使用git时&#xff0c;我提交commit后&#xff0c;并未push&#xff0c;然后直接切到了当前分支的某个tag&#xff0c;最后我想切回来的时候&#xff0c;竟然找不到我刚才提交commit的节点了… 关联篇 Git指南 - 你该掌握的那些基础认知和首次配置Git指南 - 项目实战中…