Самостоятельная служба WCF REST и обычная аутентификация - PullRequest
11 голосов
/ 04 февраля 2010

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

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

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

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

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

Кто-нибудь получил базовую аутентификацию, работающую с REST-службой WCF? Если да, то как ты это сделал?

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

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

Спасибо.

Ответы [ 3 ]

13 голосов
/ 05 февраля 2010

К сожалению, я определил (анализируя эталонный исходный код 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 согласно спецификации.

6 голосов
/ 14 июня 2011

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

Первое - переопределить метод 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>

Наконец, мы должны указать конфигурацию для webHttpBinding и применить ее кконечная точка:

<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>
1 голос
/ 02 июня 2012

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

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

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

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

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

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

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

...