這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
今年我涉獵的領域有些“廣泛”,並且有那麼一點“跳躍”:從上半年的終端(遊戲)開發到下半年golang、docker以及目前將要提及的公眾平台 介面開發,似乎有些遠離了老本行C以及技術管理的內容。但在這個轉型以及創新驅動的時代,這顯然是順勢而為。尋求與新興領域的主動接軌,在實打實的實踐 中,擴大了自己的視野,並可以進一步甄別發現適合自己的領域。
移動互連網時代,平台一枝獨秀,是社交領域的巨人,但其誕生也才不到4年。平台的發展前景十分廣闊,企鵝公司將其打造為人與人、人與物、物與物的統一、萬能入口之雄心不變,因此圍繞平台廣大開發人員依舊有諸多機會。
公眾平台介面應該算是平台首批對外開放的介面吧。公眾平台相對成熟,但其業務模式依舊在演化和創新。公眾平台介面的開發並非不難,上手幾個月就可 以寫成一本諸如“公眾平台應用開發實踐”的事情就發生在你我眼前,因此這裡後續有關公眾平台介面開發的文章也都是一些入門級的,我個人也是邊學 習,邊實踐,邊記錄,邊分享,就像上半年寫Cocos2d-x文章那樣。
一、公眾號申請(可選)
本著“再小的個體,也有自己的品牌”的公眾平台產品哲學,只要你是合法自然人類,你就可以到https://mp.weixin.qq.com/上申請一個公眾號,一般對於個體而言,只能申請訂閱號。
對於具有開發能力的訂閱號擁有者,你可以在訂閱號的“開發人員中心”,啟用開發人員帳號。並且“一旦啟用並設定伺服器配置後,使用者發給公眾號的訊息以及開發人員需要的事件推送,將被轉寄到該URL中”。
不過此時即便你填寫相關資訊並提交,你也不會通過驗證。這正是本篇要告訴你的事情,如何寫程式實現公眾平台的接入驗證,後續道來。
二、測試號申請(可選)
正式的訂閱號申請有些繁瑣,需要提交個人資訊,需要審核,不會立即生效。並且未認證的訂閱號所能使用的功能介面有限(只能使用普通訊息介面),而認證又需 要一筆費用(現價300rmb/次)。對於學習者而言,也許真的沒有必要。於是我們在學習開發的過程中可以申請測試號來替代真正的公眾號。
測試號是一種體驗帳號,有效期間一年,具有各種功能介面體驗許可權。測試號可以在http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login下申請,申請時有一個類似公眾號開發人員配置的頁面需要你填寫伺服器配置。同樣,你需要進行接入驗證(後續道來)。
一旦申請成功,可以用終端app掃一掃測試號的QR,關注平台測試號,用於後續平台介面開發測試。
以上公眾號和測試號二選一。
三、公眾號伺服器
為何需要公眾號伺服器,這就要談及公眾平台的架構了。
很多人覺得公眾平台的業務模式有些類似於若干年前火爆一時的簡訊增值業務模式-SP/CP模式:
【終端使用者】 <—-簡訊—> 【行動電信業者移動增值業務網關】 <—-> 【SP/CP伺服器】
公眾號時代,這個業務模式變成了:
【終端使用者】 <—-訊息—> 【公眾平台】 <—–> 【公眾號伺服器】
簡訊變成了,SP/CP變成了公眾號。公眾平台將終端使用者發給公眾號的資訊轉寄至公眾號配置的公眾號伺服器URL,公眾號伺服器做業務處理後,將響應資訊通過公眾平台再發給終端使用者。因此我們需要實現公眾號商務邏輯的公眾號伺服器。
本文標題裡所說的“接入驗證”,指的就是公眾平台對公眾號伺服器提供服務的URL有效性的驗證。我們在填寫開發人員中心的“介面配置信 息”並提交時,公眾平台會向配置的公眾號伺服器的URL發送驗證Request,只有公眾號服務存在,且按要求返回包含特定資訊的Response, 我們才能真正通過公眾平台的驗證,“介面配置資訊”才真正生效。
因此我們需要一台放在公網的主機。如果採用Golang開發公眾號服務的話,這樣的主機只能是獨立的VPS,像國內新浪提供的app engine主機不能運行Golang,無法滿足要求(當然如果你使用其他語言開發的話,比如PHP,那麼可用的主機範圍就很廣泛了)。
這裡建議申請一個亞馬遜免費EC2主機(t2.micro型,免費一年,學習夠用)用作學習測試使用或者購買像Linode或DigitalOcean的VPS。關於如何申請亞馬遜主機可以諮詢Google和度娘,這裡不贅述。
注意:Amazon EC2執行個體預設採用的時動態IP,instance重啟後IP會發生變化。因此可申請分配一個Elastic IP,並綁定在你的EC2執行個體上,目前綁定instance的Elastic IP是免費的,這個IP在instance重啟後不會變更。當你EC2主機到期後,記得釋放這個IP,否則就收費了。
四、接入驗證邏輯
前面提到過,無論是公眾號還是測試號,當你提交配置URL時會收到提交失敗的資訊,這是公眾平台接入驗證失敗所致。在公眾平台開發人員文檔中,關於URL驗證邏輯如下:
開發人員提交資訊(包括URL、Token)後,伺服器將發送Http Get請求到填寫的URL上,GET請求攜帶四個參數:signature、timestamp、nonce和echostr。公眾號服務程式應該按如下要求進行接入驗證:
1. 將token、timestamp、nonce三個參數進行字典序排序
2. 將三個參數字串拼接成一個字串進行sha1加密
3. 將加密後獲得的字串與signature對比,如果一致,說明該請求來源於
4. 如果請求來自於,則原樣返回echostr參數內容
以上完成後,接入驗證就會生效,開發人員配置提交就會成功。
列出Http抓包分析後的文本,理解起來就更容易些:
伺服器發出的驗證Request如下:
GET /?signature=d01007dcff994c555bc51d22e154956ccdc61ec5×tamp=1418970951&nonce=484765335&echostr=qwe1235 HTTP/1.0\r\n
User-Agent: Mozilla/4.0\r\n
Accept: */*\r\n
Host: wechat.tonybai.com\r\n
Pragma: no-cache\r\n
Content-Length: 0\r\n
應答返回如下:
HTTP/1.0 200 OK\r\n
Date: Fri, 19 Dec 2014 06:35:59 GMT\r\n
Content-Length: nn\r\n
Content-Type: text/plain; charset=utf-8\r\n
qwe1235
五、參考實現
環境:AWS t2.micro ubuntu 14.04 x86_64 Server
go 1.4
Go語言標準庫提供了一個強大的http server,我們直接利用這個server來處理平台的Url驗證請求。另外平台發給公眾平台伺服器的http request都是請求到"/"下的,這樣我們的service無需設定太多http route。
//urlvalidation.go
package main
import (
"crypto/sha1"
"fmt"
"io"
"log"
"net/http"
"sort"
"strings"
)
const (
token = "wechat4go"
)
func makeSignature(timestamp, nonce string) string {
sl := []string{token, timestamp, nonce}
sort.Strings(sl)
s := sha1.New()
io.WriteString(s, strings.Join(sl, ""))
return fmt.Sprintf("%x", s.Sum(nil))
}
func validateUrl(w http.ResponseWriter, r *http.Request) bool {
timestamp := strings.Join(r.Form["timestamp"], "")
nonce := strings.Join(r.Form["nonce"], "")
signatureGen := makeSignature(timestamp, nonce)
signatureIn := strings.Join(r.Form["signature"], "")
if signatureGen != signatureIn {
return false
}
echostr := strings.Join(r.Form["echostr"], "")
fmt.Fprintf(w, echostr)
return true
}
func procRequest(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
if !validateUrl(w, r) {
log.Println("Wechat Service: this http request is not from Wechat platform!")
return
}
log.Println("Wechat Service: validateUrl Ok!")
}
func main() {
log.Println("Wechat Service: Start!")
http.HandleFunc("/", procRequest)
err := http.ListenAndServe(":80", nil)
if err != nil {
log.Fatal("Wechat Service: ListenAndServe failed, ", err)
}
log.Println("Wechat Service: Stop!")
}
編譯這個go源碼,執行urlvalidation。
$> urlvalidation
2014/12/18 17:48:10 Wechat Service: Start!
2014/12/18 17:48:10 Wechat Service: ListenAndServe failed, listen tcp :80: bind: permission denied
程式提示沒有許可權綁定80連接埠。80連接埠只有管理員權限才能綁定,因此我們需要通過sudo方式執行validation。
$ sudo ./urlvalidation
2014/12/18 09:56:29 Wechat Service: Start!
接下來我們回到訂閱號開發人員中心配置頁面或測試號伺服器配置頁面,點擊提交。在我們的公眾號伺服器後台可以看到如下日誌:
2014/12/18 09:56:52 Wechat Service: validateUrl Ok!
同時你的提交也會顯示成功,Url已經驗證通過,你將正式成為開發人員。
如果我們隨意構造一個http get 請求發給validate程式,比如:
curl -s http://wechat.tonybai.com(比如我的URL為http://wechat.tonybai.com)
那麼我們將看到validation輸出如下錯誤記錄檔:
2014/12/18 10:02:07 Wechat Service: this http request is not from Wechat platform!
以上源碼檔案在這裡可以下載。
處於安全考慮,後續訂閱號平台均需要對收到的http request進行驗證,以確保請求來源於公眾平台。
2014, bigwhite. 著作權.