通过Postman实现专有云云解析API网关的请求签名与调试

本文涉及的产品
云原生 API 网关,700元额度,多规格可选
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 本文主要使用Postman调试API网关的方法,文中使用Pre-request Script实现了阿里专有云云解析API网关的签名算法。

1.参考文档

以下两篇文档是我调试成功的基础,感激不尽。

通过Postman实现API网关的请求签名与调试

云解析 DNS-调用方式-签名机制

2. 起因

需要使用云解析python sdk,同事已经写好了相关python脚本。我想借此机会了解一下restful api。

先是找到了postman这个工具,再者搜到了上面提到的两篇文章。当然还需要wireshark把python sdk的请求抓一下包,照猫画虎,构造postman中的结构。

3. 获取域名列表

3.1 Headers

image.png

3.2 Params

image.png

3.3 Pre-request Script

Date.prototype.format = function(format) {
       var date = {
              "M+": this.getUTCMonth() + 1,
              "d+": this.getUTCDate(),
              "h+": this.getUTCHours(),
              "m+": this.getUTCMinutes(),
              "s+": this.getUTCSeconds(),
              "q+": Math.floor((this.getMonth() + 3) / 3),
              "S+": this.getUTCMilliseconds()
       };
       if (/(y+)/i.test(format)) {
              format = format.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length));
       }
       for (var k in date) {
              if (new RegExp("(" + k + ")").test(format)) {
                     format = format.replace(RegExp.$1, RegExp.$1.length == 1
                            ? date[k] : ("00" + date[k]).substr(("" + date[k]).length));
              }
       }
       return format;

}

//填写实际使用的key和secret
var appKey = "******";
var appSecret = "**********";

//var md5 = calcMd5();
var nowDate = new Date();
var date = nowDate.toUTCString();
var nonce = createUuid();
var tstamp = nowDate.format('yyyy-MM-ddThh%3Amm%3AssZ');
pm.globals.set("tstamp", tstamp);

var textToSign = "";
textToSign += request.method + "&" + "%2F" + "&";
//textToSign += request.headers["accept"] + "\n";
//textToSign += md5 + "\n";
//textToSign += request.headers["content-type"] + "\n";
//textToSign += date + "\n";

//var headers = headersToSign();
//var signatureHeaders;
//var sortedKeys = Array.from(headers.keys()).sort()
//for (var headerName of sortedKeys) {
//    textToSign += headerName + ":" + headers.get(headerName) + "\n";
//    signatureHeaders = signatureHeaders ? signatureHeaders + "," + headerName : headerName;
//}
textToSign += urlToSign();
console.log("textToSign\n" + textToSign.replace(/\n/g, "#"));
//var hash = CryptoJS.HmacSHA256(textToSign, appSecret + "&" )
var hash = CryptoJS.HmacSHA1(textToSign, appSecret + "&" )
console.log("hash:" + hash)
var signature = hash.toString(CryptoJS.enc.Base64)
console.log("signature:" + signature)

pm.globals.set('AppKey', appKey);
//pm.globals.set('Md5', md5);
pm.globals.set("Date", date);
pm.globals.set("Signature", signature);
//pm.globals.set("SignatureHeaders", signatureHeaders);
pm.globals.set("Nonce", nonce);

//function headersToSign() {
//    var headers = new Map();
//    for (var name in request.headers) {
//        name = name.toLowerCase();
//        if (!name.startsWith('x-ca-')) {
//            continue;
//        } 
//        if (name === "x-ca-signature" || name === "x-ca-signature-headers" || name == "x-ca-key" || name === 'x-ca-nonce') {
//            continue;
//        }
//        var value = request.headers[name];
//        headers.set(name, value);
//    }
//    headers.set('x-ca-key', appKey);
//    headers.set('x-ca-nonce', nonce);
//    return headers;
//}

function urlToSign() {
    var params = new Map();
//    var contentType = request.headers["content-type"];
//    if (contentType && contentType.startsWith('application/x-www-form-urlencoded')) {
//        const formParams = request.data.split("&");
//        formParams.forEach((p) => {
//            const ss = p.split('=');
//            params.set(ss[0], ss[1]);
//        })
//    }
    
    const ss = request.url.split('?');
    if (ss.length > 1 && ss[1]) {
        const queryParams = ss[1].split('&');
        queryParams.forEach((p) => {
            const ss = p.split('=');
            if(ss[0]==="Signature") return;
            if(ss[1]==="{{tstamp}}") ss[1]=tstamp;
            if(ss[1]==="{{Nonce}}") ss[1]=nonce;
            params.set(ss[0], ss[1]);
        })
    }
    
    var sortedKeys = Array.from(params.keys())
    sortedKeys.sort();
    
//    var l1 = ss[0].lastIndexOf('/');
//    var url = ss[0].substring(l1);
    var first = true;
    var qs
    for (var k of sortedKeys) {
        var s = k + "=" + params.get(k);
        qs = qs ? qs + "&" + s : s;
        console.log("key=" + k + " value=" + params.get(k));
    }
//    return qs ? url + "?" + qs : url;
    return encodeURIComponent(qs);
}

//function calcMd5() {
//    var contentType = request.headers["content-type"];
//    if (request.data && !contentType.startsWith('application/x-www-form-urlencoded')) {
//        var data = request.data;
//        var md5 = CryptoJS.MD5(data);
//        var md5String = md5.toString(CryptoJS.enc.Base64);
//        console.log("data:" + data + "\nmd5:" + md5String);
//        return md5String;
//    } else {
//        return "";
//    }
//}

function createUuid() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
        return v.toString(16);
    });
}

这里的大多数脚本都是来源于前面提到的文档。
pm.globals.set设置的全局变量可以在params、headers、body等中引用,用两个大括号括起来。比如{{tstamp}}

最主要是签名规则,本例是把params中所有参数按key排序,连在一起,再签名。

3.4 post

image.png
左侧下拉菜单选择POST。地址框中要写api的地址,后面的参数都是自动填写的。

点右侧的send按钮,运气好的话,正确的reponse就出现了。

4.获取A记录

4.1 Headers

image.png

4.2 Params

image.png

4.3 Body

image.png
这里Id的值是53,是之前请求域名列表时返回的DomainId。

4.4 Pre-request Script

Date.prototype.format = function(format) {
       var date = {
              "M+": this.getUTCMonth() + 1,
              "d+": this.getUTCDate(),
              "h+": this.getUTCHours(),
              "m+": this.getUTCMinutes(),
              "s+": this.getUTCSeconds(),
              "q+": Math.floor((this.getMonth() + 3) / 3),
              "S+": this.getUTCMilliseconds()
       };
       if (/(y+)/i.test(format)) {
              format = format.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length));
       }
       for (var k in date) {
              if (new RegExp("(" + k + ")").test(format)) {
                     format = format.replace(RegExp.$1, RegExp.$1.length == 1
                            ? date[k] : ("00" + date[k]).substr(("" + date[k]).length));
              }
       }
       return format;

}
//填写自己的key和secret
var appKey = "******";
var appSecret = "**********";

//var md5 = calcMd5();
var nowDate = new Date();
var date = nowDate.toUTCString();
var nonce = createUuid();
var tstamp = nowDate.format('yyyy-MM-ddThh%3Amm%3AssZ');
pm.globals.set("tstamp", tstamp);

var textToSign = "";
textToSign += request.method + "&" + "%2F" + "&";
//textToSign += request.headers["accept"] + "\n";
//textToSign += md5 + "\n";
//textToSign += request.headers["content-type"] + "\n";
//textToSign += date + "\n";

//var headers = headersToSign();
//var signatureHeaders;
//var sortedKeys = Array.from(headers.keys()).sort()
//for (var headerName of sortedKeys) {
//    textToSign += headerName + ":" + headers.get(headerName) + "\n";
//    signatureHeaders = signatureHeaders ? signatureHeaders + "," + headerName : headerName;
//}
textToSign += urlToSign();
console.log("textToSign\n" + textToSign.replace(/\n/g, "#"));
//var hash = CryptoJS.HmacSHA256(textToSign, appSecret + "&" )
var hash = CryptoJS.HmacSHA1(textToSign, appSecret + "&" )
console.log("hash:" + hash)
var signature = hash.toString(CryptoJS.enc.Base64)
console.log("signature:" + signature)

pm.globals.set('AppKey', appKey);
//pm.globals.set('Md5', md5);
pm.globals.set("Date", date);
pm.globals.set("Signature", signature);
//pm.globals.set("SignatureHeaders", signatureHeaders);
pm.globals.set("Nonce", nonce);

//function headersToSign() {
//    var headers = new Map();
//    for (var name in request.headers) {
//        name = name.toLowerCase();
//        if (!name.startsWith('x-ca-')) {
//            continue;
//        } 
//        if (name === "x-ca-signature" || name === "x-ca-signature-headers" || name == "x-ca-key" || name === 'x-ca-nonce') {
//            continue;
//        }
//        var value = request.headers[name];
//        headers.set(name, value);
//    }
//    headers.set('x-ca-key', appKey);
//    headers.set('x-ca-nonce', nonce);
//    return headers;
//}

function urlToSign() {
    var params = new Map();
    var contentType = request.headers["content-type"];
    console.log("request");
    console.log(request);
    console.log("request.data");
    console.log(request.data);
    console.log("pm.request");
    console.log(pm.request);
//    if (contentType && contentType.startsWith('application/x-www-form-urlencoded')) {
//        const formParams = request.data.split("&");
//        formParams.forEach((p) => {
//            const ss = p.split('=');
//            params.set(ss[0], ss[1]);
//        })
//    }

    console.log("body form:");
    for(var key in request.data) {
        var value = request.data[key];
        if(key==="RrSet") value=escape(value);
        console.log("key = "+key+", value = "+value);
        params.set(key,value);
    }
    
    const ss = request.url.split('?');
    if (ss.length > 1 && ss[1]) {
        const queryParams = ss[1].split('&');
        queryParams.forEach((p) => {
            const ss = p.split('=');
            if(ss[0]==="Signature") return;
            if(ss[1]==="{{tstamp}}") ss[1]=tstamp;
            if(ss[1]==="{{Nonce}}") ss[1]=nonce;
            params.set(ss[0], ss[1]);
        })
    }
    
    var sortedKeys = Array.from(params.keys())
    sortedKeys.sort();
    
//    var l1 = ss[0].lastIndexOf('/');
//    var url = ss[0].substring(l1);
    var first = true;
    var qs
    console.log("All params:");
    for (var k of sortedKeys) {
        var s = k + "=" + params.get(k);
        qs = qs ? qs + "&" + s : s;
        console.log("key=" + k + " value=" + params.get(k));
    }
//    return qs ? url + "?" + qs : url;
//    return encodeURIComponent(qs);
    return escape(qs);
}

//function calcMd5() {
//    var contentType = request.headers["content-type"];
//    if (request.data && !contentType.startsWith('application/x-www-form-urlencoded')) {
//        var data = request.data;
//        var md5 = CryptoJS.MD5(data);
//        var md5String = md5.toString(CryptoJS.enc.Base64);
//        console.log("data:" + data + "\nmd5:" + md5String);
//        return md5String;
//    } else {
//        return "";
//    }
//}

function createUuid() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
        return v.toString(16);
    });
}

5.更新A记录

5.1 Headers

image.png

5.2 Params

image.png

5.3 Body

image.png

5.4 Pre-request Script

Date.prototype.format = function(format) {
       var date = {
              "M+": this.getUTCMonth() + 1,
              "d+": this.getUTCDate(),
              "h+": this.getUTCHours(),
              "m+": this.getUTCMinutes(),
              "s+": this.getUTCSeconds(),
              "q+": Math.floor((this.getMonth() + 3) / 3),
              "S+": this.getUTCMilliseconds()
       };
       if (/(y+)/i.test(format)) {
              format = format.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length));
       }
       for (var k in date) {
              if (new RegExp("(" + k + ")").test(format)) {
                     format = format.replace(RegExp.$1, RegExp.$1.length == 1
                            ? date[k] : ("00" + date[k]).substr(("" + date[k]).length));
              }
       }
       return format;

}
//填写自己的key和scret
var appKey = "*******";
var appSecret = "********";

//var md5 = calcMd5();
var nowDate = new Date();
var date = nowDate.toUTCString();
var nonce = createUuid();
var tstamp = nowDate.format('yyyy-MM-ddThh%3Amm%3AssZ');
pm.globals.set("tstamp", tstamp);

var textToSign = "";
textToSign += request.method + "&" + "%2F" + "&";
//textToSign += request.headers["accept"] + "\n";
//textToSign += md5 + "\n";
//textToSign += request.headers["content-type"] + "\n";
//textToSign += date + "\n";

//var headers = headersToSign();
//var signatureHeaders;
//var sortedKeys = Array.from(headers.keys()).sort()
//for (var headerName of sortedKeys) {
//    textToSign += headerName + ":" + headers.get(headerName) + "\n";
//    signatureHeaders = signatureHeaders ? signatureHeaders + "," + headerName : headerName;
//}
textToSign += urlToSign();
console.log("textToSign\n" + textToSign.replace(/\n/g, "#"));
//var hash = CryptoJS.HmacSHA256(textToSign, appSecret + "&" )
var hash = CryptoJS.HmacSHA1(textToSign, appSecret + "&" )
console.log("hash:" + hash)
var signature = hash.toString(CryptoJS.enc.Base64)
console.log("signature:" + signature)

pm.globals.set('AppKey', appKey);
//pm.globals.set('Md5', md5);
pm.globals.set("Date", date);
pm.globals.set("Signature", signature);
//pm.globals.set("SignatureHeaders", signatureHeaders);
pm.globals.set("Nonce", nonce);

//function headersToSign() {
//    var headers = new Map();
//    for (var name in request.headers) {
//        name = name.toLowerCase();
//        if (!name.startsWith('x-ca-')) {
//            continue;
//        } 
//        if (name === "x-ca-signature" || name === "x-ca-signature-headers" || name == "x-ca-key" || name === 'x-ca-nonce') {
//            continue;
//        }
//        var value = request.headers[name];
//        headers.set(name, value);
//    }
//    headers.set('x-ca-key', appKey);
//    headers.set('x-ca-nonce', nonce);
//    return headers;
//}

function urlToSign() {
    var params = new Map();
    var contentType = request.headers["content-type"];
    console.log("request");
    console.log(request);
    console.log("request.data");
    console.log(request.data);
    console.log("pm.request");
    console.log(pm.request);
//    if (contentType && contentType.startsWith('application/x-www-form-urlencoded')) {
//        const formParams = request.data.split("&");
//        formParams.forEach((p) => {
//            const ss = p.split('=');
//            params.set(ss[0], ss[1]);
//        })
//    }

    console.log("body form:");
    for(var key in request.data) {
        var value = request.data[key];
      //根据response中提示的签名字符串,body中form中的数组多进行了一次转义,把每个百分号多转义了一次
      if(key==="RrSet") value=escape(value);
        console.log("key = "+key+", value = "+value);
        params.set(key,value);
    }
    
    const ss = request.url.split('?');
    if (ss.length > 1 && ss[1]) {
        const queryParams = ss[1].split('&');
        queryParams.forEach((p) => {
            const ss = p.split('=');
            if(ss[0]==="Signature") return;
            if(ss[1]==="{{tstamp}}") ss[1]=tstamp;
            if(ss[1]==="{{Nonce}}") ss[1]=nonce;
            params.set(ss[0], ss[1]);
        })
    }
    
    var sortedKeys = Array.from(params.keys())
    sortedKeys.sort();
    
//    var l1 = ss[0].lastIndexOf('/');
//    var url = ss[0].substring(l1);
    var first = true;
    var qs
    console.log("All params:");
    for (var k of sortedKeys) {
        var s = k + "=" + params.get(k);
        qs = qs ? qs + "&" + s : s;
        console.log("key=" + k + " value=" + params.get(k));
    }
//    return qs ? url + "?" + qs : url;
//    return encodeURIComponent(qs);
    return escape(qs);
}

//function calcMd5() {
//    var contentType = request.headers["content-type"];
//    if (request.data && !contentType.startsWith('application/x-www-form-urlencoded')) {
//        var data = request.data;
//        var md5 = CryptoJS.MD5(data);
//        var md5String = md5.toString(CryptoJS.enc.Base64);
//        console.log("data:" + data + "\nmd5:" + md5String);
//        return md5String;
//    } else {
//        return "";
//    }
//}

function createUuid() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
        return v.toString(16);
    });
}

6.调试

6.1 充分利用wireshark

把sdk成功执行的请求抓出来,看header、body、parameter部分,在postman中配置成一样的

6.2 充分利用postman的console

利用console.log打印出变量值,注意打印对象值的时候不要把对象用加号和字符串连接进来。
比如

    console.log(request);
    console.log("request.data");
    console.log(request.data);
    console.log("pm.request");
    console.log(pm.request);

这些对象的结果非常清晰直观。

把textToSign打印出来,当签名有问题时,用于和服务器端返回的reponse中提示的签名字符串比较。

6.3 仔细看reponse中的提示

比如签名有问题时,提示消息中有"Message": "Specified signature is not matched with our calculation. server string to sign is:后面是签名字符串,看看和自己的testToSign是否一样

7.存在问题

偶尔会提示Specified signature is not matched,再点一下send就好了,没再找原因了。

相关文章
|
7天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
22 2
|
28天前
|
存储 缓存 搜索推荐
Lazada淘宝详情API的价值与应用解析
在电商行业,数据是驱动业务增长的核心。Lazada作为东南亚知名电商平台,其商品详情API对电商行业影响深远。本文探讨了Lazada商品详情API的重要性,包括提供全面准确的商品信息、增强平台竞争力、促进销售转化、支持用户搜索和发现需求、数据驱动决策、竞品分析、用户行为研究及提升购物体验。文章还介绍了如何通过Lazada提供的API接口、编写代码及使用第三方工具实现实时数据获取。
55 3
|
2月前
|
存储 JSON API
深入解析RESTful API设计原则与实践
【9月更文挑战第21天】在数字化时代,后端开发不仅仅是编写代码那么简单。它关乎于如何高效地连接不同的系统和服务。RESTful API作为一套广泛采用的设计准则,提供了一种优雅的解决方案来简化网络服务的开发。本文将带你深入了解RESTful API的核心设计原则,并通过实际代码示例展示如何将这些原则应用于日常的后端开发工作中。
|
8天前
|
API 数据安全/隐私保护
抖音视频,图集无水印直链解析免费API接口教程
该接口用于解析抖音视频和图集的无水印直链地址。请求地址为 `https://cn.apihz.cn/api/fun/douyin.php`,支持POST或GET请求。请求参数包括用户ID、用户KEY和视频或图集地址。返回参数包括状态码、信息提示、作者昵称、标题、视频地址、封面、图集和类型。示例请求和返回数据详见文档。
|
10天前
|
域名解析 网络协议 测试技术
IP、掩码、网关、DNS1、DNS2到底是什么东西,ping telnet测试
理解IP地址、子网掩码、默认网关和DNS服务器的概念是有效管理和配置网络的基础。通过使用ping和telnet命令,可以测试网络连通性和服务状态,快速诊断和解决网络问题。这些工具和概念是网络管理员和IT专业人员日常工作中不可或缺的部分。希望本文提供的详细解释和示例能够帮助您更好地理解和应用这些网络配置和测试工具。
36 2
|
25天前
|
缓存 监控 API
抖音抖店 API 请求获取宝贝详情数据的调用频率限制如何调整?
抖音抖店API请求获取宝贝详情数据的调用频率受限,需遵循平台规则。开发者可通过提升账号等级、申请更高配额、优化业务逻辑(如缓存数据、异步处理、批量请求)及监控调整等方式来应对。
|
27天前
|
缓存 负载均衡 API
抖音抖店API请求获取宝贝详情数据、原价、销量、主图等参数可支持高并发调用接入演示
这是一个使用Python编写的示例代码,用于从抖音抖店API获取商品详情,包括原价、销量和主图等信息。示例展示了如何构建请求、处理响应及提取所需数据。针对高并发场景,建议采用缓存、限流、负载均衡、异步处理及代码优化等策略,以提升性能和稳定性。
|
7天前
|
JSON API 数据格式
携程API接口系列,酒店景点详情请求示例参考
携程API接口系列涵盖了酒店预订、机票预订、旅游度假产品预订、景点门票预订等多个领域,其中酒店和景点详情请求是较为常用的功能。以下提供酒店和景点详情请求的示例参考
|
1月前
|
JavaScript 前端开发 Java
多种语言请求API接口方法
每种语言和库的选择取决于具体需求、项目环境以及个人偏好。了解这些基本方法,开发者就可以根据项目需求选择合适的语言和库来高效地与API交互。
38 1
|
1月前
|
JSON API 数据格式
postman如何发送json请求其中file字段是一个图片
postman如何发送json请求其中file字段是一个图片
122 4

推荐镜像

更多