基于koa框架接入微信公众号实现自动回复消息的功能
# 准备工作
- 注册一个服务号或者申请 (opens new window)一个测试公众号
- 一台服务器或者本地使用内网穿透软件(ngrok、花生壳、lanproxy...)
# 公众号接入配置
在公众号管理后台填写接口配置信息。
开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败
加密/校验流程如下:
- 将token、timestamp、nonce三个参数进行字典序排序
- 将三个参数字符串拼接成一个字符串进行sha1加密
- 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
# 服务号配置
在公众平台官网的开发-基本设置页面,勾选协议成为开发者,点击“修改配置”按钮,填写服务器地址(URL)、Token和EncodingAESKey,其中URL是开发者用来接收微信消息和事件的接口URL。Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)。EncodingAESKey由开发者手动填写或随机生成,将用作消息体加解密密钥
# 测试号配置
# 主要代码
// 验证消息的确来自微信服务器
router.get('/', async (ctx, next) => {
let signature = ctx.query.signature,
timestamp = ctx.query.timestamp,
nonce = ctx.query.nonce,
token = '123456'
let str = [token, timestamp, nonce].sort().join('')
let result = sha1(str)
let rt
if (result === signature) {
rt = ctx.query.echostr
} else {
rt = {
code: -1,
msg: 'fail',
}
}
ctx.body = rt
await next()
})
router.post('/', async (ctx, next) => {
var data = await getRawBody(ctx.req, {
length: ctx.length,
limit: '1mb',
encoding: ctx.charset,
})
const xml = await parseXMLAsync(data)
const createTime = Date.parse(new Date())
const msgType = xml.xml.MsgType[0]
const toUserName = xml.xml.ToUserName[0]
const toFromName = xml.xml.FromUserName[0]
const event = xml.xml.Event ? xml.xml.Event[0] : ''
if (msgType == 'event' && event == 'subscribe') {
//关注后
ctx.body = `<xml>
<ToUserName><![CDATA[${toFromName}]]></ToUserName>
<FromUserName><![CDATA[${toUserName}]]></FromUserName>
<CreateTime>${createTime}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[欢迎关注]]></Content>
</xml>`
} else {
//其他情况
ctx.body = `<xml>
<ToUserName><![CDATA[${toFromName}]]></ToUserName>
<FromUserName><![CDATA[${toUserName}]]></FromUserName>
<CreateTime>${createTime}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你说啥?]]></Content>
</xml>`
}
})
function parseXMLAsync(xml) {
return new Promise((resolve, reject) => {
xml2js.parseString(xml, { trim: true }, function (err, content) {
if (err) {
reject(err)
}
resolve(content)
})
})
}
# 扫码体验
测试号二维码