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

Posted

技术标签:

【中文标题】邮递员 - 在预请求脚本中访问完整的请求正文(以计算哈希)【英文标题】:Postman - access full request body (to calculate hash) in pre-request script 【发布时间】:2018-09-05 16:03:49 【问题描述】:

我正在尝试在 PostMan 中重新创建 C# DelegatingHandler。 我创建了计算 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 内部的正文哈希差异。

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

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

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

我有 C# 中的工作代码,我不想用 Postman 重新创建相同的请求。

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

我在 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("012345", _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;
    

【问题讨论】:

可能只是不可能(不支持):github.com/postmanlabs/postman-app-support/issues/1050 @Evk 感谢您找到这个。可悲的是,我将不得不找到替代方案:/ @Evk 在内部 Postman 从正文计算哈希值:github.com/postmanlabs/postman-request/blob/master/lib/…,但这在预请求脚本中不可用。 你的哈希函数是 SHA512,链接的源代码表明它只计算 SHA1。 @Evk 哈希函数无关紧要——我可以改变它。我的主要问题是我无法在预请求脚本中访问请求正文。 【参考方案1】:

之前引用的问题 (#1050) 似乎仅适用于二进制/文件模式的请求正文 (RequestBody.MODES.file)。 RequestBody API 提供了我需要的类似用例:https://www.postmanlabs.com/postman-collection/RequestBody.html

我相信,如果您在预请求脚本中引用 pm.request.body,它将提供您正在寻找的内容。具体来说,pm.request.body.toString() 似乎提供了最终将出现在请求中的实际 x-www-url-encoded 字符串(尽管,如果您在请求参数中使用 环境变量,这些将被发出未解析,例如,@ 987654327@)。

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

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

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

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

【讨论】:

以上是关于邮递员 - 在预请求脚本中访问完整的请求正文(以计算哈希)的主要内容,如果未能解决你的问题,请参考以下文章

如何在预请求脚本中更改 Postman 环境?

如果响应正文中的 id 具有依赖性,如何使用预请求脚本在邮递员中设置 2 个集合变量?

如何将邮递员请求正文保存在一个地方并在运行时通过

请求后执行的邮递员预请求脚本

在spring mvc测试中访问请求体和请求头

在预请求脚本中从集合中调用请求