签名
<p>将请求参数和appSecret封装成Map集合,将Map集合的参数值(value)拼装成字符串进行MD5签名。排序顺序{requestBody}{appSecret}{timestamp},参数之间用分号;隔开。其中loginkey,appid不参与签名。</p>
<p><strong>注意:</strong>
1、时间戳是毫秒单位;需要在15分钟内
2、requestBody按<strong>字典顺序</strong>排列。
3、如果参数中有对象,将对象的值转换成JSON字符串再进行签名</p>
<h3>GET请求实例</h3>
<p>请求地址:<code>https://gmall.m.qq.com/getDemo1</code>
appid: <code>1</code>
secret: <code>testsecret</code>
时间戳:<code>1678862436802</code>
请求参数:</p>
<table>
<thead>
<tr>
<th style="text-align: left;">key</th>
<th style="text-align: left;">value</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;">foo</td>
<td style="text-align: left;">bar</td>
</tr>
<tr>
<td style="text-align: left;">url</td>
<td style="text-align: left;"><a href="https://www.qq.com">https://www.qq.com</a></td>
</tr>
<tr>
<td style="text-align: left;">a</td>
<td style="text-align: left;">b</td>
</tr>
</tbody>
</table>
<p>签名字符串为:<code>a=b;foo=bar;url=https%3A%2F%2Fwww.qq.com;testsecret1678862493257</code>
签名后sign为:<code>bfabdf358273ac9fbcb3383b927304c1</code>
实际请求地址为:<code>https://gmall.m.qq.com/getDemo1?app_id=1&amp;sign=bfabdf358273ac9fbcb3383b927304c1&amp;timestamp=1678862493257&amp;foo=bar&amp;url=https%3A%2F%2Fwww.qq.com&amp;a=b</code></p>
<h3>POST请求实例</h3>
<p>请求地址:<code>https://gmall.m.qq.com/postDemo</code>
appid: <code>1</code>
secret: <code>testsecret</code>
时间戳:<code>1678863346070</code>
请求参数详情可见curl
实际请求的curl为</p>
<pre><code class="language-shell">curl --location --request POST 'https://testgmall.m.qq.com/postDemo?appid=1&amp;sign=9b6ad75f4cf0dfc42fb3e19e1dec9ecf&amp;timestamp=1678863346070&amp;' \
--header 'Content-Type: application/json' \
--data-raw '{
&quot;url&quot;: &quot;https://www.qq.com&quot;,
&quot;timestamp&quot;: 1678863346070,
&quot;number&quot;: 1,
&quot;foo&quot;: &quot;bar&quot;,
&quot;object&quot;: {
&quot;foo&quot;: &quot;bar&quot;,
&quot;bar&quot;: &quot;foo&quot;,
&quot;url&quot;: &quot;https://www.qq.com&quot;
}
}'</code></pre>
<p>签名字符串为:<code>foo=bar;number=1;object={&quot;foo&quot;:&quot;bar&quot;,&quot;bar&quot;:&quot;foo&quot;,&quot;url&quot;:&quot;https://www.qq.com&quot;};url=https%3A%2F%2Fwww.qq.com;testsecret1678863346070</code></p>
<p>go语言实现</p>
<pre><code class="language-javascript">package main
import (
&quot;bytes&quot;
&quot;crypto/md5&quot;
&quot;encoding/hex&quot;
&quot;encoding/json&quot;
&quot;fmt&quot;
&quot;io/ioutil&quot;
&quot;net/http&quot;
&quot;net/url&quot;
&quot;reflect&quot;
&quot;sort&quot;
&quot;strconv&quot;
&quot;strings&quot;
&quot;time&quot;
)
// 生成MD5签名字符串
// params: 请求参数map[string]string
// appSecret: 开发者的appSecret
// 返回值:string类型的签名字符串
func generateSignature(params map[string]string, appSecret string) string {
// 将请求参数和appSecret封装成Map集合
params[&quot;appSecret&quot;] = appSecret
// 按照字典序排序
keys := make([]string, 0, len(params))
for k := range params {
keys = append(keys, k)
}
sort.Strings(keys)
// 将Map集合的参数值(value)拼装成字符串进行MD5签名
var sign strings.Builder
for _, k := range keys {
if k == &quot;loginkey&quot; || k == &quot;token&quot; || k == &quot;appid&quot; {
continue
}
sign.WriteString(k)
sign.WriteByte('=')
sign.WriteString(params[k])
sign.WriteByte(';')
}
sign.WriteString(appSecret)
sign.WriteString(params[&quot;timestamp&quot;])
// 对签名字符串进行MD5加密
h := md5.New()
h.Write([]byte(sign.String()))
signature := hex.EncodeToString(h.Sum(nil))
return signature
}
// 发送GET请求
// url: 请求的URL地址
// params: 请求参数map[string]string
// appID: 开发者的appID
// appSecret: 开发者的appSecret
// 返回值:[]byte类型的响应结果和error类型的错误
func sendGetRequest(urlString string, params map[string]string, appID string, appSecret string) ([]byte, error) {
// 生成签名参数
params[&quot;appId&quot;] = appID
params[&quot;timestamp&quot;] = strconv.FormatInt(time.Now().UnixNano()/1e6, 10)
signature := generateSignature(params, appSecret)
// 构建请求URL
reqURL, err := url.ParseRequestURI(urlString)
if err != nil {
return nil, err
}
q := reqURL.Query()
q.Set(&quot;appid&quot;, appID)
q.Set(&quot;sign&quot;, signature)
q.Set(&quot;_timestamp&quot;, params[&quot;timestamp&quot;])
for k, v := range params {
q.Set(k, v)
}
reqURL.RawQuery = q.Encode()
// 发送请求
resp, err := http.Get(reqURL.String())
if err != nil {
return nil, err
}
defer resp.Body.Close()
// 读取响应结果
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return body, nil
}
// 发送POST请求
// url: 请求的URL地址
// params: 请求参数map[string]interface{}
// appID: 开发者的appID
// appSecret: 开发者的appSecret
// 返回值:[]byte类型的响应结果和error类型的错误
func sendPostRequest(urlString string, params map[string]interface{}, appID string, appSecret string) ([]byte, error) {
// 生成签名参数
timestamp := strconv.FormatInt(time.Now().UnixNano()/1e6, 10)
signature := generateSignatureForPost(params, appSecret, timestamp)
// 构建请求URL
reqURL, err := url.ParseRequestURI(urlString)
if err != nil {
return nil, err
}
q := reqURL.Query()
q.Set(&quot;appid&quot;, appID)
q.Set(&quot;sign&quot;, signature)
q.Set(&quot;timestamp&quot;, timestamp)
reqURL.RawQuery = q.Encode()
// 构建请求体
requestBody, err := json.Marshal(params)
if err != nil {
return nil, err
}
// 发送请求
resp, err := http.Post(reqURL.String(), &quot;application/json&quot;, bytes.NewReader(requestBody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
// 读取响应结果
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return body, nil
}
// 生成POST请求的MD5签名字符串
// params: 请求参数map[string]interface{}
// appSecret: 开发者的appSecret
// timestamp: 时间戳
// 返回值:string类型的签名字符串
func generateSignatureForPost(params map[string]interface{}, appSecret string, timestamp string) string {
// 将请求参数和appSecret封装成Map集合
// 对参数中的对象进行JSON序列化
for k, v := range params {
if reflect.TypeOf(v).Kind() == reflect.Map || reflect.TypeOf(v).Kind() == reflect.Slice {
jsonBytes, _ := json.Marshal(v)
params[k] = string(jsonBytes)
}
}
// 按照字典序排序
keys := make([]string, 0, len(params))
for k := range params {
keys = append(keys, k)
}
sort.Strings(keys)
// 将Map集合的参数值(value)拼装成字符串进行MD5签名
var sign strings.Builder
for _, k := range keys {
if k == &quot;loginkey&quot; || k == &quot;appid&quot; {
continue
}
sign.WriteString(k)
sign.WriteByte('=')
sign.WriteString(fmt.Sprintf(&quot;%v&quot;, params[k]))
sign.WriteByte(';')
}
sign.WriteString(appSecret)
sign.WriteString(timestamp)
// 对签名字符串进行MD5加密
h := md5.New()
h.Write([]byte(sign.String()))
signature := hex.EncodeToString(h.Sum(nil))
return signature
}
func main() {
appid := &quot;appid&quot;
secret := &quot;secret&quot;
body, err := sendPostRequest(&quot;https://testgmall.m.qq.com/ext/stock2/consumeStock&quot;, map[string]interface{}{
&quot;account_id&quot;: &quot;testaccount&quot;,
&quot;cid&quot;: 5705,
&quot;order_id&quot;: &quot;test-12341&quot;,
&quot;device_id&quot;: &quot;&quot;,
}, appid, secret)
if err != nil {
fmt.Printf(&quot;Sign: %s\n&quot;, err)
}
fmt.Printf(&quot;Post Result: %s\n&quot;, string(body))
getBody, err := sendGetRequest(&quot;https://testgmall.m.qq.com/ext/mall/getMallItemData&quot;, map[string]string{
&quot;account_id&quot;: &quot;testaccount&quot;,
&quot;mid&quot;: &quot;5705&quot;,
&quot;device_id&quot;: &quot;&quot;,
}, appid, secret)
if err != nil {
fmt.Printf(&quot;Sign: %s\n&quot;, err)
}
fmt.Printf(&quot;Get Result: %s\n&quot;, string(getBody))
}
</code></pre>