之前写过一个解密json格式加密的,我以为xml的和json的差不多,是上上个星期五吧,我的同事也是在做微信公众号里面的消息推送解密,发现好像只能使用xml加密格式的发送到服务器,我们去年也做过企业微信的那个消息推送的解密,真的是,感觉虽然都差不多,但是三者如果使用同样的代码的话完全不能复用,只是你做过一个之后,如果在做其他的就会了解他的原理。我在github上面正在完善node对于微信的各种API的配置和使用,地址 https://github.com/zzz111111/wx_app 里面有所有的源代码,使用了express框架。

  xml格式的解密实际上和json格式的是差不多的,只不过是express框架的一些问题影响到了我们,使用了body-parser中间件之后json格式的数据会加到 req.body 里面去,但是xml格式的话我们一般没有使用其他的中间件去把xml格式的数据加入到 req.body 上面。这里我用到了 express-xml-bodyparser 中间件把xml解析出来了,也可以不使用这些,直接使用原生的代码也可以拿到这些数据。

  使用中间件的代码。

 const xmlparser = require('express-xml-bodyparser');

 app.use(xmlparser()); /* 为了解析微信的 xml 格式的文件而加入的 */

 router.post("/你的地址", (req, res) => {
console.log('接收到了请求url中');
console.log(req.query);
console.log('接收到了请求,请求体中');
console.log(req.body);
});

  不使用中间件也是可以获取到的,写这个的原因是因为同事用的是koa2框架,并没有找到解析xml格式的中间件,所以后来在网上找了一些资料,自己写了一个。

  刚才在测验的时候,我在原生的req对象上面找到了请求的内容在哪里。。。,我也真是个天才。 koa2框架。

 router.post('/xml', async ctx => {
console.log('请求到了xml接口'); console.log(ctx.req._readableState.buffer);
console.log('其它的内容 --- head');
var buf1 = ctx.req._readableState.buffer.head.data;
console.log(buf1);
console.log(new Buffer(buf1).toString());
console.log('其它的内容 --- tail');
var buf2 = ctx.req._readableState.buffer.tail.data;
console.log(buf2);
console.log(new Buffer(buf2).toString());
ctx.body = 'xml';
})

  那两个被Buffer对象转换之后的都是xml格式的对象。

  这里的xml格式的内容还是一个字符串,我们先用正常的方式来获取请求体中内容,再来解析它。

  上面的纯属瞎搞,一般我们的属性的名字前面加了一个 _ (也就是下划线)就代表这个东西不希望外部访问到吧,所以用一个正常人的操作。正常的来获取它。

 router.post('/xmlTest', async ctx => {
console.log('请求到了xml接口');
var data = '';
ctx.req.on('data', (chunk) => {
data += chunk;
});
ctx.req.on('end', () => {
console.log('传输数据结束');
console.log(data);
});
ctx.body = 'xml';
});

  下面就是请求他的东西。

  这个样子的才算是正常的,拿到请求的xml格式的东西,接下来我们使用 xml2js 模块来解析它,把这个东西转换为json格式的对象。

  npm i -S xml2js

  xml转json  和json 转xml的方法

 const xml = {
xmlToJson(str){
return new Promise((resolve, reject) => {
const parseString = xml2js.parseString
parseString(str, (err, result) => {
if (err) {
reject(err)
} else {
resolve(result)
}
})
})
},
jsonToXml() {
const builder = new xml2js.Builder()
return builder.buildObject(obj)
} }

  我们可以使用koa2这种洋葱模型的方式,在前一个方法中解析xml文件,把他放到 ctx.req.body的上面访问到它。

 router.post('/xml2', async (ctx , next)=> {
if (ctx.method == 'POST' && ctx.is('text/xml')) {
let promise = new Promise(function (resolve, reject) {
let buf = ''
ctx.req.setEncoding('utf8')
ctx.req.on('data', (chunk) => {
console.log('接收数据'); console.log(chunk);
buf += chunk })
ctx.req.on('end', () => {
xml.xmlToJson(buf)
.then(resolve)
.catch(reject)
})
}) await promise.then((result) => {
ctx.req.body = result
})
.catch((e) => {
e.status = 400
}) next()
} else {
await next()
}
}, async ctx => {
console.log(ctx.req.body); console.log(ctx.req.body.xml.ToUserName[0])
ctx.body = '第二次返回信息';
});

  上面就可以解析到了,把xml格式转为json对象了。

  这篇文章的主题就是解析微信推送xml消息,咱们不能跑题,上面这些都是铺垫,都是基础,我们当时就是因为拿不到xml的数据,找了好久的方法,express直接安装上面所说的中间件就好了,koa2由于没有找到适合的中间件,可以自己写一下也可以拿到。

  闲话不多说,拿到之后其实就和json格式的没有区别了,可以去看看json格式的解析。

  由于我是使用的express框架,把方法单离出来了,所以把代码发出来,看详细的可以去我的 github上面去看,上面也给了地址了。

 /**
* 此处方法解析的是微信消息加密 XML 格式的
*
* 过程介绍为
* 1. 先拿到消息 URL 中的字符串,并且拿到消息体中的密文体
* 2. 对 URL 和 密文体 进行微信方面提供的加密方法验证是否等于消息体签名,验证消息是否为微信转发过来的
* 3. 第 2 步验证成功之后,对微信消息进行解密,解密函数在工具函数中
*
* URL地址中的内容
*
* @params {String} signature 签名串
* @params {String} timestamp 时间戳
* @params {String} nonce 随机串
* @params {String} encrypt_type 加密类型(aes)
* @params {String} openid
* @params {String} msg_signature 消息体签名.用于验证消息体的正确性
*
* 请求体中的内容 -- 解析后
* @params {String} tousername 小程序的原始id
* @params {String} encrypt 加密后的消息字符串
*
*/
exports.handleCustomerServerXML = (req, res) => {
console.log('接收到了请求url中');
console.log(req.query);
console.log('接收到了请求,请求体中');
console.log(req.body);
const {signature,timestamp, nonce, encrypt_type, openid, msg_signature} = req.query;
const msg_encrypt = req.body.xml.encrypt[0]; // 验证消息的正确性
const dev_msg_signature = sha1(config.pushToken, timestamp, nonce, msg_encrypt);
if(dev_msg_signature == msg_signature){
// 签名消息正确,来自微信服务器 解密
const lastData = utils.decryptXML({
AESKey: config.server.EncodingAESKey,
text: msg_encrypt,
corpid: config.app.appId
});
console.log('msg函数中接收到的数据内容'); console.log(lastData);
console.log('收到的消息为 --------- ' + lastData.msg.xml.Content[0]); var msgArr = {
'新年好': '你TM新年也好啊',
'值班': '老子今天不上班,你值你m呢',
'你好': '你好',
'什么': '你在说什么呢?'
};
var replyMsg = msgArr[lastData.msg.xml.Content[0]];
if(replyMsg){
ZY.msg.textMsg(openid, openid, replyMsg)
.then(res => {
console.log('消息发送成功!');
console.log(res);
})
.catch(err => {
console.log('消息发送失败');
console.log(err);
})
}else{
ZY.msg.textMsg(openid, openid, '你瞧瞧你说的是人话吗?')
.then(res => {
console.log('消息发送成功!');
console.log(res);
})
.catch(err => {
console.log('消息发送失败');
console.log(err);
})
}
res.send('success');
}else{
console.log('非微信服务器试图发送消息给我!!');
res.send('你在玩啥呢??');
}
}

  sha1方法

 /*
@explain sh1加密
@version 1.0.1 @author : Z
@data : 2019-2-13 @params {String, String...} a,b,c……
@return {String} 加密完成后的字符串
*/
exports.sha1 = function (...arr) {
return crypto.createHash('sha1').update(arr.sort().join('')).digest('hex');
};

  只针对于xml格式的解析方法。

 /**
* 解析微信上传消息 此方法只解析 XML 格式
* @versin 1.0.0
* @data 2019-3-21
*
* @params {Object}
* obj.AESKey:解密的aesKey值
* obj.text: 需要解密的密文
* obj.corpid: 企业的id / 微信小程序的appid
*
* @return {Object}
* obj.noncestr 随机数
* obj.msg_len 微信密文的len
* obj.msg 解密后的明文 JSON 格式
* obj.corpid 错乱的 appid 尾部填充了些东西,可以舍弃,因为我们知道自己的appid是多少,不需要这里告诉我们
*
*/
exports.decryptXML = function(obj){
let aesKey = Buffer.from(obj.AESKey + '=', 'base64');
const cipherEncoding = 'base64';
const clearEncoding = 'utf8';
const cipher = crypto.createDecipheriv('aes-256-cbc',aesKey,aesKey.slice(0, 16));
cipher.setAutoPadding(false); // 是否取消自动填充 不取消
let this_text = cipher.update(obj.text, cipherEncoding, clearEncoding) + cipher.final(clearEncoding);
let xmlText = '';
xml2js.parseString(this_text.substring(20,this_text.lastIndexOf(">")+1), function(err, result){
if(err) throw err;
xmlText = result;
});
return {
noncestr:this_text.substring(0,16),
msg_len:this_text.substring(16,20),
msg:xmlText,
corpid: this_text.substring(this_text.lastIndexOf(">")+1)
}
}

放到服务器上面的打印是这样的。

  那些看不到的信息,可以在加密的函数中打印看到到底返回来什么。

  回复的消息图。

 

  这一篇文章的解析过程没有之前那个详细,如果想了解更多,可以两篇互相补充的看一看。 https://www.cnblogs.com/z937741304/p/10380496.html

  如果你看了我的文章学习到了,并且解决了你的问题我会非常高兴,如有不足之处,希望各位可以指正,谢谢!

node.js解析微信消息推送xml格式加密的消息的更多相关文章

  1. [置顶] 手把手教你iOS消息推送证书生成以及Push消息

    iOS推送消息是许多iOS应用都具备的功能,今天在给应用加推送功能,在生成证书的过程中,发生了各种令人蛋痛的事.下面就把步骤拿出来分享下: iOS消息推送的工作机制可以简单的用下图来概括: Provi ...

  2. APNS IOS 消息推送JSON格式介绍

    在开发向苹果Apns推送消息服务功能,我们需要根据Apns接受的数据格式进行推送.下面积累了我在进行apns推送时候总结的 apns服务接受的Json数据格式 示例 1: 以下负载包含哦一个简单的 a ...

  3. 【js学习】js连接RabbitMQ达到实时消息推送

    js连接RabbitMQ达到实时消息推送 最近在自己捯饬一个网站,有一个功能是需要后端处理完数据把数据发布到MQ中,前端再从MQ中接收数据.但是前端连接MQ又成了一个问题,在网上搜了下资料,点进去一篇 ...

  4. 基于XMPP协议(openfire服务器)的消息推送实现

    转自:http://blog.csdn.net/nomousewch/article/details/8088277 最近好像有不少朋友关注Android客户端消息推送的实现,我在之前的项目中用到过J ...

  5. [Erlang 0106] Erlang实现Apple Push Notifications消息推送

        我们的IOS移动应用要实现消息推送,告诉用户有多少条消息未读,类似下图的效果(笑果),特把APNS和Erlang相关解决方案笔记于此备忘.          上面图片中是Apple Notif ...

  6. ios 消息推送流程 转载

    iOS开发:推送通知简述及开发实践热度 1已有 706 次阅读 2013-10-15 09:23 |个人分类:经验之谈|系统分类:ios| IOS, 推送一.关于推送通知 推送通知,也被叫做远程通知, ...

  7. iOS开发如何实现消息推送机制

    一.关于推送通知 推送通知,也被叫做远程通知,是在iOS 3.0以后被引入的功能.是当程序没有启动或不在前台运行时,告诉用户有新消息的一种途径,是从外部服务器发送到应用程序上的.一般说来,当要显示消息 ...

  8. Windows phone Toast消息推送 学习笔记

    简单介绍: Windows phone平台支持三种形式的推送通知: 1.Tile——也就是在Start屏幕程序平铺图标 2.Toast——创建一个显示在当前屏幕中的Toast弹出窗口 3.Raw——有 ...

  9. 基于socket.io的实时消息推送

    用户访问Web站点的过程是基于HTTP协议的,而HTTP协议的工作模式是:请求-响应,客户端发出访问请求,服务器端以资源数据响应请求. 也就是说,服务器端始终是被动的,即使服务器端的资源数据发生变化, ...

随机推荐

  1. MySQL8.0新特性——支持原子DDL语句

    MySQL 8.0开始支持原子数据定义语言(DDL)语句.此功能称为原子DDL.原子DDL语句将与DDL操作关联的数据字典更新,存储引擎操作和二进制日志写入组合到单个原子事务中.即使服务器在操作期间暂 ...

  2. C++基础——类继承

    一.前言  好吧,本系列博客已经变成了<C++ Primer Plus>的读书笔记,尴尬.在使用C语言时,多通过添加库函数的方式实现代码重用,但有一个弊端就是原来写好的代码并不完全适用于现 ...

  3. IIS web搭建之虚拟主机

    IIS web搭建之虚拟主机 虚拟目录:能将一个网站的文件分散存储在同一个计算机的不同目录和其他计算机. 使用虚拟目录的好处: 1.将数据分散保存到不同的磁盘或者计算机上,便于分别开发和维护. 2.当 ...

  4. 访问System x3650 IMM2的几种方式

    一.通过web浏览器访问 1.打开浏览器,在地址栏上输入IMM2的IP地址访问,打开登录页面后,输入用户名和密码 登录 PS:第一次登录IMM2时,初始的用户名为USERID,密码为PASSW0RD( ...

  5. 对 Undefined 与 Null 的一些理解

    Undefined 和 Null 是 Javascript 中两种特殊的原始数据类型(Primary Type),它们都只有一个值,分别对应 undefined 和 null ,这两种不同类型的值,既 ...

  6. 【死磕 Spring】----- IOC 之 获取 Document 对象

    原文出自:http://cmsblogs.com 在 XmlBeanDefinitionReader.doLoadDocument() 方法中做了两件事情,一是调用 getValidationMode ...

  7. 2019腾讯WXG移动客户端暑期实习面经

    微信这个比较迷,二面完官网流程灰了,但是过了一周多突然来三面,下午面完三面晚上HR面,第三天offer call, 莫名其妙过了 之前以为已经挂了,所以没有写面经,现在距一面已经快一个月了,只能还记得 ...

  8. Docker 导出&加载镜像

    文章首发自个人网站:https://www.exception.site/docker/docker-save-load-image 本文中,您将学习 Docker 如何导出&加载镜像.当我们 ...

  9. Docker 查看镜像信息

    欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 资深架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 文章首发个人网站: https://ww ...

  10. 死磕 java集合之HashSet源码分析

    问题 (1)集合(Collection)和集合(Set)有什么区别? (2)HashSet怎么保证添加元素不重复? (3)HashSet是否允许null元素? (4)HashSet是有序的吗? (5) ...