php轉go程式設計師一枚,最近公司專案也在逐漸轉go。
對接的第一步就是簽名驗證中介軟體。
需要php和go都實現同一套簽名驗證。
簽名步驟
介面資料統一採用 簽名認證。步驟如下:
- 約定好appKey、appSecret。
- 通過簽名規則生成簽名sign;
- 每次請求業務介面 Header 中需攜帶appKey、timestamp、 sign 。
appSecret 傳送請求不傳,僅作為簽名加密使用
簽名規則
引數名ASCII碼從小到大排序(字典序);
假設引數:
{
"event": "add",
"event_desc":"add",
"source":"crs",
"data": {
"order_id":1,
"enterprise_id":1,
"order_no":"1111"
}
}
- 按照引數名ASCII字典序升序(引數如果是陣列,陣列也應ASCII字典序升序),將引數轉為json,得到引數串
{"data":{"enterprise_id":1,"order_id":1,"order_no":"1111"},"event":"add","event_desc":"add","source":"crs"}
- 引數串首尾拼接appSecret並進行md5加密
遇到的問題
排序。
go map內部儲存是有序的,只是在遍歷的時候,會隨機選一個bucket 進行。
php 陣列順序就是接收順序。
所以這裡是對php陣列進行了排序。網上搜了很多例子,都是隻進行了一次ksort,但是go的排序是每一層都是順序的。
php 多維陣列遞迴排序如下://多維陣列內排序 $sortFunc = static function (array $params, callable $sortFunc): array { array_walk($params, static function(&$item) use ($sortFunc) { $item = is_array($item) ? $sortFunc($item, $sortFunc) : $item; }); ksort($params); return $params; }; $sortedArr = $sortFunc($params, $sortFunc);
go和php url編碼結果不一致。
最初的打算是使用http_build_query生成 URL-encode的字串再md5,
go程式碼如下:func HttpBuildQuery(queryData url.Values) string { return queryData.Encode() }
但是兩者生成的url編碼結果差異挺大,遂放棄。轉而使用json字串。
go和php json 編碼結果不一樣。
go語言中json.Marshal生成json時特殊html字元(常見的有&、<、>)會被轉義。第一次嘗試: go 設定 SetEscapeHTML(false) (放棄)
type Test struct { Content string } func sign() { t := new(Test) t.Content = "http://www.baidu.com?id=123&test=1" bf := bytes.NewBuffer([]byte{}) jsonEncoder := json.NewEncoder(bf) jsonEncoder.SetEscapeHTML(false) jsonEncoder.Encode(t) fmt.Println(bf.String()) }
SetEscapeHTML(false)那個會在json最後自動拼個換行符,處理起來挺麻煩。
第二次嘗試:go 字串替換(可行)inputJsonStr = strings.Replace(inputJsonStr, "\\u003c", "<", -1) inputJsonStr = strings.Replace(inputJsonStr, "\\u003e", ">", -1) inputJsonStr = strings.Replace(inputJsonStr, "\\u0026", "&", -1)
第三次嘗試:php json_encode(增加引數JSON_HEX_AMP 和JSON_HEX_TAG,最終使用)
$jsonStr = json_encode($sortedData, JSON_HEX_AMP + JSON_HEX_TAG + JSON_UNESCAPED_UNICODE + JSON_UNESCAPED_SLASHES);
這裡用到的json_encode的4個引數說明:
JSON_HEX_AMP (integer) 所有的 & 轉換成 \u0026。 自 PHP 5.3.0 起生效。
JSON_HEX_TAG (integer) 所有的 < 和 > 轉換成 \u003C 和 \u003E。 自 PHP 5.3.0 起生效。
JSON_UNESCAPED_SLASHES (integer) 不要編碼 /。 自 PHP 5.4.0 起生效。JSON_UNESCAPED_UNICODE (integer) 以字面編碼多位元組 Unicode 字元(預設是編碼成 \uXXXX)。 自 PHP 5.4.0 起生效。如果json資料中沒有特殊字元,也需要JSON_UNESCAPED_SLASHES 和 JSON_UNESCAPED_UNICODE 這兩個引數,否則php json編碼出來的結果滿篇都是 / 和 \uXXXX。
但是非常離譜的,走一遍對比之後,結果仍然不一致。
php json_encode增加引數JSON_HEX_TAG後,
php 將 <、> 轉成了 \u003C、\u003E,大寫的,
go 將 <、> 轉成了 \u003c、\u003e,小寫的…這。。。。。只能字串替換了,go的專案已經在用了,我們選擇在php程式碼中替換
$jsonStr = str_replace("\u003C", "\u003c", $jsonStr); $jsonStr = str_replace("\u003E", "\u003e", $jsonStr);
最後的簽名程式碼
go
import (
"crypto/md5"
"encoding/hex"
"encoding/json"
)
var appSecret = "123456"
// 驗證簽名
func verifySign() error {
params := make(map[string]interface{})
params["name"] = "test"
params["domain"] = "https://www.baidu.com?name=1&id=1"
params["ssss"] = "sssss"
params["aaaa"] = "aaaaa"
paramsJson, _ := json.Marshal(params)
s := md5.New()
s.Write([]byte(AppSecret + string(paramsJson) + AppSecret))
sign := hex.EncodeToString(s.Sum(nil))
}
php
$appSecret = '123456';
$params = [
'name' => 'test',
'domain' => 'https://www.baidu.com?name=1&id=1',
];
//多維陣列內排序
$sortFunc = static function (array $params, callable $sortFunc): array {
array_walk($params, static function(&$item) use ($sortFunc) {
$item = is_array($item) ? $sortFunc($item, $sortFunc) : $item;
});
ksort($params);
return $params;
};
$jsonStr = json_encode($sortFunc($params, $sortFunc), JSON_HEX_AMP + JSON_UNESCAPED_UNICODE + JSON_UNESCAPED_SLASHES);
$jsonStr = str_replace("\u003C", "\u003c", $jsonStr);
$jsonStr = str_replace("\u003E", "\u003e", $jsonStr);
$sign = md5($appSecret . $jsonStr . $appSecret);
工具推薦
- php2go
- json2go
其他
其他json_encode 第二個引數說明:
JSON_HEX_APOS (integer) 所有的 ‘ 轉換成 \u0027。 自 PHP 5.3.0 起生效。
JSON_HEX_QUOT (integer) 所有的 “ 轉換成 \u0022。 自 PHP 5.3.0 起生效。
JSON_FORCE_OBJECT (integer) 使一個非關聯陣列輸出一個類(Object)而非陣列。 在陣列為空而接受者需要一個類(Object)的時候尤其有用。 自 PHP 5.3.0 起生效。
JSON_NUMERIC_CHECK (integer) 將所有數字字串編碼成數字(numbers)。 自 PHP 5.3.3 起生效。
JSON_BIGINT_AS_STRING (integer) 將大數字編碼成原始字元原來的值。 自 PHP 5.4.0 起生效。
JSON_PRETTY_PRINT (integer) 用空白字元格式化返回的資料。 自 PHP 5.4.0 起生效。
本作品採用《CC 協議》,轉載必須註明作者和本文連結