3

我在 Delphi XE 中使用 DataSnap 创建了一个 REST Web 服务。我正在使用 XMLHttpRequest JavaScript 对象调用服务器方法。我在 XMLHttpRequest Open 方法的第四个和第五个可选参数中传递用户名和密码以进行身份​​验证。

当我在 Delphi 中加载我的 DataSnap 服务器并将调试器附加到 IIS(我使用的是 IIS 7.5)时,我可以看到第一次调用其中一个服务器方法会导致 DSAuthenticationManager 的 OnUserAuthenticate 事件被调用两次。

在第一次调用中,OnUserAuthenticate 事件处理程序的用户和密码参数是空字符串。在第二次调用中,我在 XMLHttpRequest Open 方法中传递的用户名和密码出现在这些参数中。

一旦用户通过身份验证,任何使用 XMLHttpRequest 对任何服务器方法的后续调用都会导致对 OnUserAuthenticate 事件处理程序的一次调用,用户的用户名和密码分别出现在用户和密码参数中。

我检查了许多 DataSnap 类的源代码,但没有找到会导致这种效果的代码,这使我认为这种行为可能源自浏览器或 IIS。

为什么 OnUserAuthenticate 在第一次服务器方法调用时被调用两次,是什么产生了这种效果?


为了回应 Mat DeLong 的回答,我展示了我用来调用服务器方法的通用函数之一。此特定方法进行同步调用。baseURL 参数通常是类似于http://mydomain.com/myservice/myservice.dll/datasnap/rest/tservermethods1的字符串,方法可能是 myservermethod。附加参数用于将参数传递给服务器方法:

function getJSONSync(baseURL, method) {
  var request = new XMLHttpRequest();
  var url = baseURL + method;
  for (var i= 2; i < arguments.length; i++) {
    url += "/" + arguments[i];
  }
  request.open("GET", encodeURI(url), false);
  request.send(null);
  if (request.status != 200) {
    throw new Error(request.statusText);
  } 
  return jQuery.parseJSON(request.responseText);
}

编辑:Lex Li 似乎是正确的,IIS 仅在第一次请求失败时才使用基本身份验证。此外,Mat 正确的是 OnUserAuthenticate 每个会话只应调用一次。在检查了他提供的链接后,很明显我未能捕获并返回会话 ID。这是一个有效的代码示例,这次是异步的。它捕获 sessionid 并将其存储在隐藏字段中。然后,它在 Pragma 标头中的每个后续调用中将该会话 ID 传递回。

function getJSONAsync(callback, baseURL, method) {
  var request = new XMLHttpRequest();
  var timedout = false;
  request.onreadystatechange = function() {
     if (request.readyState !== 4) { return }
    if (timedout) { return }
    if (request.status >= 200 && request.status < 300)
    {
      callback(jQuery.parseJSON(request.responseText));
      //store the sessionid in a hidden field
      $("#sessionid").val(request.getResponseHeader('Pragma').split(',')[0].split("=")[1]);
    }
  }
  var url = baseURL + method;
  for (var i= 3; i < arguments.length; i++) {
    url += "/" + arguments[i];
  }
  request.open("GET", encodeURI(url), true);
  //pass the sessionid in the Pragma header, if available
  if (! $("#sessionid").val() == "") {
    request.setRequestHeader("Pragma", "dssession="+$("#sessionid").val());
  }
  //time out if request does not complete in 10 seconds
  var timer = setTimeout(function() { timedout = true; request.abort(); }, 10000);
  request.send(null);
}

编辑:当我第一次发布这两个代码示例时,我在 XMLHttpRequest open 方法的第四个和第五个参数中包含了示例用户名和密码。我在测试期间明确地传递了这些值。不建议传递这样的参数,因此我在此编辑中删除了这些参数。如果您省略这些密码,并且不使用其他方法(例如在您的第一个 REST 服务器方法调用的标头中)传递它们,浏览器将显示一个对话框,询问用户此信息。提供用户名和密码后,浏览器将在会话的剩余部分中记住此信息。如果您关闭然后重新打开浏览器并调用另一个 REST 服务器方法,您将再次被要求输入用户名和密码。这当然假设,

4

2 回答 2

3

嗯,这是其他技术(例如 ASP.NET)的典型 IIS 行为。

初始请求不包含您的用户凭据,并触发 401 响应。然后第二个请求确实发送了您的用户凭据,然后 IIS 可以返回 200。

不确定这是否适用于 DataSnap,但如果您捕获网络数据包,您可能会发现它。

http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.preauthenticate.aspx

于 2011-08-06T12:20:46.570 回答
2

我无法确定为什么您会在没有看到服务器代码以及与 XHR 一起使用的确切 JavaScript 的情况下看到这种行为。OnUserAuthenticate 旨在为会话调用一次。

您可以在此处查看 XE DataSnap REST 协议:http: //docwiki.embarcadero.com/RADStudio/en/DataSnap_REST_Messaging_Protocol#Session_Information

第一次调用的目的是返回一个会话 ID,并且所有未来的调用都应该指定它。如果这样做了,除了第一个调用之外的所有调用都使用给定的会话 ID 来跳过身份验证过程,并且只转到 OnUserAuthorize 事件(如果分配了一个)。

如果您看到 OnUserAuthenticate 被调用了两次,这可能是一个错误,除非它在 ​​XHR 或 IIS 配置中有些奇怪。

如果你能提供更多细节,我很乐意仔细看看。

于 2011-08-05T19:24:55.393 回答