1.進入微信官方文檔(公衆號);html
2.接口測試號申請(微信公衆號的註冊有必定門檻) node
3.測試帳號管理 git
1.編寫啓動項文件github
// app-1.js
/** * @建立碼農: pr * @建立日期: 2019-12-26 19:47:02 * @最近更新: pr * @更新時間: 2019-12-26 19:47:02 * @文件描述: 啓動項文件 */
const Koa = require('koa');
const sha1 = require('sha1');
// 配置文件
const config = {
wechat: {
appID: 'wxb284d7a53fa2da16',
appSecret: '24af419d8f6c997b5582fd46eafb377c',
token: 'ruizhengyunpr840690384'
}
};
const PORT = 1989;
const app = new Koa();
// 中間件
app.use(function*(next) {
console.log('query', this.query);
const token = config.wechat.token;
const signature = this.query.signature;
const nonce = this.query.nonce;
const timestamp = this.query.timestamp;
const echostr = this.query.echostr;
const str = [token, timestamp, nonce].sort().join('');
const sha = sha1(str);
if (sha === signature) {
this.body = echostr + '';
} else {
this.body = 'wrong';
}
});
app.listen(PORT);
console.log(`正在監聽:${PORT}`);
複製代碼
2.執行啓動項編程
node --harmony app-1.js
複製代碼
3.ngrok 啓動json
./ngrok http 1989
複製代碼
4.拷貝 ngrok 產生的 https 地址到微信公衆號 > 接口配置信息 > URL 中,而後點擊 提交
,結果以下api
微信 - token、timestamp、nonce - 字典排序 - sha1 加密 - (r === signature) - echostrbash
1.編寫啓動項文件微信
// app-2.js
/** * @建立碼農: pr * @建立日期: 2019-12-27 19:47:02 * @最近更新: pr * @更新時間: 2019-12-27 19:47:02 * @文件描述: 啓動項文件,版本二 */
const Koa = require('koa');
const weChatMiddleWare = require('./app-2/weChat');
// 配置文件
const config = {
wechat: {
appID: 'wxb284d7a53fa2da16',
appSecret: '24af419d8f6c997b5582fd46eafb377c',
token: 'ruizhengyunpr840690384'
}
};
const PORT = 1989;
const app = new Koa();
// 中間件
app.use(weChatMiddleWare(config.wechat));
app.listen(PORT);
console.log(`正在監聽:${PORT}`);
複製代碼
2.編寫中間件網絡
// app-2/weChat.js
/** * @建立碼農: pr * @建立日期: 2019-12-27 19:47:02 * @最近更新: pr * @更新時間: 2019-12-27 19:47:02 * @文件描述: 公衆號校驗中間件 */
const sha1 = require('sha1');
// 中間件
module.exports = function(opts) {
return function*(next) {
console.log('query', this.query);
const token = opts.token;
const signature = this.query.signature;
const nonce = this.query.nonce;
const timestamp = this.query.timestamp;
const echostr = this.query.echostr;
const str = [token, timestamp, nonce].sort().join('');
const sha = sha1(str);
if (sha === signature) {
this.body = echostr + '';
} else {
this.body = 'wrong';
}
};
};
複製代碼
3.執行啓動項、ngrok 啓動、拷貝 ngrok 產生的 https 地址
1.啓動項改寫,這塊要建個 /app-3/config/access-token.txt
// app-3.js
/** * @建立碼農: pr * @建立日期: 2019-12-28 19:47:02 * @最近更新: pr * @更新時間: 2019-12-28 19:47:02 * @文件描述: 啓動項文件,版本三 */
const Koa = require('koa');
const path = require('path');
const util = require('./app-3/util');
const weChatMiddleWare = require('./app-3/weChat');
const wechat_file = path.join(__dirname, './app-3/config/access-token.txt');
// 配置文件
const config = {
wechat: {
appID: 'wxb284d7a53fa2da16',
appSecret: '24af419d8f6c997b5582fd46eafb377c',
token: 'ruizhengyunpr840690384',
getAccessToken: () => {
return util.readFileAsync(wechat_file);
},
saveAccessToken: data => {
data = JSON.stringify(data);
return util.writeFileAsync(wechat_file, data);
}
}
};
const PORT = 1989;
const app = new Koa();
// 中間件
app.use(weChatMiddleWare(config.wechat));
app.listen(PORT);
console.log(`正在監聽:${PORT}`);
複製代碼
2.編寫中間件
// app-3/weChat.js
/** * @建立碼農: pr * @建立日期: 2019-12-28 19:47:02 * @最近更新: pr * @更新時間: 2019-12-28 19:47:02 * @文件描述: 公衆號校驗中間件 */
// 依賴包引入
const sha1 = require('sha1');
const Promise = require('bluebird');
const request = Promise.promisify(require('request'));
// 參數定義
const prefix = 'https://api.weixin.qq.com/cgi-bin/';
const api = {
accessToken: `${prefix}token?grant_type=client_credential`
};
// 判斷 access_token 是否過時
function AccessTokenInfo(opts) {
const that = this;
this.appID = opts.appID;
this.appSecret = opts.appSecret;
this.getAccessToken = opts.getAccessToken;
this.saveAccessToken = opts.saveAccessToken;
that
.getAccessToken()
.then(function(data) {
try {
data = JSON.parse(data);
} catch (e) {
// 不合法就從新更新 access_token
return that.updateAccessToken();
}
if (that.isValidAccessToken(data)) {
// 判斷是否有效
// 取到合法 access_token
that.access_token = data.access_token;
that.expires_in = data.expires_in;
that.saveAccessToken(data);
} else {
// 不合法就從新更新 access_token
return that.updateAccessToken();
}
})
}
// 驗證 access_token 是否有效
AccessTokenInfo.prototype.isValidAccessToken = function(data) {
if (!data || !data.access_token || !data.expires_in) {
return false;
}
const now = new Date().getTime();
if (now < data.expires_in) {
return true;
}
return false;
};
// 更新 access_token
AccessTokenInfo.prototype.updateAccessToken = function() {
const url = `${api.accessToken}&appid=${this.appID}&secret=${this.appSecret}`;
console.log('url', url);
return new Promise((resolve, reject) => {
request({
url,
json: true
}).then(res => {
const data = res.body;
console.log('data', data);
const now = new Date().getTime();
// 縮短 20 秒(算上網絡請求時間)
const expires_in = now + (data.expires_in - 20) * 1000;
data.expires_in = expires_in;
resolve(data);
});
});
};
// 中間件
module.exports = function(opts) {
const accessTokenInfo = new AccessTokenInfo(opts);
return function*(next) {
console.log('query', this.query);
const token = opts.token;
const signature = this.query.signature;
const nonce = this.query.nonce;
const timestamp = this.query.timestamp;
const echostr = this.query.echostr;
const str = [token, timestamp, nonce].sort().join('');
const sha = sha1(str);
if (sha === signature) {
this.body = echostr + '';
} else {
this.body = 'wrong';
}
};
};
複製代碼
3.編寫工具庫
/** * @建立碼農: 芮正雲 16396@etransfar.com * @建立日期: 2019-12-28 16:58:04 * @最近更新: 芮正雲 16396@etransfar.com * @更新時間: 2019-12-28 16:58:04 * @文件描述: 工具庫 */
const fs = require('fs');
const Promise = require('bluebird');
// access_token 讀操做
exports.readFileAsync = (fpath, encoding) => {
return new Promise((resolve, reject) => {
fs.readFile(fpath, encoding, function(err, content) {
if (err) {
reject(err);
} else {
resolve(content);
}
});
});
};
// access_token 寫操做
exports.writeFileAsync = (fpath, content) => {
return new Promise((resolve, reject) => {
fs.writeFile(fpath, content, function(err, content) {
if (err) {
reject(err);
} else {
resolve();
}
});
});
};
複製代碼
4.執行啓動項、ngrok 啓動、拷貝 ngrok 產生的 https 地址
5.你會發現 /app-3/config/access-token.txt
文件生成 access_token 信息內容
關注公衆號後
1.啓動項
// app-4.js
/** * @建立碼農: pr * @建立日期: 2019-12-29 19:47:02 * @最近更新: pr * @更新時間: 2019-12-29 19:47:02 * @文件描述: 啓動項文件,版本四 */
const Koa = require('koa');
const path = require('path');
const fileAsync = require('./app-4/util/fileAsync');
const weChatMiddleWare = require('./app-4/middleWare/weChat');
const wechat_file = path.join(__dirname, './app-4/config/access-token.txt');
// 配置文件
const config = {
wechat: {
appID: 'wxb284d7a53fa2da16',
appSecret: '24af419d8f6c997b5582fd46eafb377c',
token: 'ruizhengyunpr840690384',
getAccessToken: () => {
// 讀取文件
return fileAsync.readFileAsync(wechat_file);
},
saveAccessToken: data => {
// 寫入文件
return fileAsync.writeFileAsync(wechat_file, JSON.stringify(data));
}
}
};
const PORT = 1989;
const app = new Koa();
// 中間件
app.use(weChatMiddleWare(config.wechat));
app.listen(PORT);
console.log(`正在監聽:${PORT}`);
複製代碼
2.編寫中間件
// app-4/middleWare/weChat.js
/** * @建立碼農: pr * @建立日期: 2019-12-29 19:47:02 * @最近更新: pr * @更新時間: 2019-12-29 19:47:02 * @文件描述: 公衆號校驗中間件 */
// 依賴包引入
const sha1 = require('sha1');
const rawBody = require('raw-body');
const fileAsync = require('../util/fileAsync');
const AccessTokenInfo = require('../util/AccessTokenInfo');
// 中間件
module.exports = function(opts) {
const accessTokenInfo = new AccessTokenInfo(opts);
return function*(next) {
console.log('query', this.query);
const token = opts.token;
const signature = this.query.signature;
const nonce = this.query.nonce;
const timestamp = this.query.timestamp;
const echostr = this.query.echostr;
const str = [token, timestamp, nonce].sort().join('');
const sha = sha1(str);
/** * GET 驗證開發者身份 * POST */
if (sha !== signature) {
this.body = '❌';
return false;
}
if (this.method === 'GET') {
this.body = echostr + '';
} else if (this.method === 'POST') {
// 依賴包 raw-body 能夠把 this 上的 request 對象(http 模塊中的 request 對象),拼寫它的數據,最終拿到一個 buffer 的 XML
const data = yield rawBody(this.req, {
length: this.length,
limit: '1mb',
encoding: this.charset
});
const content = yield fileAsync.parseXMLAsync(data);
const message = fileAsync.formatMessage(content.xml);
if (message.MsgType === 'event') {
if (message.Event === 'subscribe') {
const now = new Date().getTime();
this.status = 200;
this.type = 'application/xml';
// 文本模板,後面能夠把這塊業務抽離處理
this.body = `<xml> <ToUserName><![CDATA[${message.FromUserName}]]></ToUserName> <FromUserName><![CDATA[${message.ToUserName}]]></FromUserName> <CreateTime>${now}</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[${`你好,${message.FromUserName},歡迎來到本公衆號。`}]]></Content> <MsgId>1234567890123456</MsgId> </xml>`;
console.log('message', this.body);
return;
}
}
console.log('message', message);
}
};
};
複製代碼
3.編寫工具庫
// app-4/uitl/fileAsync.js
/** * @建立碼農: pr * @建立日期: 2019-12-29 16:58:04 * @最近更新: pr * @更新時間: 2019-12-29 16:58:04 * @文件描述: 工具庫 */
const fs = require('fs');
const Promise = require('bluebird');
const xml2js = require('xml2js');
// access_token 讀操做
exports.readFileAsync = (fpath, encoding) => {
return new Promise((resolve, reject) => {
fs.readFile(fpath, encoding, function(err, content) {
if (err) {
reject(err);
} else {
resolve(content);
}
});
});
};
// access_token 寫操做
exports.writeFileAsync = (fpath, content) => {
return new Promise((resolve, reject) => {
fs.writeFile(fpath, content, function(err, content) {
if (err) {
reject(err);
} else {
resolve();
}
});
});
};
// 格式化 xml 消息
function formatMessage(data) {
const message = {};
if (typeof data === 'object') {
const keys = Object.keys(data);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const item = data[key];
const len = item.length;
if (!(item instanceof Array) || len === 0) {
continue;
}
if (len === 1) {
const value = item[0];
if (typeof value === 'object') {
message[key] = formatMessage(value);
} else {
message[key] = (value || '').trim();
}
} else {
message[key] = [];
for (let i = 0; i < len; i++) {
message[key].push(formatMessage(item[i]));
}
}
}
}
return message;
}
exports.formatMessage = formatMessage;
exports.parseXMLAsync = function(xml) {
return new Promise(function(resolve, reject) {
xml2js.parseString(xml, { trim: true }, function(err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
};
複製代碼
// app-4/util/AccessTokenInfo.js
/** * @建立碼農: pr * @建立日期: 2019-12-29 19:47:02 * @最近更新: pr * @更新時間: 2019-12-29 19:47:02 * @文件描述: 公衆號校驗中間件 */
// 依賴包引入
const Promise = require('bluebird');
const request = Promise.promisify(require('request'));
// 參數定義
const prefix = 'https://api.weixin.qq.com/cgi-bin/';
const api = {
accessToken: `${prefix}token?grant_type=client_credential`
};
// 判斷 access_token 是否過時
function AccessTokenInfo(opts) {
const that = this;
this.appID = opts.appID;
this.appSecret = opts.appSecret;
this.getAccessToken = opts.getAccessToken;
this.saveAccessToken = opts.saveAccessToken;
that.getAccessToken().then(function(data) {
try {
data = JSON.parse(data);
} catch (e) {
// 不合法就從新更新 access_token
return that.updateAccessToken();
}
if (that.isValidAccessToken(data)) {
//判斷是否有效
// Promise.resolve(data);
// 取到合法 access_token
that.access_token = data.access_token;
that.expires_in = data.expires_in;
that.saveAccessToken(data);
} else {
// 不合法就從新更新 access_token
return that.updateAccessToken();
}
});
// .then(function(data) {
// // 取到合法 access_token
// that.access_token = data.access_token;
// that.expires_in = data.expires_in;
// that.saveAccessToken(data);
// });
}
// 驗證 access_token 是否有效
AccessTokenInfo.prototype.isValidAccessToken = function(data) {
if (!data || !data.access_token || !data.expires_in) {
return false;
}
const now = new Date().getTime();
if (now < data.expires_in) {
return true;
}
return false;
};
// 更新 access_token
AccessTokenInfo.prototype.updateAccessToken = function() {
const url = `${api.accessToken}&appid=${this.appID}&secret=${this.appSecret}`;
console.log('url', url);
return new Promise((resolve, reject) => {
request({
url,
json: true
}).then(res => {
const data = res.body;
const now = new Date().getTime();
// 縮短 20 秒(算上網絡請求時間)
const expires_in = now + (data.expires_in - 20) * 1000;
data.expires_in = expires_in;
resolve(data);
});
});
};
module.exports = AccessTokenInfo;
複製代碼
5.執行啓動項、ngrok 啓動、拷貝 ngrok 產生的 https 地址
6.掃描關注公衆號