欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

node.js解析微信消息推送xml格式加密的消息

程序员文章站 2022-04-14 17:03:16
之前写过一个解密json格式加密的,我以为xml的和json的差不多,是上上个星期五吧,我的同事也是在做微信公众号里面的消息推送解密,发现好像只能使用xml加密格式的发送到服务器,我们去年也做过企业微信的那个消息推送的解密,真的是,感觉虽然都差不多,但是三者如果使用同样的代码的话完全不能复用,只是你 ......

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

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

  使用中间件的代码。

 1 const xmlparser = require('express-xml-bodyparser');
 2 
 3 app.use(xmlparser()); /* 为了解析微信的 xml 格式的文件而加入的 */
 4 
 5 
 6 router.post("/你的地址", (req, res) => {
 7   console.log('接收到了请求url中');
 8   console.log(req.query);
 9   console.log('接收到了请求,请求体中');
10   console.log(req.body);
11 });

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

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

 1 router.post('/xml', async ctx => {
 2     console.log('请求到了xml接口');
 3     
 4     console.log(ctx.req._readablestate.buffer);
 5     console.log('其它的内容 ---  head');
 6     var buf1 = ctx.req._readablestate.buffer.head.data;
 7     console.log(buf1);
 8     console.log(new buffer(buf1).tostring());
 9     console.log('其它的内容 ---  tail');
10     var buf2 = ctx.req._readablestate.buffer.tail.data;
11     console.log(buf2);
12     console.log(new buffer(buf2).tostring());
13     ctx.body = 'xml';
14 })

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

node.js解析微信消息推送xml格式加密的消息

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

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

 1 router.post('/xmltest', async ctx => {
 2     console.log('请求到了xml接口');
 3     var data = '';
 4     ctx.req.on('data', (chunk) => {
 5         data += chunk;
 6     });
 7     ctx.req.on('end', () => {
 8         console.log('传输数据结束');
 9         console.log(data);
10     });
11     ctx.body = 'xml';
12 });

  下面就是请求他的东西。

node.js解析微信消息推送xml格式加密的消息

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

  npm i -s xml2js

  xml转json  和json 转xml的方法

 1 const xml = {
 2     xmltojson(str){
 3         return new promise((resolve, reject) => {
 4             const parsestring = xml2js.parsestring
 5             parsestring(str, (err, result) => {
 6                 if (err) {
 7                     reject(err)
 8                 } else {
 9                     resolve(result)
10                 }
11             })
12          })
13     },
14     jsontoxml() {
15         const builder = new xml2js.builder()
16         return builder.buildobject(obj)
17     }
18 
19 }

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

 1 router.post('/xml2', async (ctx , next)=> {
 2     if (ctx.method == 'post' && ctx.is('text/xml')) {
 3         let promise = new promise(function (resolve, reject) {
 4             let buf = ''
 5             ctx.req.setencoding('utf8')
 6             ctx.req.on('data', (chunk) => {
 7                 console.log('接收数据');
 8                 
 9                 console.log(chunk);
10                 buf += chunk
11 
12             })
13             ctx.req.on('end', () => {
14                 xml.xmltojson(buf)
15                     .then(resolve)
16                     .catch(reject)
17             })
18         })
19 
20         await promise.then((result) => {
21                 ctx.req.body = result
22             })
23             .catch((e) => {
24                 e.status = 400
25             })
26 
27         next()
28     } else {
29         await next()
30     }
31 },  async ctx => {
32     console.log(ctx.req.body);
33     
34     console.log(ctx.req.body.xml.tousername[0])
35     ctx.body = '第二次返回信息';
36 });

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

node.js解析微信消息推送xml格式加密的消息

 

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

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

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

 1 /**
 2  * 此处方法解析的是微信消息加密 xml 格式的
 3  * 
 4  * 过程介绍为 
 5  * 1. 先拿到消息 url 中的字符串,并且拿到消息体中的密文体
 6  * 2. 对 url 和 密文体 进行微信方面提供的加密方法验证是否等于消息体签名,验证消息是否为微信转发过来的
 7  * 3. 第 2 步验证成功之后,对微信消息进行解密,解密函数在工具函数中
 8  * 
 9  * url地址中的内容
10  * 
11  * @params {string} signature      签名串
12  * @params {string} timestamp      时间戳
13  * @params {string} nonce          随机串
14  * @params {string} encrypt_type   加密类型(aes)
15  * @params {string} openid         
16  * @params {string} msg_signature  消息体签名.用于验证消息体的正确性
17  * 
18  * 请求体中的内容 -- 解析后
19  * @params {string} tousername    小程序的原始id
20  * @params {string} encrypt       加密后的消息字符串
21  *  
22  */
23 exports.handlecustomerserverxml = (req, res) => {
24   console.log('接收到了请求url中');
25   console.log(req.query);
26   console.log('接收到了请求,请求体中');
27   console.log(req.body);
28   const {signature,timestamp, nonce, encrypt_type, openid, msg_signature} = req.query;
29   const msg_encrypt = req.body.xml.encrypt[0];
30   
31   // 验证消息的正确性
32   const dev_msg_signature = sha1(config.pushtoken, timestamp, nonce, msg_encrypt);
33   if(dev_msg_signature == msg_signature){
34     // 签名消息正确,来自微信服务器 解密
35     const lastdata = utils.decryptxml({
36       aeskey: config.server.encodingaeskey,
37       text: msg_encrypt,
38       corpid: config.app.appid
39     });
40     console.log('msg函数中接收到的数据内容');
41 
42     console.log(lastdata);
43     console.log('收到的消息为 --------- ' + lastdata.msg.xml.content[0]);
44 
45     var msgarr = {
46       '新年好': '你tm新年也好啊',
47       '值班': '老子今天不上班,你值你m呢',
48       '你好': '你好',
49       '什么': '你在说什么呢?'
50     };
51     var replymsg = msgarr[lastdata.msg.xml.content[0]];
52     if(replymsg){
53       zy.msg.textmsg(openid, openid, replymsg)
54         .then(res => {
55           console.log('消息发送成功!');
56           console.log(res);
57         })
58         .catch(err => {
59           console.log('消息发送失败');
60           console.log(err);
61         })
62     }else{
63       zy.msg.textmsg(openid, openid, '你瞧瞧你说的是人话吗?')
64         .then(res => {
65           console.log('消息发送成功!');
66           console.log(res);
67         })
68         .catch(err => {
69           console.log('消息发送失败');
70           console.log(err);
71         })
72     }
73     res.send('success');
74   }else{
75     console.log('非微信服务器试图发送消息给我!!');
76     res.send('你在玩啥呢??');
77   }
78 }

  sha1方法

 1 /*
 2     @explain sh1加密
 3     @version 1.0.1
 4     
 5     @author : z
 6     @data : 2019-2-13
 7     
 8     @params {string, string...} a,b,c……
 9     @return {string} 加密完成后的字符串
10  */
11 exports.sha1 = function (...arr) {
12     return crypto.createhash('sha1').update(arr.sort().join('')).digest('hex');
13 };

 

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

 1 /**
 2  * 解析微信上传消息 此方法只解析 xml 格式
 3  * @versin 1.0.0
 4  * @data 2019-3-21
 5  * 
 6  * @params {object}
 7  *  obj.aeskey:解密的aeskey值
 8  *  obj.text: 需要解密的密文
 9  *  obj.corpid: 企业的id / 微信小程序的appid
10  * 
11  * @return {object}
12  *  obj.noncestr  随机数
13  *  obj.msg_len   微信密文的len
14  *  obj.msg       解密后的明文 json 格式
15  *  obj.corpid    错乱的 appid 尾部填充了些东西,可以舍弃,因为我们知道自己的appid是多少,不需要这里告诉我们
16  * 
17  */
18 exports.decryptxml = function(obj){
19     let aeskey = buffer.from(obj.aeskey + '=', 'base64');
20     const cipherencoding = 'base64';
21     const clearencoding = 'utf8';
22     const cipher = crypto.createdecipheriv('aes-256-cbc',aeskey,aeskey.slice(0, 16));
23     cipher.setautopadding(false); // 是否取消自动填充 不取消
24     let this_text = cipher.update(obj.text, cipherencoding, clearencoding) + cipher.final(clearencoding);
25     let xmltext = '';
26     xml2js.parsestring(this_text.substring(20,this_text.lastindexof(">")+1), function(err, result){
27         if(err) throw err;
28         xmltext = result;
29     });
30     return {
31         noncestr:this_text.substring(0,16),
32         msg_len:this_text.substring(16,20),
33         msg:xmltext,
34         corpid: this_text.substring(this_text.lastindexof(">")+1)
35     }
36 }

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

node.js解析微信消息推送xml格式加密的消息

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

  回复的消息图。

 node.js解析微信消息推送xml格式加密的消息

  这一篇文章的解析过程没有之前那个详细,如果想了解更多,可以两篇互相补充的看一看。 

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