Ответ 1
Хорошо, я полагаю, что сейчас я возьму удар по моему собственному вопросу, когда я провел несколько часов, играя с различными подходами.
Мой первый подход заключался в том, чтобы настроить аутентификацию на основе сертификатов между сервисом WCF и общедоступным веб-сайтом (веб-сайт является клиентом/клиентом службы). Несколько тестовых сертификатов, сгенерированных с помощью makecert
, вставляют их в теги Personal
, Trusted People
и Trusted Root Certification Authorities
(потому что я не мог беспокоиться о том, чтобы генерировать реальные данные в отношении наших служб сертификатов домена), некоторые модификации файлов конфигурации, и здорово, мы все настроены.
Чтобы веб-сайт не мог поддерживать имя пользователя и пароль для пользователей, идея заключается в том, что после входа пользователя на веб-сайт с помощью проверки подлинности с помощью форм веб-сайт может передавать только имя пользователя (доступное через HttpContext.Current.User.Identity.Name
) как необязательный UserNameSecurityToken
в дополнение к X509CertificateSecurityToken
, который фактически используется для защиты сообщения. Если обнаружен опциональный токен имени пользователя, то служба WCF скажет: "Эй, эта доверенная подсистема говорит, что этот пользователь правильно аутентифицирован, поэтому позвольте мне настроить MyCustomPrincipal
для этого пользователя и установить его в текущем потоке, чтобы фактический код службы может проверить это". Если это не так, то будет установлена анонимная версия MyCustomPrincipal
.
Так что я пошел на пять часов, пытаясь реализовать это, и с помощью различных blogs, я смог это сделать. (Я потратил большую часть своего времени на отладку проблемы, когда у меня была всякая конфигурация и поддерживался класс, и после этого я установил свои пользовательские полномочия после того, как я запустил хост, а не раньше, поэтому ни одно из моих усилий не вступало в силу. компьютеры.) У меня был TrustedSubsystemAuthorizationPolicy
, который проверил проверку сертификата X509, установив анонимный MyCustomPrincipal
, TrustedSubsystemImpersonationAuthorizationPolicy
, который принял токен имени пользователя с пустым паролем и установил клиентскую роль MyCustomPrincipal
, если увидел, что анонимная доверенная подсистема, и UserNameAuthorizationPolicy
, которая провела регулярную проверку имени пользователя и пароля для других конечных точек, где сертификаты X509 не используются. Это сработало, и это было замечательно.
Но.
В тот момент, когда я играл с созданным клиентским прокси-кодом, веб-сайт использовал бы для общения с этой услугой. Указание UserName
в свойстве ClientCredentials
сгенерированного объекта ClientBase<T>
было достаточно простым. Но главная проблема заключается в том, что учетные данные специфичны для ChannelFactory
, а не для конкретного вызова метода.
Вы видите, что новый() подключение к клиенту WCF-клиента более дорогое, чем вы можете подумать. Я написал быстрое и грязное приложение, чтобы проверить производительность самостоятельно: и новый(), запустивший новый прокси, и вызов метода десять раз занял около 6 секунд, тогда как новый() подключился к прокси-серверу один раз и вызвал только метод в 10 раз около 3/5-й секунды. Это всего лишь удручающая разница в производительности.
Итак, я могу просто реализовать пул или кеш для клиентского прокси, не так ли? Ну, нет, он нелегко работал: информация учетных данных клиента находится на уровне канала factory, потому что он может использоваться для защиты транспорта, а не только от сообщения, а некоторые привязки сохраняют фактический транспорт между вызовами службы. Поскольку учетные данные клиента уникальны для прокси-объекта, это означает, что мне нужно будет иметь уникальный кешированный экземпляр для каждого пользователя, находящегося на веб-сайте. Это потенциально много прокси-объектов, сидящих в памяти, и довольно @# [email protected]# близко к проблеме, которую я пытался избежать в первую очередь! И так как мне нужно прикоснуться к свойству Endpoint
, чтобы настроить привязку для необязательного поддерживающего токена имени пользователя, я не могу воспользоваться автоматическим каналом factory кэширование, что Microsoft добавила "бесплатно" в .NET 3.5.
Вернемся к чертежной доске: мой второй подход и тот, который, как мне кажется, я сейчас использую, должен придерживаться безопасности сертификата X509 между клиентским веб-сайтом и службой WCF. Я просто отправлю пользовательский заголовок SOA-заголовка "UserName" в своих сообщениях, и служба WCF может проверить этот заголовок SOAP, определить, исходит ли она из доверенной подсистемы, такой как веб-сайт, и если да, установите MyCustomPrincipal
в аналогично предыдущему.
Codeproject и случайные люди в Googleэто чудесные вещи, потому что они помогли мне быстро и быстро запустить это, даже после того, как запутался в странной ошибке WCF, когда дело доходит до пользовательских настроек конечных точек в конфигурации. Внедряя инспекторов сообщений на стороне клиента и стороне службы - один для добавления заголовка UserName, а другой для его чтения и установки правильного принципала - этот код находится в одном месте, где я могу просто забыть об этом. Поскольку мне не нужно прикасаться к свойству Endpoint
, я получаю встроенное кэширование factory в качестве бесплатного. А поскольку ClientCredentials
одинаковы для любого пользователя, обращающегося к веб-сайту (действительно, он всегда является сертификатом X509 - изменяется только значение заголовка UserName внутри самого сообщения), добавление кэширования прокси-сервера клиента или пула прокси-сервера гораздо более тривиальным.
Итак, что я закончил делать. Фактический код службы в службе WCF может делать такие вещи, как
// Scenario 1: X509Cert + custom UserName header yields for a Web site customer ...
Console.WriteLine("{0}", Thread.CurrentPrincipal.Identity.Name); // prints out, say, "[email protected]"
Console.WriteLine("{0}", Thread.CurrentPrincipal.IsInRole(MyRoles.Customer)); // prints out "True"
// Scenario 2: My custom UserNameSecurityToken authentication yields for an employee ...
Console.WriteLine("{0}", Thread.CurrentPrincipal.Identity.Name); // prints out, say, CN=Nick,DC=example, DC=com
Console.WriteLine("{0}", Thread.CurrentPrincipal.IsInRole(MyRoles.Employee)); // prints out "True"
// Scenario 3: Web site doesn't pass in a UserName header ...
Console.WriteLine("{0}", Thread.CurrentPrincipal.Identity.Name); // prints out nothing
Console.WriteLine("{0}", Thread.CurrentPrincipal.IsInRole(MyRoles.Guest)); // prints out "True"
Console.WriteLine("{0}", Thread.CurrentPrincipal.IsInRole(MyRoles.Customer)); // prints out "False"
Не имеет значения, как эти люди прошли аутентификацию или что некоторые из них живут на SQL-сервере или что некоторые из них живут в Active Directory: PrincipalPermission.Demand
и регистрация для целей аудита теперь оснащена.
Я надеюсь, что это поможет некоторым бедным душам в будущем.