WCF, поставщик членства в ASP.NET и служба проверки подлинности

Я написал приложение Silverlight 2, взаимодействующее с сервисом WCF (BasicHttpBinding). Сайт, на котором размещается контент Silverlight, защищен с использованием поставщика членства ASP.NET. Я могу получить доступ к текущему пользователю, используя HttpContext.Current.User.Identity.Name из моей службы WCF, и я включил AspNetCompatibilityRequirementsMode.

Теперь я хочу написать приложение Windows, используя тот же веб-сервис. Чтобы обрабатывать аутентификацию, я включил службу Аутентификация и может вызвать "login" для аутентификации моего пользователя... Okey, все хорошо... Но как, черт возьми, я получаю этот cookie проверки подлинности на моем другом сервисном клиенте?!

Обе службы размещаются в одном домене

  • MyDataService.svc - тот, который касается моих данных
  • AuthenticationService.svc < - тот, который приложение Windows должно выполнить для аутентификации.

Я не хочу создавать новую службу для клиента Windows или использовать другую привязку...

Службы клиентских приложений - еще одна альтернатива, но все примеры ограничены, чтобы показать, как получить пользователя, роли и его профиль... Но как только мы аутентифицируемся с использованием клиентских приложений, должен быть способ получить этот cookie проверки подлинности, прикрепленный к моим клиентам службы при обращении к тому же серверу.

По словам коллег, решение добавляет конечную точку wsHttpBinding, но я надеюсь, что смогу обойти это...

Ответы

Ответ 1

Наконец-то я нашел способ сделать эту работу. Для аутентификации я использую службу проверки подлинности WCF". При аутентификации службы попытается установить cookie аутентификации. Мне нужно получить этот файл cookie из ответа и добавить его к любому другому запросу, сделанному в других веб-службах на том же компьютере. Код для этого выглядит следующим образом:

var authService = new AuthService.AuthenticationServiceClient();
var diveService = new DiveLogService.DiveLogServiceClient();

string cookieHeader = "";
using (OperationContextScope scope = new OperationContextScope(authService.InnerChannel))
{
    HttpRequestMessageProperty requestProperty = new HttpRequestMessageProperty();
    OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestProperty;
    bool isGood = authService.Login("jonas", "jonas", string.Empty, true);
    MessageProperties properties = OperationContext.Current.IncomingMessageProperties;
    HttpResponseMessageProperty responseProperty = (HttpResponseMessageProperty)properties[HttpResponseMessageProperty.Name];
    cookieHeader = responseProperty.Headers[HttpResponseHeader.SetCookie];                
}

using (OperationContextScope scope = new OperationContextScope(diveService.InnerChannel))
{
    HttpRequestMessageProperty httpRequest = new HttpRequestMessageProperty();
    OperationContext.Current.OutgoingMessageProperties.Add(HttpRequestMessageProperty.Name, httpRequest);
    httpRequest.Headers.Add(HttpRequestHeader.Cookie, cookieHeader);
    var res = diveService.GetDives();
}      

Как вы можете видеть, у меня есть два клиента службы, один для службы проверки подлинности и один для службы, которую я фактически использую. Первый блок вызовет метод "Вход" и извлечет cookie аутентификации из ответа. Второй блок добавит заголовок к запросу перед вызовом метода сервиса GetDives.

Я совсем не доволен этим кодом, и я думаю, что лучшей альтернативой может быть использование "Web Reference" вместо "Service Reference" и вместо этого использовать стек .NET 2.0.

Ответ 2

Веб-службы, такие как созданные WCF, часто лучше всего используются "без гражданства", поэтому каждый вызов веб-службы запускается заново. Это упрощает код сервера, так как нет необходимости иметь "сеанс", который напоминает состояние клиента. Он также упрощает клиентский код, поскольку нет необходимости хранить билеты, куки или другие гэги, которые предполагают что-то о состоянии сервера.

Создание двух сервисов описанным способом приводит к проявлению состояния. Клиент либо "аутентифицирован", либо "не аутентифицирован", а MyDataService.svc должен выяснить, какой из них.

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

Подробнее см. статью MSDN здесь.

[Что очень привлекательно для меня, поскольку я ленив, это то, что это полностью декларативный. Я просто разбрасываю правильные записи конфигурации для своего MemberhipProvider в app.config для приложения и! лото! все обращения к каждому контракту в службе аутентифицируются.]

Справедливо отметить, что это не будет особенно быстро. Если вы используете SQL Server для своей базы данных аутентификации, у вас будет хотя бы один, возможно, два вызова хранимых процедур на вызов службы. Во многих случаях (особенно для привязок HTTP) накладные расходы самого вызова службы будут больше; если нет, рассмотрите возможность развертывания собственной реализации поставщика членства, который кэширует запросы проверки подлинности.

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

Ответ 3

На клиенте измените ваше <binding> тег для службы (внутри < system.serviceModel > ), чтобы включить: allowCookies = "true"

Теперь приложение должно сохранять cookie и использовать его. Вы заметите, что IsLoggedIn теперь возвращает true после входа в систему - он возвращает false, если вы не разрешаете файлы cookie.

Ответ 4

Можно скрыть большую часть дополнительного кода за специальным инспектором сообщений и его поведением, поэтому вам не нужно самостоятельно выполнять поиск с помощью OperationContextScope.

Я попытаюсь что-то издеваться над вами и отправить его вам.

- larsw

Ответ 5

Вы должны взглянуть на объект CookieContainer в System.Net. Этот объект позволяет клиенту, не использующему браузер, зависать с файлами cookie. Это то, что моя команда использовала в последний раз, когда мы столкнулись с этой проблемой.

Вот краткая статья о том, как ее использовать. Там могут быть лучшие, но это должно заставить вас начать.

Мы отправили маршрут без гражданства для нашего текущего набора служб WCF и приложения Silverlight 2. Можно заставить Silverlight 2 работать с службами, связанными с безопасностью TransportWithMessageCredential, хотя на стороне Silverlight требуется некоторый код безопасности. Результатом является то, что любое приложение может получить доступ к услугам, просто установив Username и Password в заголовках сообщений. Это можно сделать один раз в пользовательской реализации IRequestChannel, чтобы разработчикам не пришлось беспокоиться о настройке самих значений. Хотя у WCF есть простой способ для разработчиков сделать это, я считаю, это serviceProxy.Security.Username и serviceProxy.Security.Password или что-то в равной степени простое.

Ответ 6

Я написал это некоторое время назад, когда я использовал службы клиентских приложений для аутентификации против веб-служб. Он использует инспектор сообщений для вставки заголовка файла cookie. Существует текстовый файл с документацией и демонстрационный проект. Хотя это не совсем то, что вы делаете, это довольно близко. Вы можете скачать его из здесь.