《軟件工程》實訓指導書
微信實驗六、微信PHP後臺驗證、接收和發送消息及源碼下載
(>>>>在公衆號中輸入文章最後彩蛋即可獲取源代碼)
開源項目:https://github.com/chenxhjeo,個人博客:http://blog.csdn.net/u013487761
技術QQ羣名稱:豆豆諮詢,羣號:625686304
微信公衆號名稱:豆豆諮詢,微信公衆號:douAsk
初建日期:2017.04.08
一、實驗目的
1、掌握微信開發PHP後臺驗證、接收和發送消息。
二、實驗內容
1、驗證代碼解析。
2、接收和發送消息解析。
三、實驗步驟及過程
只有當微信服務器驗證了應用服務器後臺PHP的有效性,才能夠嚮應用服務器發送消息和接收消息。每個用戶針對每個公衆號會產生一個安全的OpenID。公衆號主要通過公衆號消息會話和公衆號內網頁來爲用戶提供服務的,本次實驗主要介紹公衆號消息會話中的被動回覆消息。
被動回覆消息:在用戶給公衆號發消息後,微信服務器會將消息發到開發者預先在開發者中心設置的服務器地址(開發者需要進行消息真實性驗證),公衆號可以在5秒內做出回覆,可以回覆一個消息,也可以回覆命令告訴微信服務器這條消息暫不回覆。
1、驗證消息是否來自微信服務器。
1)參數說明
開發者提交信息後,微信服務器將發送GET請求到填寫的服務器地址URL上,GET請求攜帶參數如下所示:
l signature:微信加密簽名,signature結合了開發者填寫的token參數和請求中的timestamp參數、nonce參數。
l timestamp:時間戳
l nonce:隨機數
l echostr:隨機字符串
2)驗證過程
開發者通過檢驗signature對請求進行校驗。若確認此次GET請求來自微信服務器,請原樣返回echostr參數內容,則接入生效,成爲開發者成功,否則接入失敗。加密/校驗流程如下:
a. 將token、timestamp、nonce三個參數進行字典序排序;
b. 將三個參數字符串拼接成一個字符串進行sha1加密;
c. 開發者獲得加密後的字符串可與signature對比,標識該請求來源於微信。
3)函數代碼說明
a. checkSignature()函數
//名稱:checkSignature()
//功能:驗證signature與加密數據(timestamp、nonce、TOKEN)是否一致
//返回:true:驗證通過;false:驗證失敗
private function checkSignature()
{
$signature = $_GET["signature"];//從微信服務器得到signature
$timestamp = $_GET["timestamp"];//從微信服務器得到timestamp
$nonce = $_GET["nonce"]; //從微信服務器得到nonce
$token = TOKEN; //從微信管理員設置的TOKEN
$tmpArr = array($token, $timestamp, $nonce);//建立數組tmpArr
sort($tmpArr);//將token、timestamp、nonce三個參數進行字典序排序;
$tmpStr = implode($tmpArr); //將數組的內容連接成一個字符串
$tmpStr = sha1($tmpStr); //將字符串進行sha1加密;
if($tmpStr == $signature){ //驗證字符串,即signature與tmpStr對比
return true;
}else{
return false;
}
}
b. valid()函數
//名稱:valid()
//功能:驗證微信服務器發送的消息,併發送給$echoStr
//返回:從微信服務器發送過來的echostr
public function valid()
{
$echoStr = $_GET["echostr"];//從微信服務器得到echostr
if($this->checkSignature()){
echo $echoStr;
exit;
}
}
c. 驗證過程
header('Content-type:text');
define("TOKEN", "weixin"); //與服務器設置的TOKEN要一致
$wechatObj = new wechatCallbackapiTest();
if (!isset($_GET['echostr'])) { //判斷echostr是否爲空
$wechatObj->responseMsg();
}else{
$wechatObj->valid();
}
驗證通過後,用戶每次向公衆號發送消息、或者產生自定義菜單點擊事件時,開發者填寫的服務器配置URL將得到微信服務器推送過來的消息和事件,然後開發者可以依據自身業務邏輯進行響應,例如回覆消息等。
2、接收消息。
responseMsg()處理接收到的消息,然後根據消息類型分別回發消息,相關函數如下所示。
1)主函數:responseMsg()函數
//名稱:responseMsg()
//功能:根據接收的消息類型(文本、事件等),分別回覆消息
//返回:返回消息結果
public functionresponseMsg()
{
$postStr = $GLOBALS["HTTP_RAW_POST_DATA"];//用$GLOBALS['HTTP_RAW_POST_DATA']來接收數據
if (!empty($postStr)){
$this->logger("R".$postStr); //記錄讀取的信息
$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);//解析xml數據到object
$RX_TYPE = trim($postObj->MsgType);//接收消息類型text/event
switch ($RX_TYPE)
{
case "event": //事件
$result = $this->receiveEvent($postObj);
break;
case "text": //文本
$result = $this->receiveText($postObj);
break;
case "image": //圖片
$result = $this->receiveImage($postObj);
break;
case "voice": //語音
$result = $this->receiveVoice($postObj);
break;
case "video": //視頻
$result = $this->receiveVideo($postObj);
break;
case "shortvideo": //小視頻
$result = $this->receiveShortVideo($postObj);
break;
case "location": //位置
$result = $this->receiveLocation($postObj);
break;
case "link": //鏈接
$result = $this->receiveLink($postObj);
break;
}
$this->logger("T".$result); //記錄發送的信息
echo $result;
}else {
echo "";
exit;
}
}
2)其他private函數
a. receiveEvent ()函數
//名稱:receiveEvent()
//功能:根據事務類型,構造回發消息
//返回:回發消息
private functionreceiveEvent($object)
{
$content = "";
if($object->Event == "subscribe"){
//1.關注公衆號;2. 掃描帶參數二維碼事件(用戶未關注時,進行關注後的事件推送)
if(var_dump(property_exists('$object', 'EventKey'))){
//2. 掃描帶參數二維碼事件(用戶未關注時,進行關注後的事件推送)
$content = "未關注時掃描二維碼關注事件,".strval($object->EventKey).strval($object->Ticket);
} else {
//1.關注公衆號
$content = "歡迎關注豆豆諮詢";
}
} else if($object->Event == "SCAN"){
//掃描帶參數二維碼事件(掃描帶參數二維碼事件,用戶已關注時的事件推送)
$content = "關注時掃描二維碼關注事件,".strval($object->EventKey).strval($object->Ticket);
} else if($object->Event == "unsubscribe"){
//取消關注公衆號
$content = "取消關注";
} else if($object->Event == "CLICK"){
//自定義菜單:用戶點擊自定義菜單後,微信會把點擊事件推送給開發者,注意,點擊菜單彈出子菜單,不會產生上報。
$content = "點擊菜單拉取消息時的事件推送:".strval($object->EventKey);
} else if($object->Event == "LOCATION"){
//上報地理位置事件:維度、經度、精度
$content = "上報地理位置:".strval($object->Latitude).",".strval($object->Longitude).",".strval($object->Precision);//
} else if($object->Event == "VIEW"){
//點擊菜單跳轉鏈接時的事件推送
$content = "點擊菜單跳轉鏈接時的事件推送:".strval($object->EventKey);
}
$result = $this->transmitText($object, $content);
return $result;
}
b. receiveText ()函數
//名稱:receiveText()
//功能:接收文本消息
//返回:回發消息
private functionreceiveText($object)
{
$keyword = trim($object->Content);
$content = date("Y-m-d H:i:s",time())."\n技術支持'豆豆諮詢'微信公衆號(douAsk)和qq羣號(625686304):";
$content = $content."\n您發送的消息是".$keyword;
if(is_array($content)){
if (isset($content[0]['PicUrl'])){
$result = $this->transmitNews($object,$content);
}else if (isset($content['MusicUrl'])){
$result = $this->transmitMusic($object,$content);
}
}else{
$result = $this->transmitText($object,$content);
}
return $result;
}
c. receiveImage ()函數
//名稱:receiveImage()
//功能:接收圖片消息
//返回:回發消息
private functionreceiveImage($object)
{
$keyword = trim($object->Content);
$content = date("Y-m-d H:i:s",time())."\n技術支持'豆豆諮詢'微信公衆號(douAsk)和qq羣號(625686304):";
$content = $content."\n您發送的消息PicUrl:".$object->PicUrl."MediaId:".$object->MediaId;
if(is_array($content)){
if (isset($content[0]['PicUrl'])){
$result = $this->transmitNews($object,$content);
}else if (isset($content['MusicUrl'])){
$result = $this->transmitMusic($object,$content);
}
}else{
$result = $this->transmitText($object,$content);
}
return $result;
}
d. receiveVoice ()函數
//名稱:receiveVoice()
//功能:接收語音消息
//返回:回發消息
private functionreceiveVoice($object)
{
$keyword = trim($object->Content);
$content = date("Y-m-d H:i:s",time())."\n技術支持'豆豆諮詢'微信公衆號(douAsk)和qq羣號(625686304):";
$content = $content."\n您發送的消息Format:".$object->Format."MediaId:".$object->MediaId;
if(is_array($content)){
if (isset($content[0]['PicUrl'])){
$result = $this->transmitNews($object,$content);
}else if (isset($content['MusicUrl'])){
$result = $this->transmitMusic($object,$content);
}
}else{
$result = $this->transmitText($object,$content);
}
return $result;
}
d. receiveVideo ()函數
//名稱:receiveVideo()
//功能:接收視頻消息
//返回:回發消息
private functionreceiveVideo($object)
{
$keyword = trim($object->Content);
$content = date("Y-m-d H:i:s",time())."\n技術支持'豆豆諮詢'微信公衆號(douAsk)和qq羣號(625686304):";
$content = $content."\n您發送的消息ThumbMediaId:".$object->ThumbMediaId."MediaId:".$object->MediaId;
if(is_array($content)){
if (isset($content[0]['PicUrl'])){
$result = $this->transmitNews($object,$content);
}else if (isset($content['MusicUrl'])){
$result = $this->transmitMusic($object,$content);
}
}else{
$result = $this->transmitText($object,$content);
}
return $result;
}
e. receiveShortVideo ()函數
//名稱:receiveShortVideo()
//功能:接收小視頻消息
//返回:回發消息
private functionreceiveShortVideo($object)
{
$keyword = trim($object->Content);
$content = date("Y-m-d H:i:s",time())."\n技術支持'豆豆諮詢'微信公衆號(douAsk)和qq羣號(625686304):";
$content = $content."\n您發送的消息ThumbMediaId:".$object->ThumbMediaId."MediaId:".$object->MediaId;
if(is_array($content)){
if (isset($content[0]['PicUrl'])){
$result = $this->transmitNews($object,$content);
}else if (isset($content['MusicUrl'])){
$result = $this->transmitMusic($object,$content);
}
}else{
$result = $this->transmitText($object,$content);
}
return $result;
}
f. receiveLocation ()函數
//名稱:receiveLocation()
//功能:接收地理位置消息
//返回:回發消息
private functionreceiveLocation($object)
{
$keyword = trim($object->Content);
$content = date("Y-m-d H:i:s",time())."\n技術支持'豆豆諮詢'微信公衆號(douAsk)和qq羣號(625686304):";
$content = $content."\n您發送的消息Location_X:".$object->Location_X."Location_Y:".$object->Location_Y;
$content = $content."\n您發送的消息Scale:".$object->Scale."Label:".$object->Label;
if(is_array($content)){
if (isset($content[0]['PicUrl'])){
$result = $this->transmitNews($object,$content);
}else if (isset($content['MusicUrl'])){
$result = $this->transmitMusic($object,$content);
}
}else{
$result = $this->transmitText($object,$content);
}
return $result;
}
g. receiveLink ()函數
//名稱:receiveLink()
//功能:接收鏈接消息
//返回:回發消息
private functionreceiveLink($object)
{
$keyword = trim($object->Content);
$content = date("Y-m-d H:i:s",time())."\n技術支持'豆豆諮詢'微信公衆號(douAsk)和qq羣號(625686304):";
$content = $content."\n您發送的消息Title:".$object->Title."Description:".$object->Description;
$content = $content."\n您發送的消息Url:".$object->Url;
if(is_array($content)){
if (isset($content[0]['PicUrl'])){
$result = $this->transmitNews($object,$content);
}else if (isset($content['MusicUrl'])){
$result = $this->transmitMusic($object,$content);
}
}else{
$result = $this->transmitText($object,$content);
}
return $result;
}
f. transmitText ()函數
//名稱:transmitText()
//功能:發送文本消息
//返回:回發消息
private functiontransmitText($object, $content)
{
$textTpl = "<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[%s]]></Content>
</xml>";
$result = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time(), $content);
return $result;
}
h. transmitText ()函數
//名稱:transmitNews()
//功能:回覆圖文消息
//返回:圖文消息
private functiontransmitNews($object, $arr_item)
{
if(!is_array($arr_item))
return;
$itemTpl = " <item>
<Title><![CDATA[%s]]></Title>
<Description><![CDATA[%s]]></Description>
<PicUrl><![CDATA[%s]]></PicUrl>
<Url><![CDATA[%s]]></Url>
</item>
";
$item_str = "";
foreach ($arr_item as $item)
$item_str .= sprintf($itemTpl, $item['Title'], $item['Description'], $item['PicUrl'], $item['Url']);
$newsTpl = "<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[news]]></MsgType>
<Content><![CDATA[]]></Content>
<ArticleCount>%s</ArticleCount>
<Articles>
$item_str</Articles>
</xml>";
$result = sprintf($newsTpl, $object->FromUserName, $object->ToUserName, time(), count($arr_item));
return $result;
}
i. transmitText ()函數
//名稱:transmitMusic()
//功能:回覆音樂消息
//返回:音樂消息
private functiontransmitMusic($object, $musicArray)
{
$itemTpl = "<Music>
<Title><![CDATA[%s]]></Title>
<Description><![CDATA[%s]]></Description>
<MusicUrl><![CDATA[%s]]></MusicUrl>
<HQMusicUrl><![CDATA[%s]]></HQMusicUrl>
</Music>";
$item_str = sprintf($itemTpl, $musicArray['Title'], $musicArray['Description'], $musicArray['MusicUrl'], $musicArray['HQMusicUrl']);
$textTpl = "<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[music]]></MsgType>
$item_str
</xml>";
$result = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time());
return $result;
}
j. transmitText ()函數
//名稱:logger()
//功能:記錄日誌
//返回:
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 = 10000;
$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('H:i:s')." ".$log_content."\r\n", FILE_APPEND);
}
}
四、技術服務
彩蛋號:1202。