3.3.4 消息交互原理分析
下面结合3.3.3节的代码来分析微信公众平台的消息交互原理。下面的代码基于微信公众平台官方示例代码修改完善而成。
1 <? php 2 /* 3 方倍工作室http://www.fangbei.org/ 4 CopyRight 2013 www.doucube.com All Rights Reserved 5 */ 6 7 define("TOKEN", "weixin"); 8 $wechatObj = new wechatCallbackapiTest(); 9 if(isset($_GET['echostr'])){ 10 $wechatObj->valid(); 11 }else{ 12 $wechatObj->responseMsg(); 13 } 14 15 class wechatCallbackapiTest 16 { 17 public function valid() 18 { 19 $echoStr = $_GET["echostr"]; 20 if($this->checkSignature()){ 21 echo $echoStr; 22 exit; 23 } 24 } 25 26 private function checkSignature() 27 { 28 $signature = $_GET["signature"]; 29 $timestamp = $_GET["timestamp"]; 30 $nonce = $_GET["nonce"]; 31 32 $token = TOKEN; 33 $tmpArr = array($token, $timestamp, $nonce); 34 sort($tmpArr); 35 $tmpStr = implode($tmpArr); 36 $tmpStr = sha1($tmpStr); 37 38 if($tmpStr == $signature){ 39 return true; 40 }else{ 41 return false; 42 } 43 } 44 45 public function responseMsg() 46 { 47 $postStr = $GLOBALS["HTTP_RAW_POST_DATA"]; 48 49 if(! empty($postStr)){ 50 $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_ NOCDATA); 51 $fromUsername = $postObj->FromUserName; 52 $toUsername = $postObj->ToUserName; 53 $keyword = trim($postObj->Content); 54 $time = time(); 55 $textTpl = "<xml> 56 <ToUserName><! [CDATA[%s]]></ToUserName> 57 <FromUserName><! [CDATA[%s]]></FromUserName> 58 <CreateTime>%s</CreateTime> 59 <MsgType><! [CDATA[%s]]></MsgType> 60 <Content><! [CDATA[%s]]></Content> 61 <FuncFlag>0</FuncFlag> 62 </xml>"; 63 if($keyword == "? " || $keyword == "? ") 64 { 65 $msgType = "text"; 66 $content = date("Y-m-d H:i:s", time()); 67 $result = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $content); 68 echo $result; 69 } 70 }else{ 71 echo ""; 72 exit; 73 } 74 } 75 } 76 ? >
首先看一下代码的结构。
第2~5行是注释部分。
第7行使用define()函数定义常量,常量名称为TOKEN,常量的值为weixin,这个值就是在启用开发模式时填写的Token。
第15~75行定义了一个类wechatCallbackapiTest,并在类中定义了3个方法valid()、checkSignature()和responseMsg()。
第8~13行为程序执行语句。第8行实例化了一个类对象。在第9行中,判断是否有GET请求有echostr变量,如果有,则执行valid()方法,否则执行responseMsg()方法。
下面分析微信消息交互流程。
提交URL和Token申请验证的时候,微信服务器将发送GET请求到填写的URL上,并且带上4个参数(signature、timestamp、nonce、echostr)。GET请求类似如下。
signature=6e35c6f3d3279338781047dbffd09426b9ecdee3&echostr=5979420653038092664&t imestamp=1392001400&nonce=1392192345
上述请求的参数说明如表3-1所示。
表3-1 请求校验参数说明
这个GET请求是包含echostr变量的,所以执行valid()方法。在该方法中,又调用了校验签名方法checkSignature()。如果签名校验为真,则原样输出变量$echoStr的值。
加密/校验流程如下。
1)将token、timestamp、nonce等3个参数进行字典序排序,见第33~34行。
2)将3个参数字符串拼接成一个字符串进行sha1加密,见第35~36行。
3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信,见第38~42行。
发送问号的时候,微信服务器也会带上前面3个参数(signature、timestamp、nonce)访问开发者设置的URL,同时还会将消息的XML数据包POST到URL上。XML格式类似如下。
<xml> <ToUserName><! [CDATA[gh_ba6050bc0be7]]></ToUserName> <FromUserName><! [CDATA[oDeOAjgSJUX10wvImSRMSwmyQAyA]]></FromUserName> <CreateTime>1392043637</CreateTime> <MsgType><! [CDATA[text]]></MsgType> <Content><! [CDATA[? ]]></Content> <MsgId>5978781895719912033</MsgId> </xml>
而消息请求不包含echostr变量,所以将执行响应消息方法responseMsg()。
响应消息方法首先接收上述原始POST数据,见第47行。
然后它将数据载入对象中,对象名为SimpleXMLElement, LIBXML_NOCDATA表示将CDATA合并为文本节点,代码中第50行实现此功能。
第51~54行取得XML类对象的值,并赋给新的变量,注意发送方变为接收方,接收方变为发送方。
第55~62行构造要回复的XML数据包。
第63行判断发送过来的关键字是不是问号。
第64~65行设置回复的消息类型为text,内容为当前年月日时分秒。
第66~67行封装回复的XML数据包,并且向微信服务器输出。XML格式如下。
<xml> <ToUserName><! [CDATA[oDeOAjgSJUX10wvImSRMSwmyQAyA]]></ToUserName> <FromUserName><! [CDATA[gh_ba6050bc0be7]]></FromUserName> <CreateTime>1392043638</CreateTime> <MsgType><! [CDATA[text]]></MsgType> <Content><! [CDATA[2014-01-05 11:43:23]]></Content> </xml>
这样用户就会收到回复的消息,效果如图3-26所示。