使用 UrlFetch 通过 Google Apps 脚本进行摘要式身份验证

问题描述 投票:0回答:4

我正在尝试使用 Google Apps 脚本的 UrlFetch 访问受摘要式身份验证保护的 URL。

我设法使用基本身份验证访问 URL:

 var options =
{
  "method" : "get",
  "headers" : {
    "Authorization" : "Basic <Base64 of user:password>"  
  },
};

var response = UrlFetchApp.fetch("http://www.example.com/service/using/basic", options);

我成功地根据文档中的example使用OAuth进行访问。

现在的问题是我需要与正在实现 Digest 的站点具有相同的功能。我可以通过以下curl访问它:

curl -v --digest --user user:password http://www.example.com/service/using/digest

已更新

如果我尝试简单地打电话

var options =
{
  "method" : "get",
  "headers" : {
    "Authorization" : "Digest " + Utilities.base64Encode( Utilities.computeDigest(user:password) )
  },
};

由于摘要式身份验证的质询-响应机制,它不起作用。

我可以使用任何实现此类功能的 JavaScript 库将其与 UrlFetch 结合使用吗?或者直接通过 UrlFetch 本身?

google-apps-script digest-authentication
4个回答
1
投票

HTTP 摘要授权比您目前所拥有的要复杂一些。查看 wiki 页面 或查看此 示例 Python 实现 了解更多信息。

使用 Henrique 提到的实用程序方法,应该可以通过 Apps 脚本实现摘要身份验证。至于处理算法的质询/响应部分,您可以使用尚未记录的 UrlFetchApp 选项

muteHttpExceptions
,如所示


0
投票

有内置的 Utilities.computeDigestbase64Encode。这可能就是您正在寻找的。


0
投票

这对我有用。 Google Sheet 脚本中摘要身份验证的实现:

function fetchDataWithDigestAuth() {
  var apiUrl = "url"; // Replace with your API endpoint
  var username = "your_username"; // Replace with your API username
  var password = "your_password"; // Replace with your API password

  var response = UrlFetchApp.fetch(apiUrl, { muteHttpExceptions: true });
  console.log("response.getResponseCode()" + response.getResponseCode());
  if (response.getResponseCode() === 401) {
    var authHeader = response.getHeaders()["WWW-Authenticate"];
    var digestAuth = createDigestAuth(username, password, authHeader);

    var options = {
      method: "get",
      headers: { "Authorization": digestAuth },
      muteHttpExceptions: true
    };

    response = UrlFetchApp.fetch(apiUrl, options);
  }

  var data = JSON.parse(response.getContentText());

  // Process the response data as needed
  Logger.log(data);
}

function createDigestAuth(username, password, authHeader) {
  var realm = extractHeaderValue(authHeader, "realm");
  var nonce = extractHeaderValue(authHeader, "nonce");
  var uri = extractHeaderValue(authHeader, "uri");
  var qop = extractHeaderValue(authHeader, "qop");
  var nc = "00000001";
  var cnonce = Utilities.getUuid();

  var ha1 = Utilities.computeDigest(
    Utilities.DigestAlgorithm.MD5,
    username + ":" + realm + ":" + password
  );
  var ha2 = Utilities.computeDigest(
    Utilities.DigestAlgorithm.MD5,
    "GET:" + uri
  );

  var testString = ha1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + ha2;

  var response = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, testString)
    .map(function (chr) { return (chr + 256).toString(16).slice(-2) })
    .join('')

  var digestAuth =
    'Digest username="' + username + '", ' +
    'realm="' + realm + '", ' +
    'nonce="' + nonce + '", ' +
    'uri="' + uri + '", ' +
    'qop=' + qop + ', ' +
    'nc=' + nc + ', ' +
    'cnonce="' + cnonce + '", ' +
    'response="' + response + '"';

  return digestAuth;
}

function extractHeaderValue(header, key) {
  var regex = new RegExp(key + '="(.*?)"', "i");
  var matches = header.match(regex);
  return matches ? matches[1] : '';
}

0
投票

我给了chatGPT这三个起源:

  1. 此 Python 食谱:https://code.activestate.com/recipes/302378-digest-authentication/
  2. 这里提供了@Xfox 的代码。
  3. digest-fetch Npm 库:https://www.npmjs.com/package/digest-fetch

它给了我一个工作代码:

class DigestClient {
  constructor(username, password) {
    this.username = username;
    this.password = password;
    this.nc = 0; // Nonce Count
  }

  fetch(apiUrl) {
    let response = UrlFetchApp.fetch(apiUrl, { muteHttpExceptions: true });
    
    if (response.getResponseCode() === 401) {
      let authHeader = response.getHeaders()["WWW-Authenticate"];
      let digestAuth = this.createDigestAuth(apiUrl, authHeader);
      
      let options = {
        method: "get",
        headers: { "Authorization": digestAuth },
        muteHttpExceptions: true
      };
      
      response = UrlFetchApp.fetch(apiUrl, options);
    }
    
    return response.getContentText();
  }

  createDigestAuth(apiUrl, authHeader) {
    this.nc++; // Increment Nonce Count
    let nc = ("00000000" + this.nc).slice(-8);
    let cnonce = Utilities.getUuid();

    let realm = this.extractHeaderValue(authHeader, "realm");
    let nonce = this.extractHeaderValue(authHeader, "nonce");
    let qop = this.extractHeaderValue(authHeader, "qop");
    let uri = apiUrl;

    let ha1 = this.md5(this.username + ":" + realm + ":" + this.password);
    let ha2 = this.md5("GET:" + uri);

    let response = this.md5(ha1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + ha2);

    return 'Digest username="' + this.username + '", ' +
           'realm="' + realm + '", ' +
           'nonce="' + nonce + '", ' +
           'uri="' + uri + '", ' +
           'qop=' + qop + ', ' +
           'nc=' + nc + ', ' +
           'cnonce="' + cnonce + '", ' +
           'response="' + response + '"';
  }

  extractHeaderValue(header, key) {
    let regex = new RegExp(key + '="(.*?)"', "i");
    let matches = header.match(regex);
    return matches ? matches[1] : '';
  }

  md5(string) {
    return Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, string)
                    .map(function (chr) {
                      return (chr + 256).toString(16).slice(-2);
                    }).join('');
  }
}

// Usage example
function testDigestClient() {
  let client = new DigestClient('your_username', 'your_password');
  let data = client.fetch('your_api_url');
  Logger.log(data);
}

© www.soinside.com 2019 - 2024. All rights reserved.