Самостоятельная служба WCF REST и обычная проверка подлинности

Я создал автономную службу WCF REST (с некоторыми дополнительными функциями из WCF REST Starter Kit Preview 2). Все работает нормально.

Сейчас я пытаюсь добавить в службу обычную проверку подлинности. Но я сталкиваюсь с некоторыми довольно большими препятствиями в стеке WCF, которые мешают мне сделать это.

Похоже, что HttpListener (который самодостаточные службы WCF используют внутри на низком уровне в стеке WCF) блокирует мои попытки вставить заголовок WWW-Authenticate в самогенерируемый ответ 401 Unauthorized. Почему?

Я могу заставить аутентификацию работать, если забуду об этом WWW-Authenticate заголовке (что, похоже, сделала и Microsoft). Но вот в чем проблема. Если я не отправлю обратно заголовок WWW-Authenticate, тогда веб-браузер не будет отображать стандартный диалог «входа в систему». Пользователь просто столкнется с 401 Unauthorized страницей ошибки без возможности входа в систему.

Службы REST должны быть доступны как для компьютеров, так и для людей (по крайней мере, на уровне запроса GET). Поэтому я считаю, что WCF REST здесь не соответствует фундаментальной части REST. Кто-нибудь со мной согласен?

У кого-нибудь есть обычная проверка подлинности, работающая с автономной службой WCF REST? Если да, то как вы это сделали?

PS: Очевидно, что мои намерения использовать небезопасную базовую аутентификацию исходят из того, что я также получу HTTPS / SSL, работающие для моей службы. Но это другое дело.

PPS: Я пробовал WCF REST Contrib (http://wcfrestcontrib.codeplex.com/) и это имеет точно такую ​​же проблему. Похоже, эта библиотека не тестировалась в автономных сценариях.

Спасибо.


person nbevans    schedule 04.02.2010    source источник


Ответы (3)


К сожалению, я определил (анализируя исходный код ссылки WCF и с помощью инструмента Fiddler для сниффинга HTTP-сеанса), что это ошибка в стеке WCF.

Используя Fiddler, я заметил, что моя служба WCF ведет себя не так, как любой другой веб-сайт, использующий обычную проверку подлинности.

Чтобы было ясно, вот что ДОЛЖНО произойти:

  1. Браузер отправляет GET запрос, не зная, что пароль вообще нужен.
  2. Веб-сервер отклоняет запрос со статусом 401 Unauthorized и включает заголовок WWW-Authenticate, содержащий информацию о допустимых методах аутентификации.
  3. Браузер предлагает пользователю ввести учетные данные.
  4. Браузер повторно отправляет GET запрос и включает соответствующий заголовок Authentication с учетными данными.
  5. Если учетные данные верны, веб-сервер отвечает 200 OK и веб-страницей. Если учетные данные были неправильными, веб-сервер отвечает 401 Unauthorized и включает тот же заголовок WWW-Authenticate, что и на шаге 2.

Что ДЕЙСТВИТЕЛЬНО происходило с моей службой WCF, было следующее:

  1. Браузер отправляет GET запрос, не зная, что пароль вообще нужен.
  2. WCF замечает, что в запросе нет заголовка Authentication, и слепо отклоняет запрос со статусом 401 Unauthorized и включает заголовок WWW-Authenticate. Пока все нормально.
  3. Браузер запрашивает у пользователя учетные данные. Все еще нормально.
  4. Браузер повторно отправляет GET запрос, включая соответствующий заголовок Authentication.
  5. Если учетные данные верны, веб-сервер отвечает 200 OK. Все хорошо. Однако, если учетные данные были неправильными, WCF отвечает 403 Forbidden и не включает никаких дополнительных заголовков, таких как WWW-Authenticate.

Когда браузер получает статус 403 Forbidden, он не воспринимает это как неудачную попытку аутентификации. Этот код состояния предназначен для информирования браузера о том, что URL-адрес, к которому он пытался получить доступ, запрещен. Это никак не связано с аутентификацией. Это имеет ужасный побочный эффект, заключающийся в том, что, когда пользователь неправильно вводит свое имя пользователя / пароль (и сервер отклоняет его с 403), тогда веб-браузер не предлагает пользователю повторно ввести свои учетные данные. Фактически, веб-браузер считает, что аутентификация прошла успешно, и поэтому сохраняет эти учетные данные до конца сеанса!

Имея это в виду, я попросил разъяснений:

RFC 2617 (http://www.faqs.org/rfcs/rfc2617.html#ixzz0eboUfnrl) нигде не упоминается использование кода состояния 403 Forbidden. Фактически, он действительно должен сказать по этому поводу следующее:

Если исходный сервер не желает принимать учетные данные, отправленные с запросом, он ДОЛЖЕН вернуть ответ 401 (неавторизованный). Ответ ДОЛЖЕН включать поле заголовка WWW-Authenticate, содержащее по крайней мере один (возможно, новый) запрос, применимый к запрошенному ресурсу.

WCF не делает ни того, ни другого. Он также не отправляет правильно 401 Unauthorized код состояния. Он также не включает заголовок WWW-Authenticate.

Теперь, чтобы найти дымящийся пистолет в исходном коде WCF:

Я обнаружил, что в классе HttpRequestContext есть метод с именем ProcessAuthentication, который содержит следующее (отрывок):

if (!authenticationSucceeded) 
{
   SendResponseAndClose(HttpStatusCode.Forbidden);
}

Я защищаю Microsoft по многим вещам, но это неоправданно.

К счастью, у меня он работает до "приемлемого" уровня. Это просто означает, что если пользователь случайно вводит свое имя пользователя / пароль неправильно, то единственный способ получить еще одну попытку - полностью закрыть свой веб-браузер и перезапустить его, чтобы повторить попытку. Все потому, что WCF не отвечает на неудачную попытку аутентификации заголовками 401 Unauthorized и WWW-Authenticate в соответствии со спецификацией.

person nbevans    schedule 04.02.2010
comment
@serialseb У меня не хватило духу сказать ему, что я отказался от попыток решить проблемы с авторизацией в WCF и сразу обратился к HttpListener напрямую. - person Darrel Miller; 08.02.2010
comment
Ура, ребята :) Даррел, я видел ваши комментарии в другом обсуждении и, поверьте, я рассматривал это как вариант. К счастью, мы еще не слишком много вложили в WCF REST. Так что мы можем пойти дальше и бросить это. - person nbevans; 08.02.2010

Я нашел решение, основанное на этом ссылка и this.

Первый - переопределить метод Validate в классе с именем CustomUserNameValidator, который наследуется от System.IdentityModel.Selectors.UserNamePasswordValidator:

Imports System.IdentityModel.Selectors
Imports System.ServiceModel
Imports System.Net

Public Class CustomUserNameValidator
    Inherits UserNamePasswordValidator

Public Overrides Sub Validate(userName As String, password As String)
    If Nothing = userName OrElse Nothing = password Then
        Throw New ArgumentNullException()
    End If

    If Not (userName = "user" AndAlso password = "password") Then
        Dim exep As New AddressAccessDeniedException("Incorrect user or password")
        exep.Data("HttpStatusCode") = HttpStatusCode.Unauthorized
        Throw exep
    End If
End Sub
End Class

Хитрость заключалась в изменении атрибута исключения "HttpStatusCode" на HttpStatusCode.Unauthorized.

Во-вторых, создайте serviceBehavior в App.config следующим образом:

<behaviors>
  <endpointBehaviors>
    <behavior name="HttpEnableBehavior">
      <webHttp />
    </behavior>
  </endpointBehaviors>
  <serviceBehaviors>
    <behavior name="SimpleServiceBehavior">
      <serviceMetadata httpGetEnabled="true"/>
      <serviceDebug includeExceptionDetailInFaults="false"/>
      <serviceCredentials>
        <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Service.CustomUserNameValidator, Service"/>
      </serviceCredentials>
    </behavior>
  </serviceBehaviors>
</behaviors>

Finally we must to specify the configuration for the webHttpBinding and apply it to the endpoint:

<bindings>
  <webHttpBinding>
    <binding name="basicAuthBinding">
      <security mode="TransportCredentialOnly">
        <transport clientCredentialType="Basic" realm=""/>
      </security>
    </binding>
  </webHttpBinding>
</bindings>

<service behaviorConfiguration="SimpleServiceBehavior" name="Service.webService">
    <host>
      <baseAddresses>
        <add baseAddress="http://localhost:8000/webService" />
      </baseAddresses>
    </host>
    <endpoint address="http://localhost:8000/webService/restful" binding="webHttpBinding" bindingConfiguration="basicAuthBinding" contract="Service.IwebService" behaviorConfiguration="HttpEnableBehavior"/>
</service>
person Isaac Hernandez    schedule 13.06.2011

Да, вы можете предоставить обычную проверку подлинности для служб WCF на основе REST. Однако есть несколько шагов, которые вы должны выполнить, чтобы получить полное и безопасное решение, и до сих пор большинство ответов являются фрагментами всех необходимых элементов.

  1. Настройте свою автономную службу так, чтобы сертификат SSL был привязан к порту, на котором вы размещаете службу WCF. Это сильно отличается от применения сертификата SSL при использовании управляемого хостинга через что-то вроде IIS. Вы должны применить сертификат SSL с помощью утилиты командной строки. Вы НЕ хотите использовать базовую аутентификацию в службе REST без использования SSL, поскольку учетные данные в заголовке небезопасны. Вот (2) подробных сообщения, которые я написал о том, именно, как это сделать. Ваш вопрос слишком велик, чтобы содержать все подробности в сообщении на форуме, поэтому я предоставляю ссылки с исчерпывающими подробностями и пошаговыми инструкциями:

    Применение и использование сертификата SSL с Самостоятельная служба WCF

    Создание службы WCF RESTful и безопасность Он использует HTTPS через SSL

  2. Настройте свою службу для использования обычной проверки подлинности. Это тоже решение, состоящее из нескольких частей. 1st настраивает вашу службу для использования базовой проверки подлинности. Второй - создать customUserNamePasswordValidatorType и проверить учетные данные для аутентификации клиента. Я вижу, что последний пост ускользнул от этого, однако он не использовал HTTPS и является лишь очень маленькой частью решения; будьте осторожны с инструкциями, которые не предоставляют комплексное решение, включая конфигурацию и безопасность. Последний шаг - посмотреть на контекст безопасности, чтобы при необходимости обеспечить авторизацию на уровне метода. В следующем посте, который я написал, вы шаг за шагом узнаете, как настроить, аутентифицировать, и авторизовать клиентов.

    RESTful Services: аутентификация клиентов с использованием базовой аутентификации

Это комплексное решение, необходимое для использования базовой проверки подлинности с автономными службами WCF.

person atconway    schedule 02.06.2012