最近需要接入 阿里雲 短信服務 用來發送驗證碼。由於官方並未提供Golang相應SDK,自己就嘗試實現了發送短信的接口,這裏分享給大家。
項目地址:https://github.com/panshiqu/dysms
使用示例:
if err := dysms.SendSms(accessKeyID, accessSecret, phoneNumbers, signName, templateParam, templateCode); err != nil {
log.Println("dysms.SendSms", err)
}
其實就是翻譯了 HTTP協議及簽名 這篇官方API文檔中的示例代碼,Java -> Go
若你也想實現該接口(重複造輪子),我建議你把所有字段值暫設爲官方示例給出的值,譬如時間戳設爲”2017-07-12T02:42:19Z“,這樣你就可以每步都與官方文檔中的打印結果作對比,進而發現自己代碼中的問題。
列舉我遇見的錯誤碼:
InvalidTimeStamp.Expired
時間戳錯誤,發出請求的時間和服務器接收的時間不在15分鐘內。經常出現該錯誤的原因是時區造成的,目前網關使用的時區是GMT等同於UTC。
SignatureNonceUsed
唯一隨機數重複,SignatureNonce是唯一隨機數,用於防止網絡重放攻擊。不同請求間要使用不同的隨機數值。關於網絡重放攻擊,我們可以構造一次合法的請求,打印並記錄URL,因爲Timestamp容忍15分鐘內的誤差,我們就可以在15分鐘內重複訪問該URL進而發送短信。SignatureNonce的存在終結了這種可能,服務器應該會緩存SignatureNonce值至少15分鐘,每次請求到來都會檢查是否存在相同的SignatureNonce值,若存在則請求失敗,返回該錯誤碼。上面實現的接口中用rand.Int63()
來產生SignatureNonce值,你可能擔心15分鐘內會產生重複的隨機值,其實大可不必考慮這點,因爲收到該錯誤碼會遞歸調用生成新的隨機值再次請求。
實現該接口大部分時間都在解決一個簡單的錯誤:malformed HTTP status code "HTML"
。不要高看這個錯誤,它其實就是字面意思,畸形的,難看的HTTP。之所以會遇到這個錯誤,主要因爲我會打印拼接好的URL,爲了方便我看,在fmt.Sprintf
中習慣性加了\n
。其實你單去搜索這個錯誤,真是找不到有價值的解釋,因爲很少會有人犯這種錯誤。至於我是如何發現錯在哪裏的,這裏作簡短說明。先是發現複製打印的URL直接用瀏覽器和CURL都可以請求成功,這時還意識不到錯在哪裏。接着用Wireshark抓包發現跟在URL後面的HTTP/1.1
,一個在同一行,一個在下一行,因爲不同點不只有這一處,所以雖然發現,但未能給我啓發。再接着就是在代碼中強行用拼接好的URL與複製打印的URL判斷是否相等,果然不相等,同時打印字符串16進制進行對比,終於發現了那深坑。不到三小時實現接口,但是爲了發現這個錯誤,我那天的剩餘時間全部搭進去啦。
這裏有必要提下兩種不同的API,阿里大於已升級爲阿里雲旗下雲通信品牌,現在應該是買不了阿里大於的短信服務吧,只能買升級後的,我就是這樣。兩者有關係但又不兼容,實在是大坑,起初我就是用老的阿里大於Golang第三方SDK去嘗試發送短信,總是回覆錯誤!
{
"error_response": {
"code": 11,
"msg": "Insufficient isv permissions",
"sub_code": "isv.permission-api-package-limit",
"sub_msg": "scope ids is 11022 11600 11863",
"request_id": "eonzx7q4qjvg"
}
}
搜索了半天才發現真是風馬牛不相及啊。
老的阿里大於Golang第三方SDK
https://github.com/ltt1987/alidayu
https://github.com/northbright/alidayu