签名算法
如果SDK列表中包含您使用的语言,可使用对应的SDK并忽略以下签名算法,SDK在调用过程中会自动签名。
数据假设
在生成 API 请求中的签名(Signature
) 时,需要提供账户密钥,包括 PublicKey
和 PrivateKey
。
密钥可以从 PICPIK用户中心 (opens in a new tab) 获取。
本例中假设
PublicKey = 'abcdefg'
PrivateKey = '123456'
您可以使用上述的 PublicKey
和 PrivateKey
调试代码, 如得到与后文一致的签名结果,即表示代码是正确的,可再换为您自己的 PublicKey
和 PrivateKey
以及业务所需的其它API请求参数。
本例中假设用户请求参数串如下:
💡
实际使用过程中,应包含:除Signature
外所有已填写的参数。
{
"Action" : "ListModels",
"PublicKey" : "abcdefg"
}
构造签名
💡
如果示例代码中包含您使用的语言,建议直接使用其中的Sign
函数构造签名。
如果示例代码中没有您使用的语言,可以联系我们补充或者按照以下规则生成签名:
1. 将请求参数按照名称进行升序排列
{
"Action" : "ListModels",
"PublicKey" : "abcdefg"
}
2. 构造被签名参数串
被签名串的构造规则为: 被签名串 = 所有请求参数拼接(无需 HTTP 转义)。并在本签名串的结尾拼接 API 密钥的私钥(PrivateKey
)。
ActionListModelsPublicKeyabcdefg123456
💡
注意:
- 对于 bool 类型,应编码为
true
/false
,首字母需小写。 - 对于浮点数类型,如果小数部分为 0,应仅保留整数部分,如
42.0
应保留42
- 对于浮点数类型,不能使用科学计数法
- 对于数组类型,将数组的每个元素直接转为字符串拼接
- 对于map类型,将每个字段按照名称进行升序排列,然后将每个字段的名称和值拼接
3. 计算签名
使用SHA1编码签名串,生成最终签名,即是请求参数 Signature
的值。
按照上述算法,本例中,计算出的 Signature
为 4a20bc1141494035f6aaaad13224c94c5a8bc3a5 。
语言编码示例
func TestGenerateApiKey(t *testing.T) {
var (
PublicKey = "abcdefg"
PrivateKey = "123456"
req = map[string]interface{}{
"Action": "ListModels",
"PublicKey": PublicKey,
}
)
sign := Sign(req, PrivateKey)
fmt.Println(sign)
}
// Sign generate signature
func Sign(params map[string]interface{}, privateKey string) string {
str := map2String(params) + privateKey
hashed := sha1.Sum([]byte(str))
return hex.EncodeToString(hashed[:])
}
// map2String convert map type to string
func map2String(params map[string]interface{}) (str string) {
for _, k := range extractSortedKeys(params) {
str += k + any2String(params[k])
}
return
}
// any2String convert any type to string
func any2String(v interface{}) string {
switch v := v.(type) {
case string, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
return simple2String(v)
case *string, *bool, *int, *int8, *int16, *int32, *int64, *uint, *uint8, *uint16, *uint32, *uint64:
return point2String(v)
case float32, float64, *float32, *float64:
return float2String(v)
case []interface{}:
return slice2String(v)
case map[string]interface{}:
return map2String(v)
default:
return reflectStruct2String(v)
}
}
func point2String(v interface{}) string {
value := reflect.ValueOf(v)
if value.Kind() == reflect.Ptr {
return simple2String(value.Elem().Interface())
} else {
return simple2String(v)
}
}
// simple2String convert slice type to string
func slice2String(arr []interface{}) (str string) {
for _, v := range arr {
switch v := v.(type) {
case string, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64,
*string, *bool, *int, *int8, *int16, *int32, *int64, *uint, *uint8, *uint16, *uint32, *uint64, *float32, *float64:
str += simple2String(v)
case []interface{}:
str += slice2String(v)
case map[string]interface{}:
str += map2String(v)
default:
str += reflectStruct2String(reflect.ValueOf(v))
}
}
return
}
// simple2String convert simple type to string
func simple2String(v interface{}) string {
switch v.(type) {
case int:
return decimal.NewFromInt(int64(v.(int))).String()
case int8:
return decimal.NewFromInt(int64(v.(int8))).String()
case int16:
return decimal.NewFromInt(int64(v.(int16))).String()
case int32:
return decimal.NewFromInt(int64(v.(int32))).String()
case int64:
return decimal.NewFromInt(v.(int64)).String()
case uint:
return decimal.NewFromInt(int64(v.(uint))).String()
case uint8:
return decimal.NewFromInt(int64(v.(uint8))).String()
case uint16:
return decimal.NewFromInt(int64(v.(uint16))).String()
case uint32:
return decimal.NewFromInt(int64(v.(uint32))).String()
case uint64:
return decimal.NewFromInt(int64(v.(uint64))).String()
default:
return fmt.Sprintf("%v", v)
}
}
func float2String(v interface{}) string {
switch v.(type) {
case *float64:
return decimal.NewFromFloat(*v.(*float64)).String()
case *float32:
return decimal.NewFromFloat32(*v.(*float32)).String()
case float64:
return decimal.NewFromFloat(v.(float64)).String()
case float32:
return decimal.NewFromFloat32(v.(float32)).String()
}
return ""
}
func struct2String(v interface{})string{
strJson,err := json.Marshal(v)
if err != nil {
return ""
}
params := make(map[string]interface{}, 0)
err = json.Unmarshal(strJson, ¶ms)
if err != nil {
return ""
}
return map2String(params)
}
// reflectStruct2String convert array or slice or struct to string in reflect way
func reflectStruct2String(v interface{}) (str string) {
rv := reflect.ValueOf(v)
if rv.Kind() == reflect.Array || rv.Kind() == reflect.Slice {
for i := 0; i < rv.Len(); i++ {
str += any2String(rv.Index(i).Interface())
}
}else if rv.Kind() == reflect.Struct {
str += struct2String(v)
}
return
}
// extractSortedKeys extract all sorted keys from map[string]interface{}
func extractSortedKeys(m map[string]interface{}) []string {
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
return keys
}