上篇文章中,我们提到微信消息加解密方式有三种,分别是明文模式、兼容模式和安全模式,选择兼容模式和安全模式,需要在公众号后台配置消息加解密秘钥EncodingAESKey。这篇文章我们来具体说一说在安全模式下微信消息加解密过程。
当用户向公众号发送消息,微信公众账号将会在URL中带上signature、timestamp、nonce、openid、encrypt_type、msg_signature等参数发送到用户服务器,例如:
http://wechat.acier.cn/index.php/Home/Index/index?signature=4fa4a9b8f704650c8b4b7b9b3ed840730fe7a6e5×tamp=1556418922&nonce=716667816&openid=ou7rj1bzsmvE8SdHhVG0v6bt5m-M&encrypt_type=aes&msg_signature=f7c0ad5059052243822c2916c7aa0e978472f97e
encrypt_type:加密类型。没有该参数或参数为raw时,表示不加密。参数为aes时,表示aes加密。
msg_signature:对消息体的签名。
同时推送如下XML消息,即一个已加密的消息:
<xml> <ToUserName><![CDATA[gh_a1535e2cabb5]]></ToUserName> <Encrypt><![CDATA[jAU5g+GnBRz7bgWBonVMdibqGwOntnh+s4MXGWVB3jzMUxvkZ1LKOx6XaHEguUGNZLIYBGU4vWFWQ0vsxuJEEd14IcUN1yuUFSQDUE76BldOeTH8dzZ6lUJwWAWCvAfy/YpuNFibR06R7/jnH97WZVa8/kHgFhw6n08SNeEh6v+EzjlNZWSvNTMkbkZZOohpsCEqmvpzjmCC/kwgnZIm2sATDaZIEHZpXcLgpMCBwk3y+NNyIjDzJThXov5hYGIafTjRjYHYolLh+LIXFR9FICu8Qbo2ax+mfdWXeC0bpVWikZk4cIn2j0uRih5rAuoqKOUOhDVvy2XbDEJpslom0z9IcQVxFhn+DlRc0UkhnmAVRoSP8ZTCnZrH4G2QKx5eTP9e3RiVeK4HUR4Gqd6f9H8tYsbuITt34gCEiZxaxkY=]]></Encrypt> </xml>
在服务器PHP代码中,我们需要获取到timestamp、nonce、encrypt_type和msg_signature,这些参数将用于对消息的加解密。
程序收到微信服务器发送过来的消息后,将进行解密。对AES对称加解密的算法,微信提供了示例代码,点此下载。这里,我将下载的示例代码放在新建的WeChat目录下,并将WeChat目录放进thinkphp的Vender目录里面,然后调用进行解密,解密之后,根据业务流程,要回复给微信服务器一个消息,这时又需要对将要回复的消息进行加密,然后发送给微信服务器。具体流程代码如下:
class IndexController extends ComController
{
//入口
public function index()
{
define("TOKEN", "acier");
define("AppID", "wx4abf8a686084d2fd");
define("EncodingAESKey", "cMhGxj3Bq1Y9vcG93ef1pCWH4AUh2E3zXpLNHiF74CI");
// $this -> traceHttp();
$this->logger(' http://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].(empty($_SERVER['QUERY_STRING'])?"":("?".$_SERVER['QUERY_STRING'])));
if (!isset($_GET['echostr'])) {
$this->responseMsg();
}else{
$this->valid();
}
}
//验证签名
public function valid()
{
$echoStr = $_GET["echostr"];
$signature = $_GET["signature"];
$timestamp = $_GET["timestamp"];
$nonce = $_GET["nonce"];
$token = TOKEN;
$tmpArr = array($token, $timestamp, $nonce);
sort($tmpArr);
$tmpStr = implode($tmpArr);
$tmpStr = sha1($tmpStr);
if($tmpStr == $signature){
echo $echoStr;
exit;
}
}
//响应消息
public function responseMsg()
{
$timestamp = $_GET['timestamp'];
$nonce = $_GET['nonce'];
$msg_signature = $_GET['msg_signature'];
$encrypt_type = (isset($_GET['encrypt_type']) && ($_GET['encrypt_type'] == 'aes')) ? "aes" : "raw";
$postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
// $postStr = file_get_contents("php://input"); //如果是PHP7,则使用本行,注释上面一行
$this->logger(" postStr \r\n".$postStr);
if (!empty($postStr)){
//解密
if ($encrypt_type == 'aes'){
Vendor('WeChat.wxBizMsgCrypt');
$pc = new \WXBizMsgCrypt(TOKEN, EncodingAESKey, AppID);
$decryptMsg = ""; //解密后的明文
$errCode = $pc->decryptMsg($msg_signature, $timestamp, $nonce, $postStr, $decryptMsg);
$postStr = $decryptMsg;
}
$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
$RX_TYPE = trim($postObj->MsgType);
//消息类型分离
switch ($RX_TYPE)
{
case "event":
$result = $this->receiveEvent($postObj);
break;
case "text":
if (strstr($postObj->Content, "第三方")){
$result = $this->relayPart3("http://discuz.comli.com/test.php".'?'.$_SERVER['QUERY_STRING'], $postStr);
}else{
$result = $this->receiveText($postObj);
}
break;
case "image":
$result = $this->receiveImage($postObj);
break;
case "location":
$result = $this->receiveLocation($postObj);
break;
case "voice":
$result = $this->receiveVoice($postObj);
break;
case "video":
$result = $this->receiveVideo($postObj);
break;
case "link":
$result = $this->receiveLink($postObj);
break;
default:
$result = "unknown msg type: ".$RX_TYPE;
break;
}
//加密
if ($encrypt_type == 'aes'){
$encryptMsg = ''; //加密后的密文
$errCode = $pc->encryptMsg($result, $timestamp, $nonce, $encryptMsg);
$result = $encryptMsg;
}
echo $result;
}else {
echo "";
exit;
}
}
//日志记录
private function logger($log_content)
{
if(isset($_SERVER['HTTP_APPNAME'])){ //SAE
sae_set_display_errors(false);
sae_debug($log_content);
sae_set_display_errors(true);
}else if($_SERVER['REMOTE_ADDR'] != "127.0.0.1"){ //LOCAL
$max_size = 500000;
$log_filename = "log.xml";
if(file_exists($log_filename) and (abs(filesize($log_filename)) > $max_size)){unlink($log_filename);}
file_put_contents($log_filename, date('Y-m-d H:i:s').$log_content."\r\n", FILE_APPEND);
}
}
}
以上就是对消息体加解密的演示流程。
原创文章转载请注明:转载自:微信公众号开发——消息体加解密
发表评论
沙发空缺中,还不快抢~