MeterSphere 在 MeterSphere 中使用预执行脚本功能生成接口认证签名

MeterSphere 小助手 · 2020年09月07日 · 最后由 lizhouquan 回复于 2020年11月26日 · 3999 次阅读

前言

目前许多系统的 API 都通过 accessKey + secretKey 生成签名的方式来完成认证,对于这样的接口请求,我们可以先使用脚本生成一个签名后再将其添加到 MeterSphere 的自定义变量中,但是大部分系统的签名信息都与当前时间或请求参数有关,如果每次都需要去执行额外的脚本进行生成就显得十分麻烦。接下来我们就以 MeterSphere 自己的 API 接口为例,介绍一下如何在 MeterSphere 中通过预执行脚本的功能,自动为接口请求生成认证签名。

MeterSphere API 认证机制

如图所示,我们可以在 MeterSphere 的 “个人信息”→“API keys“ 页面创建 API 认证密钥。通过 API 调用 MeterSphere 接口时需要在请求头中传入 accessKey 及 signature 请求头,其中 accessKey 即为创建 API Key 时生成的 Access Key,signature 通过如下加密算法得出

加密算法:AES
加密模式:CBC
填充方式:PKCS5Padding
加密字符串:Access Key| 当前时间戳 (生成的签名只在特定的时间范围内有效,默认 30 分钟)
加密密钥:创建 API Key 时生成的 Secret Key
AES IV:创建 API Key 时生成的 Access Key

操作步骤

在 MeterSphere 创建接口测试,并在场景配置中添加 API 认证需要用到的 accessKey 和 secretKey 作为自定义变量

在该场景中添加一个 HTTP 请求,调用 GET/project/listAll 获取项目列表接口,在该请求的预执行脚本中,添加以下代码生成签名并将签名值存入 signature 变量中

import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

String accessKey = "${accessKey}";
String secretKey = "${secretKey}";

public static String aesEncrypt(String src, String secretKey, String iv) throws Exception {
    byte[] raw = secretKey.getBytes("UTF-8");
    SecretKeySpec secretKeySpec = new SecretKeySpec(raw, "AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    IvParameterSpec iv1 = new IvParameterSpec(iv.getBytes());
    cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, iv1);
    byte[] encrypted = cipher.doFinal(src.getBytes("UTF-8"));
    return Base64.encodeBase64String(encrypted);
}

try {
    //调用加密算法生成签名
    String signature = aesEncrypt(accessKey + "|" + System.currentTimeMillis(), secretKey, accessKey);
    //将签名值存入 signature 变量中
    vars.put("signature",signature);
} catch (Exception e) {
    e.printStackTrace();
}

在该请求中添加 accessKey 及 signature 两个请求头,accessKey 的值为在场景中配置的 accessKey 变量,signature 的值为上一步通过预执行脚本计算出来的签名

在该场景中再次添加一个 HTTP 请求作为对比,同样调用 GET /project/listAll 接口,不添加 accessKey 及 signature 请求头

保存并执行该接口测试,在生成的报告中可以看到,添加了认证请求头的接口调用成功,没有添加认证请求头的接口调用失败

共收到 13 条回复 时间 点赞

你这个相当于在请求前 一个前置处理器 来处理参数签名吧

Wan 回复

是的

点赞。

预执行脚本兼容 Python 吗?

guolong123 回复

嗯,现在支持 python 2.7

体验了一下,发现每一个接口用例都需要写一遍请求头、请求地址、请求方法等接口描述信息;这样的话,我本身一个接口有多个用例的情况下,只有数据需要修改,这个没有做到用例的接口和数据分开。写起来还是比较麻烦的。

guolong123 回复

在现在的版本里,可以通过复制请求、配置场景通用的请求头、配置环境信息等方法来处理下。
针对单接口用例这种使用场景的支持,我们正在开发中,整体变化会比较大,预计在 12 月份更新,欢迎持续关注下。


我用 Python 写的预置脚本并没有生效啊?

import time



def _dispose_params(dict, timestamp=None):
    """
    将参数按照规则排序
    :param dict:
    :return:
    """
    if not timestamp:
        timestamp = int(time.time() * 1000)
    string = 'timestamp={}'.format(timestamp)
    if not dict:
        return string
    sorted_list = sorted(dict.items(), key=lambda item: item[0], reverse=False)
    l = []
    for i in sorted_list:
        if i[1] or i[1] == 0:
            l.append(i)

    if not l:
        return string
    sorted_str = [str(x[0]) + "=" + str(x[1]) for x in l]
    string = string + "&" + '&'.join(sorted_str)
    return string


def _rsa(message):
    """
    使用rsa非对称进行签名
    :param message: 要加密的字符串
    :return:
    """
    privite_key = """-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQC8qQANiKqjB3XtYvC7MTvCqVZu9swVAls7Qh0TszpKI+E8RuBZ
Fb/lEUYDSTjhq8i93GOlCOt2FfsOFrw3tqQ8CNq53fOsBgOJkUbFVOKW8VWF683n
rQ4kA17w+EGSPorwC5WU/gN28MN17T4hiG1gYl5UVXae4bIVbdqwCzuDlwIDAQAB
AoGAZ00ubzqvHHegVeyAHi+2Jj+syTOvya0xoT/xRfE0cJY25Vd1zrG/EkGkEXaN
g9wZHgTINvysWUtt3WgLhYInaJn1xU5GGVHPQKWsD87vgRapcTkCT7gjhzGNi/D8
VKcl7izibrPObJ0uCLylDa9zZ5sel46z4Mm4Ji3CsVnOQ7kCQQD0XXQyDgK8p6D8
VNpufkJiiAYysfg8hDJPyiIJx9d0zZIDfQ4ESAcaXf5TqqU4l5KvpN5g80kcy5uM
92ZkO75tAkEAxaSQU9GwcxwSNWrBWibHYka54e7ASdfCI6KjLsOnWEGxFCQAYrpe
9FGFekYorXPj7HNYDgrKP0hlj8/eUbr3kwJBAK5XQoA3uMIqAlQkoda1I92yWGGw
0X8zfcWpW9GIj3JY+/SbMvXhAZhRORMuK0SN92xy/ulCiYYOycKv+5BFg50CQQC4
wa1prYuVD5i19qXo8HdSl2sMmXOHk/oX9eE1xhCZoE841K7HnBFHsy1jsFI2Uy+t
yeV2uv91SOgoF9lr7auhAkAeftJnpmveDObrG4HCdbSRZBu2zbXIvFDiXZkfVIge
MR1LJ19k2zvIT64vXJXz1LJfAkVb9cpegz037Qbl5+4r
-----END RSA PRIVATE KEY-----""".encode()
    privkey = rsa.PrivateKey.load_pkcs1(privite_key)
    crypto_text = rsa.sign(message.encode(), privkey, 'MD5')
    crypto_text = base64.b64encode(crypto_text)
    print('base64:', crypto_text)
    return crypto_textvars.put("variable_name", "variable_value")

def sign():
    clientId = '8d04c39446e17ebbc6f13abc61383b11'
    timestamp = str(int(time.time() * 1000))
    message = _dispose_params(params, timestamp)
    sign = _rsa(message)
    vars.put("clientId", clientId)
    vars.put("timestamp", timestamp)
    vars.put('sign', sign)

sign()
guolong123 回复

知道问题了,是 Python 第三方包的问题。这种依赖第三方依赖库的问题有解决办法吗?

要是支持 python3.x 就好了,毕竟 2 已经不维护了

guolong123 回复

现在可以通过将包上传到服务器容器中的某个目录,然后在脚本中添加 sys.path 路径的方式来解决一下,后续我们尽可能提供更易用的方式。

个人觉得这个功能可以支持提交 requirements.txt,后台服务自动安装依赖库。然后当脚本执行出错时应该将错误抛出到页面。

我使用的 1.4.3 版本, 好像使用 python 处理提取数据还是没有生效

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册