首页 文章

邮递员 - 在 pre-request 脚本中访问完整请求正文(计算哈希)

提问于
浏览
5

我正在尝试在 PostMan 中重新创建 C#DelegatingHandler。
我创建了 pre-request 脚本来计算 auth 标头值。

目前我的脚本如下所示:

function S4() {
    return (((1+Math.random())*0x10000)|0).toString(16).substring(1); 
}

function GetNonce() {
    return (S4() + S4() + S4()+ S4() + S4() + S4() + S4()+ S4()).toLowerCase();
}

function GetTimeStamp() {
    var d = new Date();
    return Math.round(d.getTime() / 1000);
}

function getAuthHeader(httpMethod, requestUrl, requestBody) {
    var CLIENT_KEY = postman.getEnvironmentVariable('hmac_user');
    var SECRET_KEY = postman.getEnvironmentVariable('hmac_key');
    var AUTH_TYPE = 'HMAC';

    requestUrl = requestUrl.replace(/{{(\w*)}}/g,function(str,key) {return environment[key]});
    requestUrl = requestUrl.toLowerCase();
    var requestTimeStamp = GetTimeStamp();
    var nonce = GetNonce();
    var bodyHash="";

    if (httpMethod == 'GET' || !requestBody) {
        requestBody = ''; 
    } else {
        var md5 = CryptoJS.MD5(requestBody);
        bodyHash = CryptoJS.enc.Base64.stringify(md5);
    }  

    var signatureRawData = [CLIENT_KEY, requestUrl, httpMethod, requestTimeStamp, nonce, bodyHash].join("");

    var key = CryptoJS.enc.Base64.parse(SECRET_KEY);
    var hash = CryptoJS.HmacSHA512(signatureRawData, key);
    var hashInBase64 = CryptoJS.enc.Base64.stringify(hash);

    var header = [CLIENT_KEY, hashInBase64, nonce, requestTimeStamp].join(":");

    return AUTH_TYPE+" "+header;
}

postman.setEnvironmentVariable('hmacAuthHeader', getAuthHeader(request.method, request.url, request.data));

这适用于没有任何正文的 GET 请求。然而,当我发送x-www-form-urlencoded请求时,我得到了未经授权的响应(401),因为 C#和 Postman 中的主体哈希差异。

Inside Postman request.data是一个 JSON 对象,但是当我调查 Fiddler 中的请求时,我看到它是以字符串形式发送的(见下面的截图)
在此输入图像描述

当我发送form-data时会发生同样的事情。 Inside Postman 我添加了 3 个字段,一个是字符串值,两个是文件。在 Fiddler 我可以看到完整的请求,但在 Postman 里面我无法访问这些文件(见下面的截图)
在此输入图像描述

我正在尝试访问完整的请求正文,因为我需要计算它的哈希值。

我在 C#中使用了代码,而不是我想用 Postman 重新创建相同的请求。

我的问题是:
如何在 pre-request 脚本中访问完整的请求正文?

我在 C#中使用此代码,它工作正常:

internal class HmacClientHandler : DelegatingHandler
{
    private readonly string _applicationId;
    private readonly string _applicationKey;

    public HmacClientHandler(string appId, string appKey)
    {
        _applicationId = appId;
        _applicationKey = appKey;
    }

    protected override async Task<HttpResponseMessage>SendAsync(HttpRequestMessage request,CancellationToken cancellationToken)
    {
        HttpResponseMessage response = null;
        string url = Uri.EscapeUriString(request.RequestUri.ToString().ToLowerInvariant());
        string methodName = request.Method.Method;

        DateTime epochStart = new DateTime(1970, 01, 01, 0, 0, 0, 0, DateTimeKind.Utc);
        TimeSpan timeSpan = DateTime.UtcNow - epochStart;
        string requestTimeStamp = Convert.ToUInt64(timeSpan.TotalSeconds).ToString();
        string nonce = Guid.NewGuid().ToString("N");

        string contentBase64String = string.Empty;

        if (request.Content != null)
        {
            byte[] content = await request.Content.ReadAsByteArrayAsync();
            MD5 md5 = MD5.Create();
            byte[] hash = md5.ComputeHash(content);
            contentBase64String = Convert.ToBase64String(hash);
        }

        string authenticationKeyString = string.Format("{0}{1}{2}{3}{4}{5}", _applicationId, url, methodName, requestTimeStamp, nonce, contentBase64String);
        var secretKeyBase64ByteArray = Convert.FromBase64String(_applicationKey);

        using (HMACSHA512 hmac = new HMACSHA512(secretKeyBase64ByteArray))
        {
            byte[] authenticationKeyBytes = Encoding.UTF8.GetBytes(authenticationKeyString);
            byte[] authenticationHash = hmac.ComputeHash(authenticationKeyBytes);
            string hashedBase64String = Convert.ToBase64String(authenticationHash);
            request.Headers.Authorization = new AuthenticationHeaderValue("HMAC", string.Format("{0}:{1}:{2}:{3}", _applicationId, hashedBase64String, nonce, requestTimeStamp));
        }

        response = await base.SendAsync(request, cancellationToken);
        return response;
    }
}

1 回答

  • 2

    先前引用的问题(#1050)似乎仅适用于 binary/file 模式(RequestBody.MODES.file)中的请求正文。 RequestBody API 提供了类似用例所需的内容:https://www.postmanlabs.com/postman-collection/RequestBody.html

    我相信如果您在 pre-request 脚本中引用pm.request.body,它将提供您正在寻找的内容。具体来说,pm.request.body.toString()似乎提供了最终将出现在请求中的实际x-www-url-encoded字符串(但是,如果在请求参数中使用环境变量,则这些将在未解析的情况下发出,e.g. ,{{variable_name}})。

    因此,对于上面的脚本,我将最后一行更改为:

    postman.setEnvironmentVariable('hmacAuthHeader', getAuthHeader(request.method, request.url, pm.request.body.toString()));
    

    ......这似乎提供了合理的 HMAC。请务必注意 hash/HMAC 协议的所有规则,包括参数排序,空格处理等。

    此外,我不确定RequestBody API 是否适用于 Postman 的所有版本,但在我的原生 Windows 和 OS X 版本上可以游戏。希望这可以帮助!

相关问题