我正在尝试使用 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 本身?
HTTP 摘要授权比您目前所拥有的要复杂一些。查看 wiki 页面 或查看此 示例 Python 实现 了解更多信息。
使用 Henrique 提到的实用程序方法,应该可以通过 Apps 脚本实现摘要身份验证。至于处理算法的质询/响应部分,您可以使用尚未记录的 UrlFetchApp 选项
muteHttpExceptions
,如所示。
有内置的 Utilities.computeDigest 和 base64Encode。这可能就是您正在寻找的。
这对我有用。 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] : '';
}
我给了chatGPT这三个起源:
它给了我一个工作代码:
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);
}