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

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

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

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

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

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

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

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

6 ответов

Решение

Я наконец нашел способ сделать эту работу. Для аутентификации я использую " Службу аутентификации WCF". При аутентификации сервис попытается установить куки аутентификации. Мне нужно извлечь этот файл 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();
}      

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

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

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

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

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

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

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

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

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

На клиенте измените свой тег для службы (внутри ), включив в него: allowCookies="true"

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

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

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

--larsw

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

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

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

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

Другие вопросы по тегам