From 48e738b6a937a05d11fdbd39bd24cf76cfb590c3 Mon Sep 17 00:00:00 2001 From: Jackson Tian Date: Mon, 8 Jan 2018 11:52:50 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E5=88=86=E7=A6=BB=20get/post=20=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/wechat.js | 228 ++++++++++++++++++++++++++++---------------------- 1 file changed, 129 insertions(+), 99 deletions(-) diff --git a/lib/wechat.js b/lib/wechat.js index 80b29e5..5da332c 100644 --- a/lib/wechat.js +++ b/lib/wechat.js @@ -112,19 +112,20 @@ var tpl = ['', */ var compiled = ejs.compile(tpl); -var wrapTpl = '' + - ']]>' + - ']]>' + - '<%-timestamp%>' + - ']]>' + -''; - -var encryptWrap = ejs.compile(wrapTpl); +var encryptWrap = (function () { + var tpl = '' + + '' + + '' + + '${locals.timestamp}' + + '' + + ''; + return new Function ('locals', 'return `' + tpl + '`;'); +}()); function reply2CustomerService (fromUsername, toUsername, kfAccount) { var info = {}; info.msgType = 'transfer_customer_service'; - info.createTime = new Date().getTime(); + info.createTime = Date.now(); info.toUsername = toUsername; info.fromUsername = fromUsername; info.content = {}; @@ -155,7 +156,7 @@ function reply (content, fromUsername, toUsername) { } } info.msgType = type; - info.createTime = new Date().getTime(); + info.createTime = Date.now(); info.toUsername = toUsername; info.fromUsername = fromUsername; return compiled(info); @@ -169,124 +170,153 @@ class Wechat { this.token = config.token; this.appid = config.appid || ''; this.encodingAESKey = config.encodingAESKey || ''; + this.cryptor = new WXBizMsgCrypt(this.token, this.encodingAESKey, this.appid); } else { throw new TypeError('please check your config'); } } - middleware(handle) { - - if (this.encodingAESKey) { - this.cryptor = new WXBizMsgCrypt(this.token, this.encodingAESKey, this.appid); - } - + get() { return async (ctx, next) => { + const method = ctx.method; + if (method !== 'GET') { + throw new Error('The wechat.get handle only support GET method'); + } + const query = ctx.query; + const {timestamp, nonce, echostr} = query; + // 加密模式 const encrypted = !!(query.encrypt_type && query.encrypt_type === 'aes' && query.msg_signature); - const timestamp = query.timestamp; - const nonce = query.nonce; - const echostr = query.echostr; - const method = ctx.method; const TOKEN = ctx.wx_token || this.token; const CRYPTOR = ctx.wx_cryptor || this.cryptor; - if (method === 'GET') { - var valid = false; + var valid = false; + if (encrypted) { + if (!CRYPTOR) { + throw new Error(`In encrypt mode, must configure 'encodingAESKey' and 'appid'`); + } + var signature = query.msg_signature; + valid = signature === CRYPTOR.getSignature(timestamp, nonce, echostr); + } else { + // 校验 + valid = query.signature === getSignature(timestamp, nonce, TOKEN); + } + + if (!valid) { + ctx.status = 401; + ctx.body = 'Invalid signature'; + } else { if (encrypted) { - var signature = query.msg_signature; - valid = signature === CRYPTOR.getSignature(timestamp, nonce, echostr); + var decrypted = CRYPTOR.decrypt(echostr); + // TODO 检查appId的正确性 + ctx.body = decrypted.message; } else { - // 校验 - valid = query.signature === getSignature(timestamp, nonce, TOKEN); + ctx.body = echostr; } + } + }; + } - if (!valid) { + post(handle) { + return async (ctx, next) => { + const method = ctx.method; + if (method !== 'POST') { + throw new Error('The wechat.post handle only support POST method'); + } + + const query = ctx.query; + const {timestamp, nonce} = query; + + const TOKEN = ctx.wx_token || this.token; + const CRYPTOR = ctx.wx_cryptor || this.cryptor; + + // 加密模式 + const encrypted = !!(query.encrypt_type && query.encrypt_type === 'aes' && query.msg_signature); + + if (!encrypted) { + // 校验 + if (query.signature !== getSignature(timestamp, nonce, TOKEN)) { ctx.status = 401; ctx.body = 'Invalid signature'; - } else { - if (encrypted) { - var decrypted = CRYPTOR.decrypt(echostr); - // TODO 检查appId的正确性 - ctx.body = decrypted.message; - } else { - ctx.body = echostr; - } - } - } else if (method === 'POST') { - if (!encrypted) { - // 校验 - if (query.signature !== getSignature(timestamp, nonce, TOKEN)) { - ctx.status = 401; - ctx.body = 'Invalid signature'; - return; - } + return; } + } - var xml; - if (ctx.request.body && typeof ctx.request.body === 'string') { - xml = ctx.request.body; - } else { - // 取原始数据 - xml = await getRawBody(ctx.req, { - length: ctx.request.length, - limit: '1mb', - encoding: ctx.request.charset || 'utf-8' - }); - } + var xml; + if (ctx.request.body && typeof ctx.request.body === 'string') { + xml = ctx.request.body; + } else { + // 取原始数据 + xml = await getRawBody(ctx.req, { + length: ctx.request.length, + limit: '1mb', + encoding: ctx.request.charset || 'utf-8' + }); + } - // 保存原始xml - ctx.weixin_xml = xml; - // 解析xml - var result = await parseXML(xml); - var formatted = formatMessage(result.xml); - if (encrypted) { - var encryptMessage = formatted.Encrypt; - if (query.msg_signature !== CRYPTOR.getSignature(timestamp, nonce, encryptMessage)) { - ctx.status = 401; - ctx.body = 'Invalid signature'; - return; - } - var decryptedXML = CRYPTOR.decrypt(encryptMessage); - var messageWrapXml = decryptedXML.message; - if (messageWrapXml === '') { - ctx.status = 401; - ctx.body = 'Invalid signature'; - return; - } - var decodedXML = await parseXML(messageWrapXml); - formatted = formatMessage(decodedXML.xml); + // 保存原始xml + ctx.weixin_xml = xml; + // 解析xml + var result = await parseXML(xml); + var formatted = formatMessage(result.xml); + if (encrypted) { + var encryptMessage = formatted.Encrypt; + if (query.msg_signature !== CRYPTOR.getSignature(timestamp, nonce, encryptMessage)) { + ctx.status = 401; + ctx.body = 'Invalid signature'; + return; } - - // 业务逻辑处理 - // 注意不要在业务逻辑中操作 body、type - const body = await handle(formatted, ctx); - - /* - * 假如服务器无法保证在五秒内处理并回复,可以直接回复空串。 - * 微信服务器不会对此作任何处理,并且不会发起重试。 - */ - if (body === '') { - ctx.body = ''; + var decryptedXML = CRYPTOR.decrypt(encryptMessage); + var messageWrapXml = decryptedXML.message; + if (messageWrapXml === '') { + ctx.status = 401; + ctx.body = 'Invalid signature'; return; } + var decodedXML = await parseXML(messageWrapXml); + formatted = formatMessage(decodedXML.xml); + } - var replyMessageXml = reply(body, formatted.ToUserName, formatted.FromUserName); + // 业务逻辑处理 + // 注意不要在业务逻辑中操作 body、type + const body = await handle(formatted, ctx); - if (!query.encrypt_type || query.encrypt_type === 'raw') { - ctx.body = replyMessageXml; - } else { - var wrap = {}; - wrap.encrypt = CRYPTOR.encrypt(replyMessageXml); - wrap.nonce = parseInt((Math.random() * 100000000000), 10); - wrap.timestamp = new Date().getTime(); - wrap.signature = CRYPTOR.getSignature(wrap.timestamp, wrap.nonce, wrap.encrypt); - ctx.body = encryptWrap(wrap); - } + /* + * 假如服务器无法保证在五秒内处理并回复,可以直接回复空串。 + * 微信服务器不会对此作任何处理,并且不会发起重试。 + */ + if (body === '') { + ctx.body = ''; + return; + } + + var replyMessageXml = reply(body, formatted.ToUserName, formatted.FromUserName); + + if (!query.encrypt_type || query.encrypt_type === 'raw') { + ctx.body = replyMessageXml; + } else { + var wrap = {}; + wrap.encrypt = CRYPTOR.encrypt(replyMessageXml); + wrap.nonce = parseInt((Math.random() * 100000000000), 10); + wrap.timestamp = Date.now(); + wrap.signature = CRYPTOR.getSignature(wrap.timestamp, wrap.nonce, wrap.encrypt); + ctx.body = encryptWrap(wrap); + } - ctx.type = 'application/xml'; + ctx.type = 'application/xml'; + }; + } + + middleware(handle) { + return async (ctx, next) => { + const method = ctx.method; + if (method === 'GET') { + this.get()(ctx, next); + } else if (method === 'POST') { + this.post(handle)(ctx, next); } else { ctx.status = 501; ctx.body = 'Not Implemented'; From 59d6c2736562a4b2308d0012ce400600c2b83c1f Mon Sep 17 00:00:00 2001 From: Jackson Tian Date: Mon, 8 Jan 2018 12:02:56 +0800 Subject: [PATCH 2/3] refine test case --- lib/wechat.js | 7 +++---- test/wechat.test.js | 8 ++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/wechat.js b/lib/wechat.js index 5da332c..003abf8 100644 --- a/lib/wechat.js +++ b/lib/wechat.js @@ -195,7 +195,7 @@ class Wechat { var valid = false; if (encrypted) { if (!CRYPTOR) { - throw new Error(`In encrypt mode, must configure 'encodingAESKey' and 'appid'`); + throw new Error('In encrypt mode, must configure \'encodingAESKey\' and \'appid\''); } var signature = query.msg_signature; valid = signature === CRYPTOR.getSignature(timestamp, nonce, echostr); @@ -282,7 +282,6 @@ class Wechat { // 业务逻辑处理 // 注意不要在业务逻辑中操作 body、type const body = await handle(formatted, ctx); - /* * 假如服务器无法保证在五秒内处理并回复,可以直接回复空串。 * 微信服务器不会对此作任何处理,并且不会发起重试。 @@ -314,9 +313,9 @@ class Wechat { const method = ctx.method; if (method === 'GET') { - this.get()(ctx, next); + await this.get()(ctx, next); } else if (method === 'POST') { - this.post(handle)(ctx, next); + await this.post(handle)(ctx, next); } else { ctx.status = 501; ctx.body = 'Not Implemented'; diff --git a/test/wechat.test.js b/test/wechat.test.js index 2e2fbc7..911ae43 100644 --- a/test/wechat.test.js +++ b/test/wechat.test.js @@ -69,7 +69,7 @@ describe('wechat.js', function () { it('should 200', function (done) { var q = { - timestamp: new Date().getTime(), + timestamp: Date.now(), nonce: parseInt((Math.random() * 10e10), 10) }; var s = ['some token', q.timestamp, q.nonce].sort().join(''); @@ -83,7 +83,7 @@ describe('wechat.js', function () { it('should 401 invalid signature', function (done) { var q = { - timestamp: new Date().getTime(), + timestamp: Date.now(), nonce: parseInt((Math.random() * 10e10), 10) }; q.signature = 'invalid_signature'; @@ -105,7 +105,7 @@ describe('wechat.js', function () { it('should 401 invalid signature', function (done) { var q = { - timestamp: new Date().getTime(), + timestamp: Date.now(), nonce: parseInt((Math.random() * 10e10), 10) }; q.signature = 'invalid_signature'; @@ -120,7 +120,7 @@ describe('wechat.js', function () { describe('valid other method', function () { it('should 200', function (done) { var q = { - timestamp: new Date().getTime(), + timestamp: Date.now(), nonce: parseInt((Math.random() * 10e10), 10) }; var s = ['some token', q.timestamp, q.nonce].sort().join(''); From 3078d6c824ac66a939c1bd3380b01d0d9abc3c41 Mon Sep 17 00:00:00 2001 From: Jackson Tian Date: Mon, 8 Jan 2018 12:27:00 +0800 Subject: [PATCH 3/3] remove useless dependencies --- .travis.yml | 1 + package.json | 2 - test/wechat.test.js | 135 ++++++++++++++++++++++---------------------- 3 files changed, 69 insertions(+), 69 deletions(-) diff --git a/.travis.yml b/.travis.yml index 63b5649..55f12a9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: node_js node_js: - "8" + - "9" script: make test-coveralls diff --git a/package.json b/package.json index 5c46cc8..bea59c2 100644 --- a/package.json +++ b/package.json @@ -26,13 +26,11 @@ "coveralls": "*", "expect.js": "*", "koa": "^2.0.0", - "koa-generic-session": "*", "mocha": "*", "mocha-lcov-reporter": "*", "muk": "*", "nyc": "^10.2.0", "rewire": "*", - "should": "~3.0.0", "supertest": "*", "travis-cov": "*" }, diff --git a/test/wechat.test.js b/test/wechat.test.js index 911ae43..ba828ef 100644 --- a/test/wechat.test.js +++ b/test/wechat.test.js @@ -1,12 +1,12 @@ 'use strict'; -require('should'); +const querystring = require('querystring'); const Koa = require('koa'); -const querystring = require('querystring'); +const expect = require('expect.js'); const request = require('supertest'); -const template = require('./support').template; -const tail = require('./support').tail; + +const { template, tail } = require('./support'); const wechat = require('../'); @@ -148,11 +148,12 @@ describe('wechat.js', function () { .end(function(err, res){ if (err) {return done(err);} var body = res.text.toString(); - body.should.include(''); - body.should.include(''); - body.should.match(/\d{13}<\/CreateTime>/); - body.should.include(''); - body.should.include(''); + + expect(body).to.contain(''); + expect(body).to.contain(''); + expect(body).to.match(/\d{13}<\/CreateTime>/); + expect(body).to.contain(''); + expect(body).to.contain(''); done(); }); }); @@ -172,11 +173,11 @@ describe('wechat.js', function () { .end(function(err, res){ if (err) {return done(err);} var body = res.text.toString(); - body.should.include(''); - body.should.include(''); - body.should.match(/\d{13}<\/CreateTime>/); - body.should.include(''); - body.should.include(''); + expect(body).to.contain(''); + expect(body).to.contain(''); + expect(body).to.match(/\d{13}<\/CreateTime>/); + expect(body).to.contain(''); + expect(body).to.contain(''); done(); }); }); @@ -196,15 +197,15 @@ describe('wechat.js', function () { .end(function(err, res){ if (err) {return done(err);} var body = res.text.toString(); - body.should.include(''); - body.should.include(''); - body.should.match(/\d{13}<\/CreateTime>/); - body.should.include(''); - body.should.include('1'); - body.should.include('<![CDATA[你来我家接我吧]]>'); - body.should.include(''); - body.should.include(''); - body.should.include(''); + expect(body).to.contain(''); + expect(body).to.contain(''); + expect(body).to.match(/\d{13}<\/CreateTime>/); + expect(body).to.contain(''); + expect(body).to.contain('1'); + expect(body).to.contain('<![CDATA[你来我家接我吧]]>'); + expect(body).to.contain(''); + expect(body).to.contain(''); + expect(body).to.contain(''); done(); }); }); @@ -224,16 +225,16 @@ describe('wechat.js', function () { .end(function(err, res){ if (err) {return done(err);} var body = res.text.toString(); - body.should.include(''); - body.should.include(''); - body.should.match(/\d{13}<\/CreateTime>/); - body.should.include(''); - body.should.include(''); - body.should.include(''); - body.should.include('<![CDATA[来段音乐吧<]]>'); - body.should.include(']]>'); - body.should.include(''); - body.should.include(''); + expect(body).to.contain(''); + expect(body).to.contain(''); + expect(body).to.match(/\d{13}<\/CreateTime>/); + expect(body).to.contain(''); + expect(body).to.contain(''); + expect(body).to.contain(''); + expect(body).to.contain('<![CDATA[来段音乐吧<]]>'); + expect(body).to.contain(']]>'); + expect(body).to.contain(''); + expect(body).to.contain(''); done(); }); }); @@ -258,11 +259,11 @@ describe('wechat.js', function () { .end(function(err, res){ if (err) {return done(err);} var body = res.text.toString(); - body.should.include(''); - body.should.include(''); - body.should.match(/\d{13}<\/CreateTime>/); - body.should.include(''); - body.should.include(''); + expect(body).to.contain(''); + expect(body).to.contain(''); + expect(body).to.match(/\d{13}<\/CreateTime>/); + expect(body).to.contain(''); + expect(body).to.contain(''); done(); }); }); @@ -284,11 +285,11 @@ describe('wechat.js', function () { .end(function(err, res){ if (err) {return done(err);} var body = res.text.toString(); - body.should.include(''); - body.should.include(''); - body.should.match(/\d{13}<\/CreateTime>/); - body.should.include(''); - body.should.include(''); + expect(body).to.contain(''); + expect(body).to.contain(''); + expect(body).to.match(/\d{13}<\/CreateTime>/); + expect(body).to.contain(''); + expect(body).to.contain(''); done(); }); }); @@ -308,10 +309,10 @@ describe('wechat.js', function () { .end(function(err, res){ if (err) {return done(err);} var body = res.text.toString(); - body.should.include(''); - body.should.include(''); - body.should.match(/\d{13}<\/CreateTime>/); - body.should.include(''); + expect(body).to.contain(''); + expect(body).to.contain(''); + expect(body).to.match(/\d{13}<\/CreateTime>/); + expect(body).to.contain(''); done(); }); }); @@ -331,11 +332,11 @@ describe('wechat.js', function () { .end(function(err, res) { if (err) {return done(err);} var body = res.text.toString(); - body.should.include(''); - body.should.include(''); - body.should.match(/\d{13}<\/CreateTime>/); - body.should.include(''); - body.should.include(''); + expect(body).to.contain(''); + expect(body).to.contain(''); + expect(body).to.match(/\d{13}<\/CreateTime>/); + expect(body).to.contain(''); + expect(body).to.contain(''); done(); }); }); @@ -355,11 +356,11 @@ describe('wechat.js', function () { .end(function(err, res){ if (err) {return done(err);} var body = res.text.toString(); - body.should.include(''); - body.should.include(''); - body.should.match(/\d{13}<\/CreateTime>/); - body.should.include(''); - body.should.include(''); + expect(body).to.contain(''); + expect(body).to.contain(''); + expect(body).to.match(/\d{13}<\/CreateTime>/); + expect(body).to.contain(''); + expect(body).to.contain(''); done(); }); }); @@ -379,16 +380,16 @@ describe('wechat.js', function () { .end(function(err, res){ if (err) {return done(err);} var body = res.text.toString(); - body.should.include(''); - body.should.include(''); - body.should.match(/\d{13}<\/CreateTime>/); - body.should.include(''); - body.should.include(''); - body.should.include(''); - body.should.include('<![CDATA[来段音乐吧<]]>'); - body.should.include(']]>'); - body.should.include(''); - body.should.include(''); + expect(body).to.contain(''); + expect(body).to.contain(''); + expect(body).to.match(/\d{13}<\/CreateTime>/); + expect(body).to.contain(''); + expect(body).to.contain(''); + expect(body).to.contain(''); + expect(body).to.contain('<![CDATA[来段音乐吧<]]>'); + expect(body).to.contain(']]>'); + expect(body).to.contain(''); + expect(body).to.contain(''); done(); }); });