在上篇隨筆《C#開發微信門戶及應用(19)-微信企業號的消息發送(文本、圖片、文件、語音、視頻、圖文消息等)》介紹了有關企業號的消息發送,官方特別聲明消息是不用加密發送的。但是在回調的服務器上,也就是我們網站的服務器上,微信傳過來的消息是加密的,需要我們調用類庫對消息和事件進行解密操作,由於官方的例子不全,因此摸索了不少時間,最終順利解密收到的各種消息和事件。本文主要介紹 微信企業號的消息和事件的接收處理及解密操作。
1、企業號回調模式的設置
和公眾號一樣,微信企業號如果需要進行二次開發,也是需要在后台設置好對應的回調參數,如下界面所示。
設置好這些后,檢查通過后,我們就可以在自己微信應用服務器上進行消息的收發操作了。
在回調的消息入口處,我們需要對POST數據和普通的GET數據進行分開處理,GET數據是微信自身的驗證處理,POST數據是微信消息的交互操作。
/// <summary> /// 企業號回調信息接口。統一接收並處理信息的入口。 /// </summary> public class corpapi : IHttpHandler { /// <summary> /// 處理企業號的信息 /// </summary> /// <param name="context"></param> public void ProcessRequest(HttpContext context) {
上面我們定義了一個一般應用處理程序來對消息進行處理。
然后我們分開不同的消息類型(POST、GET 方式),針對性的進行處理。
if (HttpContext.Current.Request.HttpMethod.ToUpper() == "POST") { using (Stream stream = HttpContext.Current.Request.InputStream) { Byte[] postBytes = new Byte[stream.Length]; stream.Read(postBytes, 0, (Int32)stream.Length); postString = Encoding.UTF8.GetString(postBytes); } if (!string.IsNullOrEmpty(postString)) { Execute(postString, accountInfo); } } else { Auth(accountInfo); }
2、微信回調消息的驗證
下面是微信對於回調模式,驗證URL的說明。
驗證URL有效性
當你提交以上信息時,企業號將發送GET請求到填寫的URL上,GET請求攜帶四個參數,企業在獲取時需要做urldecode處理,否則會驗證不成功。
參數 | 描述 | 是否必帶 |
---|---|---|
msg_signature | 微信加密簽名,msg_signature結合了企業填寫的token、請求中的timestamp、nonce參數、加密的消息體 | 是 |
timestamp | 時間戳 | 是 |
nonce | 隨機數 | 是 |
echostr | 加密的隨機字符串,以msg_encrypt格式提供。需要解密並返回echostr明文,解密后有random、msg_len、msg、$CorpID四個字段,其中msg即為echostr明文 | 首次校驗時必帶 |
企業通過參數msg_signature對請求進行校驗,如果確認此次GET請求來自企業號,那么企業應用對echostr參數解密並原樣返回echostr明文(不能加引號),則接入驗證生效,回調模式才能開啟。
后續回調企業時都會在請求URL中帶上以上參數(echostr除外),校驗方式與首次驗證URL一致。
根據上面的說明,我們需要獲取這些參數,然后調用微信提供的消息處理函數進行加解密處理。
在驗證URL的Auth(accountInfo);操作里面,我們可以看到核心的內容如下所示,就是獲取到這些傳遞過來的參數信息,然后交給基類處理消息的簽名內容。
#region 具體處理邏輯 string echoString = HttpContext.Current.Request.QueryString["echoStr"]; string signature = HttpContext.Current.Request.QueryString["msg_signature"];//企業號的 msg_signature string timestamp = HttpContext.Current.Request.QueryString["timestamp"]; string nonce = HttpContext.Current.Request.QueryString["nonce"]; string decryptEchoString = ""; if (new CorpBasicApi().CheckSignature(token, signature, timestamp, nonce, corpId, encodingAESKey, echoString, ref decryptEchoString)) { if (!string.IsNullOrEmpty(decryptEchoString)) { HttpContext.Current.Response.Write(decryptEchoString); HttpContext.Current.Response.End(); } } #endregion
驗證代碼部門如下所示。
/// <summary> /// 驗證企業號簽名 /// </summary> /// <param name="token">企業號配置的Token</param> /// <param name="signature">簽名內容</param> /// <param name="timestamp">時間戳</param> /// <param name="nonce">nonce參數</param> /// <param name="corpId">企業號ID標識</param> /// <param name="encodingAESKey">加密鍵</param> /// <param name="echostr">內容字符串</param> /// <param name="retEchostr">返回的字符串</param> /// <returns></returns> public bool CheckSignature(string token, string signature, string timestamp, string nonce, string corpId, string encodingAESKey, string echostr, ref string retEchostr) { WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(token, encodingAESKey, corpId); int result = wxcpt.VerifyURL(signature, timestamp, nonce, echostr, ref retEchostr); if (result != 0) { LogTextHelper.Error("ERR: VerifyURL fail, ret: " + result); return false; } return true; }
3、企業號的消息處理
上面介紹了,微信企業號對URL的驗證過程,還有另外一個消息處理過程,就是微信服務器把消息發送給我們自己的應用服務器進行處理的過程,我們應用服務器需要在收到消息后,及時進行常規回復處理。
也就是下面的代碼邏輯。
if (HttpContext.Current.Request.HttpMethod.ToUpper() == "POST") { using (Stream stream = HttpContext.Current.Request.InputStream) { Byte[] postBytes = new Byte[stream.Length]; stream.Read(postBytes, 0, (Int32)stream.Length); postString = Encoding.UTF8.GetString(postBytes); } if (!string.IsNullOrEmpty(postString)) { Execute(postString, accountInfo); } }
同樣,我們給微信服務器回應消息的時候,我們也需要獲得相應的參數,然后再行構造信息回答。
string echoString = HttpContext.Current.Request.QueryString["echoStr"]; string signature = HttpContext.Current.Request.QueryString["msg_signature"];//企業號的 msg_signature string timestamp = HttpContext.Current.Request.QueryString["timestamp"]; string nonce = HttpContext.Current.Request.QueryString["nonce"];
而另外一些參數信息,則是來源於我們企業號賬號的配置參數。
//獲取配置參數並對加解密函數初始化 string CorpToken = accountInfo.Token; string AESKey = accountInfo.EncodingAESKey; string CorpId = accountInfo.CorpID;
然后使用微信提供的消息加解密類,就可以順利對消息進行加解密的處理了。具體操作代碼如下所示。
//根據參數信息,初始化微信對應的消息加密解密類 WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(CorpToken, AESKey, CorpId); //對收到的密文進行解析處理 string sMsg = ""; // 解析之后的明文 int flag = wxcpt.DecryptMsg(signature, timestamp, nonce, postStr, ref sMsg); if (flag == 0) { //LogTextHelper.Info("記錄解密后的數據:"); //LogTextHelper.Info(sMsg);//記錄解密后的數據 CorpApiDispatch dispatch = new CorpApiDispatch(); string responseContent = dispatch.Execute(sMsg); //加密后並發送 //LogTextHelper.Info(responseContent); string encryptResponse = ""; timestamp = DateTime.Now.DateTimeToInt().ToString(); wxcpt.EncryptMsg(responseContent, timestamp, nonce, ref encryptResponse, ref signature); HttpContext.Current.Response.ContentEncoding = Encoding.UTF8; HttpContext.Current.Response.Write(encryptResponse); } else { LogTextHelper.Info("解密消息失敗!"); }
最終,我們把解密完成的消息交給對應的封裝類進行統一處理就可以了。
CorpApiDispatch dispatch = new CorpApiDispatch(); string responseContent = dispatch.Execute(sMsg);
這樣我們在企業號API的封裝,就可以只需要關注消息如何應答的邏輯就可以了,其他的不用關心。
如果對這個《C#開發微信門戶及應用》系列感興趣,可以關注我的其他文章,系列隨筆如下所示:
C#開發微信門戶及應用(23)-微信小店商品管理接口的封裝和測試
C#開發微信門戶及應用(21)-微信企業號的消息和事件的接收處理及解密
C#開發微信門戶及應用(19)-微信企業號的消息發送(文本、圖片、文件、語音、視頻、圖文消息等)
C#開發微信門戶及應用(18)-微信企業號的通訊錄管理開發之成員管理
C#開發微信門戶及應用(17)-微信企業號的通訊錄管理開發之部門管理
C#開發微信門戶及應用(15)-微信菜單增加掃一掃、發圖片、發地理位置功能
C#開發微信門戶及應用(14)-在微信菜單中采用重定向獲取用戶數據
C#開發微信門戶及應用(11)--微信菜單的多種表現方式介紹
C#開發微信門戶及應用(10)--在管理系統中同步微信用戶分組信息
C#開發微信門戶及應用(9)-微信門戶菜單管理及提交到微信服務器