教你在Postman中对请求参数进行加密加签预处理!

一、序言

开发和测试同学平时多少都会用Postman进行后台接口调试,尤其是请求经过网关时,通常数据都需要进行加密传输,以及加签等操作。

这时候需要用到Pre-requestPost-response脚本,这篇文章将会详细介绍关于Postman这些脚本的小细节。


二、Pre-request预请求脚本语法

1、Pre-request脚本简介

通过名字就可以看出来,Pre-request脚本会在请求发送到服务端前执行,如下:

预请求脚本可以在Collections(集合)、Folder(文件夹)和API接口上定义,如果三者都定义了预请求脚本,那么执行顺序为:Collections > Folder > API接口

备注:Postman运行环境基于Node.js,因此相关JS语法可以无缝迁移。

2、Postman中的变量

Postman中支持各种作用域变量定义,支持的作用域有Global(全局)、Collection(集合)、Environment(环境变量)、DataLocal(脚本中本地变量),变量访问优先级从内到外,依次递增,如下:


通过pm内置对象可以访问各作用域的变量,如pm.gloabalpm.environment等等,如下:

// collection var 'score' = 1
// environment var 'score' = 2

// first request run
console.log(pm.variables.get('score')); // outputs 2
console.log(pm.collectionVariables.get('score')); // outputs 1
console.log(pm.environment.get('score')); // outputs 2

// second request run
pm.variables.set('score', 3);// local var
console.log(pm.variables.get('score')); // outputs 3

// third request run
console.log(pm.variables.get('score')); // outputs 2

备注:通过pm.variables对象获取变量时会根据上图中的优先级从里到外获取。

除此之外,Postman中还内置了一些动态变量,这些动态变量可以通过{{$guid}}花括号进行引用,如下:


在脚本中可以通过pm.variables.replaceIn()方法进行引用,如下:

const stringWithVars = pm.variables.replaceIn("Hi, my name is {{$randomFirstName}}");
console.log(stringWithVars);

3、Postman中的全局对象

Postman中支持常用的Javascript全局对象,可以看到大部分都支持。


除此之代,Postman还内置了一些三方库,比如moment.jsuuid.js等,使用内置三方库格式如下:

const moment = pm.require('moment');
const now = moment().format('YYYY-MM-DD HH:mm:ss');
console.log("当前时间: "+ now);

三、Pre-request脚本实现请求参数加密加签

目前前端常见的加解密库有CryptoJSjsencryptjsrsasign,关于如何通过这些库实现对称加解密、非对称加解密&加验签的细节可以参考我之前的两篇文章:

  • 前端CryptoJS和Java后端数据互相加解密(AES)
  • 前后端RSA互相加解密、加签验签、密钥对生成

接下来的例子将以RSA非对称加密和加签为例,通过postman预请求脚本进行操作。

1、引入三方库

这里我使用了jsencryptjsrsasign两个库,首先定义了两个全局变量,变量的值为对应库的js脚本,如下:

通过全局变量获取到脚本内容后,通过eval()函数动态加载执行,就能获取到相关全局对象。

备注:这两个库的脚本可直接在https://cdnjs.***/上搜索获取。

2、定义公私钥环境变量

开发、测试环境使用的公私钥都不一样,最好用环境变量进行区分,这里我定义了两个环境变量来存储公钥和私钥,如下:

备注:前端第三方库要求publicKeyprivateKey的格式为标准pem格式,需要带-----BEGIN PUBLIC KEY-----前缀。

3、预请求脚本实操

这里的预请求脚本可以作用在Collections文件夹或者单个接口上,我一般全局定义在某个Collections上。

脚本内容如下:

/**
 * RSA加密
 */
function encryptByRSA(publicKey, plainText) {
    eval(pm.globals.get('jsencrypt'));
    const encryptor = new window.JSEncrypt();
    encryptor.setPublicKey(publicKey);
    return encryptor.encrypt(plainText);
}

/**
 * RSA SHA256加签
 */
function signBySHA256WithRSA(privateKey, msg) {
    eval(pm.globals.get('jsrsasign'));
    const key = KEYUTIL.getKey(privateKey);
    const signature = new KJUR.crypto.Signature({
        alg: "SHA256withRSA",
    });
    signature.init(key);
    signature.updateString(msg);
    // 签名后的为16进制字符串,这里转换为16进制字符串
    return hextob64(signature.sign());
}

function sign(data) {
    // 获取私钥
    const privateKey = pm.environment.get('privateKey');
    const signatureStr = signBySHA256WithRSA(privateKey, data);
    console.log(`生成的签名为: ${signatureStr}`)
    return signatureStr;
}

function encrypt() {
    // 获取公钥
    const publicKey = pm.environment.get('publicKey');

    // 获取请求体
    const requestBody = pm.request.body.raw;
    if (!requestBody) {
        return;
    }

    console.log("请求数据:" + requestBody);
    try {
        // 将请求体解析为JSON对象
        const jsonData = JSON.parse(requestBody);

        // 要加密的字段名 (替换为实际需要加密的字段名)
        const encryptedFields = ["password", "encryptPwd", "newEncryptPwd", "paymentPin", "loginPwd"];
        const filterFields = encryptedFields.filter(v => jsonData[v]);
        if (!filterFields) {
            return;
        }

        filterFields.forEach(field => {
            // 加密指定字段
            const plainText = jsonData[field];
            const encryptedValue = encryptByRSA(publicKey, plainText)
            console.log(`字段[${field}]加密后的内容为:${encryptedValue}`)

            // 更新JSON中的值
            jsonData[field] = encryptedValue;
            // 将修改后的JSON转回字符串并更新请求体
            pm.request.body.raw = JSON.stringify(jsonData);
        })

        return jsonData;
    } catch (error) {
        console.error("加密过程发生错误:", error);
    }
}


// 定义全局变量
const navigator = {};
const window = {};

const jsonData = encrypt();

这里需要注意的是,脚本最后为什么要定义navigatorwindow两个全局变量,原因如下:

  • JSEncrypt依赖于windownavigator对象,没有这两对象访问会失败。
  • 前面说过Postman基于Node.js运行,而windownavigator是浏览器特有的对象。

备注:关于pm.request和pm.response等对象的数据结构可参考Postman变量结构说明。

转载请说明出处内容投诉
CSS教程网 » 教你在Postman中对请求参数进行加密加签预处理!

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买