使用请求参数构造规范化的请求字符串(Canonicalized Query String)
A-Z、a-z、0-9
以及字符 “-”、“_”、“.”、“~”
不编码+
)替换成 %20
、星号(*
)替换成 %2A
、%7E
替换回波浪号(~
),即可得到上述规则描述的编码字符串按照 RFC2104 的定义,使用上面的构造的请求字符串计算签名 HMAC 值(使用的哈希算法是sha1算法)。
上面计算得到的HAMC值,经过base64加密后,在字符串前加上版本号
经过第3步后得到得就是签名,赋值给 F_sign参数
字符 - URL编码值
空格
- %20
"
- %22
#
- %23
%
- %25
&
- %26
'
- %27
(
- %28
)
- %29
+
- %2B
,
- %2C
/
- %2F
:
- %3A
;
- %3B
<
- %3C
=
- %3D
>
- %3E
?
- %3F
@
- %40
\
- %5C
|
- %7C
{
- %7B
}
- %7D
生成代码示例
func reqSign() string {
accessToken := "someToken"
// 请求参数
p := url.Values{}
p.Set("F_param_a", "value_a")
p.Set("F_param_b", "value_b")
p.Set("F_accesstoken", accessToken)
// 将参数按字母表顺序排列,并 URL encode 参数值
// 注:鉴于不会有人使用会被编码的字符命名参数(如果有,吊起来打到没有),此处省略对参数名的 URL encode 操作
e := p.Encode()
e = strings.Replace(e, "+", "%20", -1) // "+" -> "%20"
e = strings.Replace(e, "*", "%2A", -1) // "*" -> "%2A"
e = strings.Replace(e, "%7E", "~", -1) // "%7E" -> "~"
// signKey ver 1
signKey := accessToken
// signKey ver 2
// method := http.MethodGet
// signKey := method + "&" + url.QueryEscape("/") + "&" + accessToken
// 计算hmac
mac := hmac.New(sha1.New, []byte(signKey))
mac.Write([]byte(e))
m := mac.Sum(nil)
// base64url encode
b64Hmac := base64.URLEncoding.EncodeToString(m)
// 签名版本: "01" or "02"
signVer := "01"
return signVer + b64Hmac
}
// base64 编码转为 base64url 编码
// @param base64Str string
// @return string
let encode64To64URL = (base64Str) => {
return base64Str.replace(/\+/g, '-').replace(/\//g, '_');
}
// URL encode 参数名/参数值
// "+" -> "%20"
// "*" -> "%2A"
// "%7E" -> "~"
// @param raw string
// @return string
let encode = (raw) => {
return encodeURIComponent(raw).
replace(/\+/g, "%20").
replace(/\*/g, "%2A").
replace(/%7E/g, "~");
}
// 构造请求签名
// @return string
let reqSign = () => {
let accesstoken = "someToken";
// 参数
let p = new URLSearchParams()
p.append(encode("F_param_a"), encode("val_a"));
p.append(encode("F_param_b"), encode("val_b"));
p.append(encode("F_accesstoken"), encode(accesstoken));
p.sort();
paramsArray = [];
for (let kv of p.entries()){
paramsArray.push(kv[0]+"="+kv[1]);
}
let msg = paramsArray.join("&");
// 此处使用 https://github.com/digitalbazaar/forge 的 hmac 实现
let signKey = accesstoken;
let hmac = forge.hmac.create();
hmac.start('sha1', signKey);
hmac.update(msg);
let signVer = "01";
return signVer + encode64To64URL(btoa(hmac.digest().data));
}