Почему OnUserAuthenticate вызывается дважды на REST-сервере DataSnap? - PullRequest
3 голосов
/ 05 августа 2011

Я создал REST Web-сервис, используя DataSnap в Delphi XE. Я вызываю методы сервера, используя объект JavaScript XMLHttpRequest. Я передаю имя пользователя и пароль для аутентификации в четвертом и пятом необязательных параметрах метода Open XMLHttpRequest.

Когда я загружаю свой сервер DataSnap в Delphi и подключаю отладчик к IIS (я использую IIS 7.5), я вижу, что при первом вызове одного из серверных методов дважды вызывается событие OnUserAuthenticate DSAuthenticationManager.

В первом вызове параметры пользователя и пароля обработчика события OnUserAuthenticate являются пустыми строками. При втором вызове в этих параметрах появляются имя пользователя и пароль, которые я передал в методе XMLHttpRequest Open.

Как только пользователь прошел аутентификацию, любые последующие вызовы любого из серверных методов, использующих XMLHttpRequest, приводят к одному вызову обработчика события OnUserAuthenticate, при этом имя пользователя и пароль пользователя появляются в параметрах user и password соответственно.

Я проверил исходный код для ряда классов DataSnap и не нашел код, который вызвал бы этот эффект, что наводит меня на мысль, что это может происходить в браузере или IIS.

Почему OnUserAuthenticate вызывается дважды при первом вызове метода сервера, и что вызывает этот эффект?


В ответ на ответ Мэта Делонга я показываю одну из универсальных функций, которые я использую для вызова методов моего сервера. Этот конкретный метод делает синхронный вызов. Параметр 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);
}

Редактировать: Лекс Ли, похоже, прав, что IIS использует обычную аутентификацию только в случае неудачи при первом запросе. Кроме того, Мат прав, что OnUserAuthenticate следует вызывать только один раз за сеанс. После изучения предоставленной им ссылки стало очевидно, что мне не удалось перехватить и вернуть идентификатор сеанса. Вот пример кода, который работает, на этот раз асинхронно. Он захватывает sessionid и сохраняет его в скрытом поле. Затем он передает этот идентификатор сеанса обратно при каждом последующем вызове в заголовке Pragma.

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. Я передавал эти значения явно во время тестирования. Передавать такие параметры не рекомендуется, поэтому я удалил эти параметры в этом редактировании. Если вы опустите эти пароли и не передадите их с помощью других методов (например, в заголовке вашего первого вызова метода REST-сервера), браузер отобразит диалоговое окно, запрашивающее у пользователя эту информацию. После ввода имени пользователя и пароля браузер запомнит эту информацию до конца сеанса. Если вы закроете, а затем снова откроете браузер и вызовете другой метод сервера REST, вам снова будет предложено ввести имя пользователя и пароль. Это предполагает, конечно, что вы отклоняете недопустимых пользователей через обработчик события OnUserAuthenticate.

Ответы [ 2 ]

3 голосов
/ 06 августа 2011

Ну, это типичное поведение IIS для других технологий, таких как ASP.NET.

Первоначальный запрос не содержит учетных данных пользователя и вызывает ответ 401. Затем второй запрос отправляет ваши учетные данные, а затем IIS может вернуть 200.

Не уверен, относится ли это к DataSnap, но если вы перехватываете сетевые пакеты, вы можете выяснить это.

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

2 голосов
/ 05 августа 2011

Я не могу точно сказать, почему вы видите такое поведение, не видя код вашего сервера и точный JavaScript, который вы используете с XHR.OnUserAuthenticate предназначен для того, чтобы вызываться один раз для сеанса.

Протокол REST XE DataSnap можно посмотреть здесь: http://docwiki.embarcadero.com/RADStudio/en/DataSnap_REST_Messaging_Protocol#Session_Information

Предполагается, что первый вызов возвращает идентификатор сеанса,и все будущие звонки должны указывать это.Если это сделано, все вызовы, кроме первого, используют идентификатор сеанса, указанный для пропуска процесса аутентификации, и переходят только к событию OnUserAuthorize, если оно назначено.

Если вы видите, что OnUserAuthenticate вызывается дважды, это вероятноошибка, если только это не является чем-то странным в конфигурации XHR или IIS.

Если бы вы могли предоставить немного больше деталей, я был бы рад поближе.

...