签名算法说明
接口鉴权需要wps4签名的接口,header中需要携带以下字段:
| 参数 | 参数类型 | 是否必须 | 说明 |
|---|---|---|---|
| Content-Type | string | 是 | 固定为:“application/json” |
| Wps-Docs-Date | string | 是 | 取当前时间,格式:“Wed, 23 Jan 2013 06:43:08 GMT” |
| Wps-Docs-Authorization | string | 是 | 签名值,格式:“WPS-4 accesskey“:Signature” |
Wps-Docs-Authorization计算方法
接口签名时,只签uri字段,不签域名,具体计算规则如下:
Wps-Docs-Authorization:"WPS-4 $access_key:$Signature"
Signature:hmac-sha256(secret_key, Ver + HttpMethod + URI + Content-Type + Wps-Docs-Date + sha256(HttpBody))Ver + HttpMethod + URI + Content-Type + Date + sha256(HttpBody) 示例:
WPS-4POST/callback/path/demoapplication/jsonWed, 20 Apr 2022 01:33:07 GMTfc005f51a6e75586d2d5d078b657dxxxdf9c1dfa6a7c0c0ba38c715daeb6ede9- sha256 与 hmac-sha256 :均取小写十六进制字符串。
- Ver: WPS-4,表示算法版本,后续算法有更新,则变更该字段。
- HttpMethod:表示HTTP 请求的Method的字符串,如PUT、GET、POST、HEAD、DELETE等。
- URI:不带域名,如:"/api_url?app_id=aaaa"。
- Content-Type:表示请求内容的类型,如:“application/json”。
- Date:自行对签名时限进行验证。
- HttpBody:如果为空,则sha256(body)部分取空串。
Go代码示例
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"net/http"
"net/url"
"openapi/common/encryption"
"strings"
"time"
)
type Wps4Auth struct {
AccessKey string
SecretKey string
}
func NewWps4Auth(accessKey, secretKey string) *Wps4Auth {
auth := &Wps4Auth{}
auth.AccessKey = accessKey
auth.SecretKey = secretKey
return auth
}
func (a *Wps4Auth)BuildWps4Headers(method string, url *url.URL, data []byte, contentType string) *http.Header {
if strings.TrimSpace(contentType) == "" {
contentType = "application/json"
}
header := http.Header{}
auth, date := a.prepare(method, url, data, contentType)
header.Set(Wps4AuthSign, auth)
header.Set(contentTypeSign, contentType)
header.Set(wps4DateSign, date)
header.Set(connectionSign, "keep-alive")
return &header
}
func (a *Wps4Auth) prepare(method string, url *url.URL, data []byte, contentType string) (auth, date string) {
path := url.Path
if url.RawQuery != "" {
path += fmt.Sprintf("?%s", url.RawQuery)
}
var content []byte
if data != nil {
content = data
}
date = time.Now().UTC().Format(http.TimeFormat)
sig := a.sign(method, path, contentType, date, content)
auth = fmt.Sprintf("WPS-4 %s:%s", a.AccessKey, sig)
return
}
func (a *Wps4Auth) sign(method, uri, contentType, date string, content []byte) (sign string) {
bodySha := ""
if content != nil {
bodySha = encryption.GetSha256(content)
}
mac := hmac.New(sha256.New, []byte(a.SecretKey))
mac.Write([]byte("WPS-4"))
mac.Write([]byte(method))
mac.Write([]byte(uri))
mac.Write([]byte(contentType))
mac.Write([]byte(date))
mac.Write([]byte(bodySha))
return hex.EncodeToString(mac.Sum(nil))
}测试方法
import (
"encoding/json"
"net/url"
"testing"
)
type TestWps4Body struct {
MsgType string `json:"msg_type" web:"msg_type,required"`
MsgData string `json:"msg_data,omitempty" web:"msg_data"`
}
func TestWps4Sign(t *testing.T) {
accessKey := "****************"
secretKey := "********************************"
wps4Auth := NewWps4Auth(accessKey, secretKey)
method := "GET"
path := "/kopen/pay/v1/wx_adapter/mp/subscribe/wps_docer?access_token=********************************'"
header := wps4Auth.BuildWps4Headers(method, &url.URL{Path: path}, nil, "application/json")
t.Log(header)
method = "POST"
path = "/kopen/pay/v1/msg/apipush?access_token=********************************'"
body := &TestWps4Body{MsgType: "wps_docer_attent_reward", MsgData: "{'account': 'Zyssnh事实上&abc=123kk//dsa?d'}"}
reqData, err := json.Marshal(body)
if err != nil {
t.Error(err)
}
header = wps4Auth.BuildWps4Headers(method, &url.URL{Path: path}, reqData, "application/json")
t.Log(header)
}Python代码示例
import json
import requests
import hashlib
import time
# 应用信息
def _wps4_sig(method, url, date, body):
if body is None:
bodySha = ""
else:
bodySha = hashlib.sha256(body.encode('utf-8')).hexdigest()
content = "WPS-4" + method + url + "application/json" + date + bodySha
signature = hmac.new(secret_key.encode('utf-8'), content.encode('utf-8'), hashlib.sha256).hexdigest()
return "WPS-4 %s:%s" % (access_key, signature)
def wps4_request(method, host, uri, body=None, cookie=None, headers=None):
requests.packages.urllib3.disable_warnings()
if body is not None:
body = json.dumps(body)
date = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
# date = "Wed, 02 Jun 2021 12:15:40 GMT"
# print date
header = {"Content-type": "application/json"}
header['Wps-Docs-Date'] = date
header['Wps-Docs-Authorization'] = _wps4_sig(method, uri, date, body)
if headers != None:
# header = {}
for key, value in headers.items():
header[key] = value
url = "%s%s" % (host, uri)
r = requests.request(method, url, data=body,
headers=header, cookies=cookie, verify=False)
return r.status_code, r.textjava代码示例
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
//获取uri路径
String path = request.getURI().getPath() ;
if(request.getURI().getRawQuery() != null && !"".equals(request.getURI().getRawQuery())) {
path = path + "?" + request.getURI().getRawQuery();
}
//获取Content-type,“application/json"
String contentType = request.getHeaders().getContentType().toString();
//日期格式化
DateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
String date = dateFormat.format(new Date());
//open不参与签名,做替换处理
if (path.startsWith("/open")) {
path = path.replace("/open", "");
}
String sha256body;
//body为空则为空,否则返回sha256(body)
if (body != null && body.length > 0) {
sha256body = HMacUtils.getSHA256StrJava(body);
} else {
sha256body = "";
}
//hmac-sha256(secret_key, Ver + HttpMethod + URI + Content-Type + Wps-Date + sha256(HttpBody))
String signature = null;
try {
signature = HMacUtils.HMACSHA256("WPS-4" + request.getMethod() + path + contentType + date + sha256body,secretKey);
} catch (Exception e) {
e.printStackTrace();
}
request.getHeaders().add("Wps-Docs-Date",date);
request.getHeaders().add("Wps-Docs-Authorization", String.format("WPS-4 %s:%s", accessKey, signature));
return execution.execute(request, body);
}sha256和hmac工具类代码:
package demo.utils;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class HMacUtils {
public static String HMACSHA256(String data, String key) throws Exception {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString();
}
/**
* 利用java原生的摘要实现SHA256加密
* @param str 加密后的报文
* @return
*/
public static String getSHA256StrJava(byte[] str){
MessageDigest messageDigest;
String encodeStr = "";
try {
messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(str);
encodeStr = byte2Hex(messageDigest.digest());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return encodeStr;
}
/**
* 将byte转为16进制
* @param bytes
* @return
*/
private static String byte2Hex(byte[] bytes){
StringBuffer stringBuffer = new StringBuffer();
String temp = null;
for (int i=0;i<bytes.length;i++){
temp = Integer.toHexString(bytes[i] & 0xFF);
if (temp.length()==1){
//1得到一位的进行补0操作
stringBuffer.append("0");
}
stringBuffer.append(temp);
}
return stringBuffer.toString();
}
}Postman脚本示例
var accessKey=pm.environment.get("accessKey")
var secretKey=pm.environment.get("secretKey")
// console.log(`accessKey=${accessKey}, secretKey=${secretKey}, appToken=${appToken}`)
// 获取请求信息
var method = pm.request.method
var path=pm.environment.replaceIn(pm.request.url.getPathWithQuery())
var body = pm.environment.replaceIn(pm.request.body.toString())
var contentType="application/json"
var dateString = new Date().toGMTString();
var authorization = "";
if(body==""){
console.log(`[${method}] ${path}`)
authorization = 'WPS-4' + method + path + contentType + dateString
} else {
console.log(`[${method}] ${path}\n${body}`)
authorization = 'WPS-4' + method + path + contentType + dateString + CryptoJS.SHA256(body).toString(CryptoJS.enc.Hex)
}
console.log(`authorization ${authorization}`)
authorization = CryptoJS.HmacSHA256(authorization, secretKey).toString(CryptoJS.enc.Hex)
authorization = 'WPS-4 ' +accessKey + ':' + authorization
// 设置请求头
pm.request.headers.upsert({
key: 'Wps-Docs-Date',
value: dateString
})
pm.request.headers.upsert({
key: 'Content-Type',
value: contentType
})
pm.request.headers.upsert({
key: 'Wps-Docs-Authorization',
value: authorization
})
// console.log(pm.request.getHeaders());