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