同福

开发微信公众号服务器后台接口(对接微信公众号)

介绍

介绍

福哥新开通了一个微信公众号,顺便重新整理一下微信公众号对接接口的开发文档,这里面包括了微信公众号加密解密的算法的详解,大家可以了解一下微信公众号加密解密的过程,从而可以根据这个教程可以实现任意语言的微信公众号对接开发了

教程包括开通、激活、回复普通消息,回复图文消息,关注事件、取消关注事件等等常用功能

开通

进入开发 > 基本配置 > 服务器配置,在弹出的对话框里填写URL、Token、EncodingAESKey,EncodingAESKey可以选择随机生成,加密解密方式选择安全

81f6d1e292116680.jpg

点击提交按钮会提示错误,因为我们的接口还没有开放嘛~~

激活

建立URL对应的程序,在里面写上微信公众号接口响应代码

激活过程

在微信公众号后台的服务器配置页面点击提交进行激活的时候,我们设置的接口程序会收到来自微信公众号服务器发来的请求

GET数据

收到若干通过QueryString传入的参数

Array
(
    [signature] => c477dabd7a1c46305527821149c555ae8c151deb
    [echostr] => 6116421323020820463
    [timestamp] => 1598413196
    [nonce] => 1221893586
)

Signature

接收到微信服务器发来的请求后需要验证消息是否可信,消息签名的算法比较简单,就是把微信公众号token、时间戳timestamp、备注nonce按照ASCII的顺序拼接到一起,然后使用SHA1算法得出一个字符串作为消息签名

SHA1(SORT(LIST(token, timestamp, nonce)))

响应请求

经过签名验证之后,需要告知微信服务器我们通过了签名验证,做法就是在响应体中输出微信服务器发过来的QueryString里的 echostr 参数

激活

后台接口开发好了之后,再次点击提交按钮,就可以通过了。

需要注意的是后台接口必须是部署在公网里的,局域网是不行的,因为微信服务器无法访问到局域网内的程序,如果一定要局域网部署可以通过NAT映射出去,当然IP地址需要添加网关公网IP地址

启用

第一次激活之后会发现服务器配置是未启用状态,需要点击启用按钮启用这个服务器配置

f0dd84547afe8fcf.jpg

接收用户消息

接口启用之后,用户就可以通过二维码关注我们的公众号了,用户进入公众号里面之后可以向公众号发送消息,如果用户向我们的公众号发送了消息,微信服务器就会把用户发送的消息推送到我们设置的后台接口,我们就可以在后台接口里处理用户发来的消息,从而完成响应用户的操作

公众号后台接口如果选择了安全模式,那么微信服务器推送过来的消息就会是加密过的,那么我们接收到用户发来的消息就需要进行一次解密操作,才能知道用户发送的消息的具体内容

被动回复用户消息

我们接收到了用户发送过来的消息之后,可以通过在接口的HTTP响应里输出回复消息体的方式向用户反馈结果,用户就可以在公众号里面收到我们给出的反馈内容,这种操作就称为被动回复用户消息

同样的公众号后台接口如果选择了安全模式,那么我们在接口的HTTP响应里输出的回复消息体也需要进行加密处理才能被微信服务器接正常处理

加密解密算法

微信公众号消息的加密解密算法很简单,福哥总结了一下

  • 微信公众号消息体需要使用 AES 算法进行加密解密,使用的 AES 算法的加密方法是 AES-256-CBC

  • 微信公众号消息体加密解密时候用到的私钥的算法是 base64decode(AESEncodingKey + "=")

  • 微信公众号消息体加密前解密后需要使用 PKCS7 算法进行补位和去补位

  • 加密后需要使用BASE64Encode一下

  • 解密前需要使用BASE64Decode一下

解密用户信息

用户给微信公众号发送消息之后,微信公众号的服务器接口会收到如下数据

解密过程

接下来和福哥一起了解一下微信用户发来的消息的解密过程

GET数据

收到若干通过QueryString传入的参数

Array
(
    [signature] => a26b5683ad121be160c488f681a0c6732db59aef
    [timestamp] => 1597898121
    [nonce] => 65207396
    [openid] => oNDX2ghS7EX6FdyZcbVHoc65Z18A
    [encrypt_type] => aes
    [msg_signature] => c0ae804689cfc7f6eeb20dbb9c1ead6555fbe113
)

RAW数据

收到的RAW数据是一个XML结构,里面包含若干参数

<xml>
    <ToUserName><![CDATA[gh_cce9e6da712f]]></ToUserName>
    <Encrypt><![CDATA[y0gzJf8xYGczuvg4fmbS2kIAlEfEBX4g+CjPe4bdEpIDHF1OPctsAL4LsLs/I6JSyyCEYh0UCmCcJi75av5qwRfwvdIcucqOireYr0qFXzIlBQt5XSeNDdtqM0PkhsE+QnlZ+V18rkBYxac1q31+AVfsHV07i1EmeUuHcDiABBk4K/HFNVcHcyfUxb0XJjclRanSaXEWsAIWkhatzkI0gWpgLreo9iCmyhLndqHMlNgJj8x0zVv+wLX1vu0nz3K7Dg01JQWxd6oSqDE/gX2cT5RTvhNdrw8eks9ar0QjzoqxLbTxP56IP4Ct6fYSflNEL0T7YuoXZMSsFGNThRxS9cOmJauJdLBauZUEoHqtUDkzeUhJoXteZqIHzjoyejKYT61m7ATrf1KK+43wbr33yIOmIjWINLEtLq6kCPmg5WA=]]></Encrypt>
</xml>

Encrypt

里面的Encrypt数据就是加密的原始消息体,我们通过前面介绍的解密算法把它解开后,得到一个处理过的消息体,这个处理过的消息体里面包含原始消息体XML结构的字符串,要拆解这个处理过的消息体,先截取16位随机数字符串,再截取4位字符串通过unpack(N)解开得到内容体的字符个数,接着根据这个内容体的字符个数截取定长的字符串就是完整的XML结构,剩下的字符串就是微信企业号的AppID了

(RANDOM=16bit) + (Pack(N)=4bit=Strlen(XML)) + XML + AppID

将这个AppID和我们系统的AppID比对一下,如果是一样的就证明消息是可信的

原始消息体

原始消息体的XML结构是下面这个样子的

<xml><ToUserName><![CDATA[gh_cce9e6da712f]]></ToUserName>
<FromUserName><![CDATA[oFEJ2jgS7DF6DcyZcbVHoc65S18A]]></FromUserName>
<CreateTime>1597899183</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你是福哥吗?]]></Content>
<MsgId>22876413731598615</MsgId>
</xml>

这里面有我们需要的各个参数,我们可以根据这些参数判断用户发送了什么内容给我们的公众号

  • ToUserName:这是我们微信公众号的唯一名称

  • FromUserName:这是用户的微信号针对我们的微信公众号的唯一名称

  • CreateTime:这是时间戳

  • MsgType:这是消息类型

  • Content:这是消息内容

  • MsgId:这是消息ID

加密回复消息

用户发送消息给我们的微信公众号,我们可以直接回复一个消息给用户,这种回复消息称为“被动回复消息”,这种回复消息的特点就是由用户主动发起的我们只是被动的回复消息

加密过程

被动回复消息需要用到数据加密算法,下面我们就来了解一下加密过程

原始消息体

首先我们组织原始的回复消息体,ToUserName和FromUserName就是把用户发上来的消息体里的ToUserName和FromUserName颠倒一下就可以了

<xml>
<ToUserName><![CDATA[oFEJ2jgS7DF6DcyZcbVHoc65S18A]]></ToUserName>
<FromUserName><![CDATA[gh_cce9e6da712f]]></FromUserName>
<CreateTime>1597919240</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[没错!我就是福哥!]]></Content>
</xml>

Encrypt数据

现在原始消息体有了,需要加工处理一下,先在原始消息体后面拼上微信公众号的AppID,接着在原始消息体前面拼上通过pack(N)加密的原始消息体的字符个数,最后在拼好的字符串最前面再拼上一个16位长度的随机字符串,这样处理过的消息体就准备好了

(RANDOM=16bit) + (Pack(N)=4bit=Strlen(XML)) + XML + AppID

接下来我们通过前面介绍的加密算法把它加密一下就是Encrypt数据了

MsgSignature

发出去之前需要给响应内容体携带一个消息签名用来给微信服务器去验证消息是否可信,消息签名的算法比较简单,就是把密文encrypted、微信公众号token、时间戳timestamp、备注nonce按照ASCII的顺序拼接到一起,然后使用SHA1算法得出一个字符串作为消息签名

SHA1(SORT(LIST(encrypt, token, timestamp, nonce)))

Response数据

最后把Encrypt、MsgSignature、时间戳timestamp、备注nonce放到响应内容的XML结构里,响应一个XML文档结构就完成了消息的回复操作

<xml>
<Encrypt><![CDATA[x6j4IvJrC/1jQcFW7EN14peJs/CVBU4jCUQDqaL1O+LvazeUpGXutJmW9KmZG0+IU03VbDSoHYKtGKt1oX63AKCaT626nxN2VMu2+y2n1N7Zh55b40QuA2T2iv/c/Th+kdpEZrJTZSGWgcRLQRWzQAkmO60NOhxNZrzDTlZlCCogmA2GM1P90n6mBtu2dkCpOdGZr0heDMKdlsqyNwhGBR3bD7BFtk8t90fsZA4aPtv6J9RYnsfld3i0mx2yuaT0go85jBDpUqWhX/ZRP64wqQqlWrcSXYy564gipW1zKFIyraAbUXrOgFVwkJE1EPysYOAIJGNB6LKW81UFviZYo0eb4Y5by1WULCEkxpQdCqkNEobtxXQPC89o4gFxtaiUU6lpCzPHTstwzH9/7NZFM4tj6TnSHqIJZHHcljgkWcQ=]]></Encrypt>
<MsgSignature><![CDATA[8ae6de21032f439f842be4ac51735144fb079d59]]></MsgSignature>
<TimeStamp>1597919240</TimeStamp>
<Nonce><![CDATA[712268213]]></Nonce>
</xml>

加密解密调试

微信官方提供了一个用来调试微信公众号服务器接口的在线工具,虽然第三方的工具也很多,不过想一想还有比微信官方更靠谱的吗?另外自己的appId、AESKey、token可以随便提交给第三方吗?

https://mp.weixin.qq.com/debug/

d3fac82cf3f9b595.jpg