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

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
云原生 API 网关,700元额度,多规格可选
简介: 本文主要使用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就好了,没再找原因了。

相关文章
|
15天前
|
存储 缓存 搜索推荐
Lazada淘宝详情API的价值与应用解析
在电商行业,数据是驱动业务增长的核心。Lazada作为东南亚知名电商平台,其商品详情API对电商行业影响深远。本文探讨了Lazada商品详情API的重要性,包括提供全面准确的商品信息、增强平台竞争力、促进销售转化、支持用户搜索和发现需求、数据驱动决策、竞品分析、用户行为研究及提升购物体验。文章还介绍了如何通过Lazada提供的API接口、编写代码及使用第三方工具实现实时数据获取。
37 3
|
2月前
|
存储 JSON API
深入解析RESTful API设计原则与实践
【9月更文挑战第21天】在数字化时代,后端开发不仅仅是编写代码那么简单。它关乎于如何高效地连接不同的系统和服务。RESTful API作为一套广泛采用的设计准则,提供了一种优雅的解决方案来简化网络服务的开发。本文将带你深入了解RESTful API的核心设计原则,并通过实际代码示例展示如何将这些原则应用于日常的后端开发工作中。
|
27天前
|
JSON API 数据格式
postman如何发送json请求其中file字段是一个图片
postman如何发送json请求其中file字段是一个图片
91 4
|
3月前
|
消息中间件 Kafka API
【Kafka消费新风潮】告别复杂,迎接简洁之美——深度解析Kafka新旧消费者API大比拼!
【8月更文挑战第24天】Apache Kafka作为一个领先的分布式流处理平台,广泛用于实时数据管道和流式应用的构建。随着其发展,消费者API经历了重大更新。旧消费者API(包括“低级”和“高级”API)虽提供灵活性但在消息顺序处理上存在挑战。2017年引入的新消费者API简化了接口,自动管理偏移量,支持更强大的消费组功能,显著降低了开发复杂度。通过对比新旧消费者API的代码示例可以看出,新API极大提高了开发效率和系统可维护性。
129 58
|
21天前
|
弹性计算 网络协议 网络安全
内网DNS解析&VPN网关联动实现云上访问云下资源
内网DNS解析&VPN网关联动实现云上访问云下资源
|
2月前
|
监控 API 开发工具
探索 Postman:API 开发的瑞士军刀
在现代软件开发中,API 起着关键作用,连接前后端应用及微服务架构。Postman 是一款流行的一站式 API 开发工具,支持 REST、GraphQL 和 SOAP 等协议,具备构建、测试、调试 API 的强大功能,包括请求构建器、环境变量管理、测试脚本编写、文档生成及 Mock 服务器创建等。本文详细介绍 Postman 的核心功能与进阶技巧,助你提高 API 开发效率。
|
27天前
|
缓存 网络协议 API
【Azure 环境】请求经过应用程序网关,当响应内容大时遇见504超时报错
应用程序网关的响应缓冲区可以收集后端服务器发送的全部或部分响应数据包,然后再将它们发送给客户端。 默认在应用程序网关上启用响应缓冲,这对于适应缓慢的客户端很有用。
|
1月前
|
JSON JavaScript API
商品详情数据接口解析返回的JSON数据(API接口整套流程)
商品详情数据接口解析返回的JSON数据是API接口使用中的一个重要环节,它涉及从发送请求到接收并处理响应的整个流程。以下是一个完整的API接口使用流程,包括如何解析返回的JSON数据:
|
2月前
|
XML JSON API
淘宝京东商品详情数据解析,API接口系列
淘宝商品详情数据包括多个方面,如商品标题、价格、图片、描述、属性、SKU(库存量单位)库存、视频等。这些数据对于买家了解商品详情以及卖家管理商品都至关重要。
|
3月前
|
数据采集 API 开发工具
淘系商品详情数据解析(属性youhui券sku详情图等)API接口开发系列
在电商领域,特别是像淘宝(淘系)这样的平台,商品详情数据对于商家、开发者以及数据分析师来说至关重要。这些数据包括但不限于商品属性、优惠券信息、SKU(Stock Keeping Unit)详情、商品图片、售后保障等。然而,直接访问淘宝的内部API接口通常需要特定的权限和认证,这通常只对淘宝的合作伙伴或内部开发者开放。 不过,对于需要这些数据的第三方开发者或商家,有几种方式可以间接获取或解析淘系商品详情数据: ——在成长的路上,我们都是同行者。这篇关于商品详情API接口的文章,希望能帮助到您。期待与您继续分享更多API接口的知识,请记得关注Anzexi58哦!

推荐镜像

更多