通过Postman实现专有云云解析API网关的请求签名与调试-阿里云开发者社区

开发者社区> 央掘魔罗> 正文

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

简介: 本文主要使用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就好了,没再找原因了。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
SpringBoot-18-之AOP+log4j 记录访问请求信息
org.springframework.boot spring-boot-starter-aop log4j log4j 1.
1073 0
阿里云openapi签名实现代码(基于C#)
部分开发者在接触阿里云openAPi调用的时候,Signature的构造和生成一直都是一只拦路虎,本文中将基于C#,和点播的APi:getPlayAuth 实现签名的构造,仅供大家参考。
553 0
linux系统性能分析命令ps.pstree及top详解
linux系统常用的性能分析命令有ps  pstree及top等,今天我们详细看下各命令的使用 1.ps ps命令是平时比较常用的查看进程的命令,ps 是显示瞬间进程的状态,并不动态变化;如果想对进程运行时间监控,需要用 top 工具。
1109 0
通过Postman实现专有云云解析API网关的请求签名与调试
本文主要使用Postman调试API网关的方法,文中使用Pre-request Script实现了阿里专有云云解析API网关的签名算法。
357 0
今日直播预告:平头哥合作伙伴计划即1520计划全解析
7月2日(周四)16:00,平头哥半导体生态运营负责人——大伍,讲解《平头哥芯片开放社区(OCC)合作伙伴计划即1520计划全解析》。
235 0
Spark in action on Kubernetes - Spark Operator的原理解析
在上篇文章中,向大家介绍了如何使用Spark Operator在kubernetes集群上面提交一个计算作业。今天我们会继续使用上篇文章中搭建的Playgroud进行调试与解析,帮助大家更深入的理解Spark Operator的工作原理。
12708 0
Java集合之HashMap源码解析
HashMap HashMap 是 Map 的一个实现类,它代表的是一种键值对的数据存储形式。 大多数情况下可以直接定位到它的值,因而具有很快的访问速度,但遍历顺序却是不确定的。
695 0
阿里云openapi签名实现代码(基于Python)
部分开发者在接触阿里云openAPi调用的时候,Signature的构造和生成一直都是一只拦路虎,本文中将基于Python,和点播的APi:getPlayAuth 实现签名的构造,仅供大家参考。
758 0
+关注
1
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载