Аутентификация rich-клиента на нескольких WCF-сервисах
От: WSA  
Дата: 16.01.14 10:11
Оценка:
Здравствуйте!

1. Загружается основный модуль программы, который содержит логику аутентификации на STS.
2. После успешной аутентификации подключаются другие модули.
3. В каждом модуле находится proxy-client для доступа к WCF — сервисам.





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


С уважением,
Сергей
Re: Аутентификация rich-клиента на нескольких WCF-сервисах
От: Аноним  
Дата: 19.01.14 05:41
Оценка:
Здравствуйте, WSA, Вы писали:

Угу, схема нормальная. А вопрос какой?
Re[2]: Аутентификация rich-клиента на нескольких WCF-сервисах
От: WSA  
Дата: 22.01.14 11:00
Оценка:
А>Угу, схема нормальная. А вопрос какой?

Да проблнма в том, что далее схемы дело не идёт )))
Ни как не удаётся заставить работать схему.


У меня STS успешно выдаёт токен, я его получаю клиентом, расшифровываю и назначаю Thread.CurrentPrincipal на клиенте....
А вот дальше... прям стена... ни как не удаётся настроить WCF сервисы, чтобы принимали этот токен.
Re[3]: Аутентификация rich-клиента на нескольких WCF-сервисах
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 22.01.14 13:35
Оценка: 72 (4)
Здравствуйте, WSA, Вы писали:

WSA>У меня STS успешно выдаёт токен, я его получаю клиентом, расшифровываю и назначаю Thread.CurrentPrincipal на клиенте....

WSA>А вот дальше... прям стена... ни как не удаётся настроить WCF сервисы, чтобы принимали этот токен.

И не будет, на сколько я понимаю, в меру своих знаний — базовый WCF не позволяет такие манипуляции.

Вы можете сделать один из следующих варианов:

1. Использовать только "чистый" WCF.
Тогда вам понадобится ws2007FederationHttpBinding. Там примерно следующая схема:

2. Использовать Windows Identity Foundation.
Именно она позволит сделать сценарий именно как у вас. Только обратите внимание, что начиная с .Net 4.5 классы WIF вроде как с некоторыми небольшими изменениями перекочевали в основной фремворк. Хотя "старую" WIF (которая поставляется отдельной библиотекой) по-прежнему можно использовать.


Для более предметной беседы, ответьте, плиз:
Re[4]: Аутентификация rich-клиента на нескольких WCF-сервисах
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 22.01.14 13:40
Оценка:
МР>Здравствуйте, WSA, Вы писали:

Немного поправлюсь... Почему-то подумал, что у вас сайт и обращение к STS идет через HTTP Redirect.

Собственно, раз у вас rich клиент, то с большой вероятностью обращение к STS идет поверх SOAP. Осталось понять по какому именно протоколу: WS-Federation или SAML (это не только язык разметки токенов, но и протокол передачи из, чтобы не путать его еще часто называют SAML-P) у вас идет обращение, а также возвращает ли токен закрытые ключи для шифрования канала (без этого ws2007FederationHttpBinding не будет работать).
Re[5]: Аутентификация rich-клиента на нескольких WCF-сервисах
От: WSA  
Дата: 23.01.14 07:41
Оценка:
Михаил, здравствуйте. Спасибо за интерес к теме и информативные комментарии.

>> Какой .Net используете

.Net 4.5. И это вносит дополнительную головную боль. Как вы заметили, в 4.5 WIF перекочевал во вреймворк, да и к томуже в другой неймспейс. в 4.5 это System.IdentityModel

>> Что используете в качестве STS (AD, свое решение — тогда на чем, сторонний продукт)

Как STS использую Thinktecture Identity Server v2

>> Используете ли WIF

Да, использую, но пока что не очень осознанно... Ничего не работает, я не знаю поэтому что на что влияет.

>> WS-Federation или SAML

В качестве формата утверждений SAML 2.0. Как протокол обмена как с WIF.... пока ничего не работает пробую все варианты из инета... в котором блин все примеры не для 4.5


С уважением,
Сергей
Re[6]: Аутентификация rich-клиента на нескольких WCF-сервисах
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 23.01.14 11:29
Оценка: 3 (1)
Добрый день, Сергей

Давайте так — я попробую вечерком посмотреть на:

Возможно, смогу подсказать что-то более конкретное (если это будет еще актуально на тот момент).

Сразу оговорюсь — ваша схема вполне работоспособна, т.к. мы сами работаем по схожей. Разница только в том, что: а) токенов мы получаем несколько — в них разная информация для разных случаев, у нас Web, а не Desktop клиент (поэтому часть токенов мы получаем по HttpRedirect, а что-то по SOAP).
Но нюансов мы насобирали там просто невозможное количество, поэтому давать советы типа "посмотрите тут, это должно работать", я воздержусь, прежде чем не перепроверю.

Идет?
Re[7]: Аутентификация rich-клиента на нескольких WCF-сервисах
От: WSA  
Дата: 23.01.14 11:43
Оценка:
Здравствуйте, Михаил Романов, Вы писали:

МР>Идет?


Будет очень здорово!
Re[8]: Аутентификация rich-клиента на нескольких WCF-сервисах
От: WSA  
Дата: 23.01.14 11:52
Оценка:
Кстати, Михаил, может проще если я вам дам адрес этого STS и админские права, смодете поковыряться в уже работающем тестовом STS thinktecture? Мой email: zergy@list.ru
Re: Аутентификация rich-клиента на нескольких WCF-сервисах
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 23.01.14 12:44
Оценка:
Здравствуйте, WSA, Вы писали:

WSA>Необходимо реализовать механизм, чтобы после единого ввода логина и пароля и получения Токена безопасности от STS, можно было аутентифицироваться на прочих WCF-сервисах, на которые идёт обращение из модулей без передачи

WSA>туда логина-пароля, а при помощи ранее полученного токена.

http://msdn.microsoft.com/en-us/library/aa355045(v=vs.110).aspx уже изучал?
... << RSDN@Home 1.2.0 alpha 5 rev. 100 on Windows 8 6.2.9200.0>>
AVK Blog
Re[2]: Аутентификация rich-клиента на нескольких WCF-сервисах
От: WSA  
Дата: 23.01.14 12:51
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>http://msdn.microsoft.com/en-us/library/aa355045(v=vs.110).aspx уже изучал?


Да, изучал очень много статей про федерацию, но и там куча нюансов... результат пока не достиг.
Re: Аутентификация rich-клиента на нескольких WCF-сервисах
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 24.01.14 09:28
Оценка:
Сергей, добрый день.

Я смог установить и настроить Thinktecture Server (кстати, если я не путаю, один из его авторов достаточно активный участник форумов MSDN по WIF и Identity, я даже пользовался как-то его советами).
А также мне удалось разобраться с большей частью нововведений (точнее изменений) в WIF 4.5 и построить, хоть и с некоторыми "костылями", работающий пример по первому описанному мною сценарию (когда вы сами руками токен не получаете, а всем занимается только инфраструктура WCF+WIF — сейчас они почти неотделимы).

К сожалению, у меня катастрофически отсутсвует время, чтобы написать подробный ответ и пример (а за одно разобраться, почему же потребовались костыли — по идее все должно работать и без них...)

Сергей, на сколько вас жмет время? Можете ли вы подождать до понедельника с этой задачей?
Если да, то я постараюсь за выходные все еще раз перепроверить и сделать детальное описание того, что я узнал.
Re[2]: Аутентификация rich-клиента на нескольких WCF-сервисах
От: WSA  
Дата: 24.01.14 09:55
Оценка:
Здравствуйте Михаил.
МР>Сергей, на сколько вас жмет время? Можете ли вы подождать до понедельника с этой задачей?

Мне даже не удобно отвечать )) Ну естественно подожду. Я всё ещё эксперементирую, но пример от вас, воплотившего подобный сценарий в жизнь, это очень круто. Буду ждать с нетерпением. Я подготовлю к понедельнику солюшен с клиентом, который получает и дешефрует токен, может быть вам будет интересно взглянуть. Куда бы отправить? Если вы напишите мне на zergy@list.ru я отправлю мылом.
Re: Вариант №1 "Чистый" WCF
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 25.01.14 14:27
Оценка: 45 (3)
Сергей, добрый день.

Что ж, я в принципе разобрался со всеми сложностями и напастями, что мне встречались и теперь могу рассказать, что же у меня получилось.
На текущий момент, у меня готов первый вариант, из тех, что я описывал, т.е. практически всю работу на себя берет WCF (правда в .Net 4.5 работа так или иначе идет с использованием WIF, но от вас лично не потребует ни строчки кода для WIF — разве что вы начнете делать какие-то кастомные расширения).

Для начала, чтобы у нас было единое понимание того, что происходит, позволю себе небольшое описание процесса (см. картинку):

0. Пользователь вводит свои UserName и Password (U/P)
1. Клиент пытается подключиться к WCF Service используя переданные ему U/P. Однако, инфраструктура WCF, анализирует указанные ей настройки и выясняет, что от нее требуется работать по Federation протоколу, а это значит, что вместо прямого обращения WCF Service, она формирует специальный Security Token Request (STR), который отсылает на STS. При этом в качестве credentials в SRT используются наши U/P.
2. STS получает STR и первым делом анализирует credentials (надо сказать, что в общем случае это не обязательно должны быть именно UserName/Password, а любой допустимый способ аутентификации Windows, Certificate, … — вплоть до полученного ранее сертификата! Но мы рассматриваем простейший случай).
Получив и аутентифицировав STR, наш STS начинает его анализировать и обрабатывать.
Делает он это следующим образом:
3. Клиент, получивший STRes проверяет подпись STS, достает свои данные и зашифрованный пакет для RP и уже с этим пакетом делает запрос на установление соединения с WCF Service
4. WCF Service получает запрос, достает оттуда зашифрованный пакет, расшифровывает его своим закрытым ключом и проверяет подпись STS. Если все в порядке, то устанавливается сессия с клиентом, а данные из токена используются как данные аутентифицированного пользователя.

Примерно так выглядит общий процесс (я тоже владею далеко не всем предметом в совершенстве, так что местами мог слегка и наврать).
Если мы его внимательно просмотрим, то получится, что для успешного решения нам желательно выполнить следующее:

Теперь перейдем к настройке STS.
Тут нужно не забыть сделать следующее:

Все, на этом настройки STS закончены.

Однако, прежде чем перейти к настройкам WSF Service, сделаем следующее: выйдем на страницу Home (она в корне нашего STS или можно через закладки), а оттуда на Application integration. На этой странице есть 2 важных для нас адреса: "WS-Trust metadata" и "WS-Trust mixed mode security (user name)" — сами мы будем использовать только 1-ый, а второй чисто для проверки, что все метаданные верно подхватились.

Переходим к нашему WCF Service!
Здесь все делается в настройке, поэтому я просто приведу config с комментариями:
<?xml version="1.0"?>
<configuration>
  <configSections>
    <!-- Обязательно указать эту секцию!!! Почему ее нет по умолчанию в machine.confug - ума не приложу -->
    <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
  </configSections>

  <system.serviceModel>
    <bindings>
      <ws2007FederationHttpBinding>
        <binding name="Default">
          <!--    Мы используем TransportWithMessageCredential безопасность, чтобы не заморачиваться с настройками,
                так как SSL мы уже настроили.
                В качестве issuerMetadata нужно указать адрес, который содержится на странице Application integration
                и называется "WS-Trust metadata"
          -->
          <security mode="TransportWithMessageCredential">
            <message>
              <issuerMetadata address="https://мой_STS/IdSrv/issue/wstrust/mex" />
            </message>
          </security>
        </binding>
      </ws2007FederationHttpBinding>
    </bindings>
    
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- Это чтобы не возиться с настройкой клиента... -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="false"/>
          <!-- Здесь мы указываем, что в pipeline WCF будет работать WIF, конфигурация которой (DefaultIConf) описывается ниже-->
          <serviceCredentials identityConfiguration="DefaultIConf" useIdentityConfiguration="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    
    <services>
      <!-- Ну и сам сервис. Т.к. хостим под IIS, адрес не указываем, binding и contract - обязательно -->
      <service name="WcfService.Service">
        <endpoint binding="ws2007FederationHttpBinding" bindingConfiguration="Default" contract="WcfService.Service" />
      </service>
    </services>
  </system.serviceModel>

  <!-- Здесь указываются все настройки WIF, т.е., собственно, все настройки обработки токенов -->
  <system.identityModel>

    <!-- Это требование MS в WIF - создавать и использовать отдельные конфигурации -->
    <identityConfiguration name="DefaultIConf">
      <securityTokenHandlers>

        <securityTokenHandlerConfiguration>
          <!-- Здесь мы указываем, что не будем проверять какому именно серверу предназначен пришедший токен
                если все же хочется проверять, то нужно будет добавить к узлу audienceUris дочерний.
                Что-то типа <add value="https://мой_хост/WcfService/Service.svc"/> - главное следить, чтобы он совпадал 
                с тем, что указано в поле Realm/Scope Name на STS
          -->
          <audienceUris mode="Never" />

          <!--    Здесь указывается класс, который будет для пришедшего токена искать закрытый ключ для расшифровки.
                К сожалению, я не нашел как для этого параметра указать настройку (т.е. в каком хранилище искать сертификаты)
                это можно сделать, если задавть настройку программно (например, делать свой наследник текущего класса и там 
                в конструкторе заполнять нужные параметры, а здесь указывать наш класс).
                Однако по умолчанию этот класс ищет сертификаты в Local Machine / My так что просто положим его туда
          -->
          <serviceTokenResolver
              type="System.IdentityModel.Tokens.X509CertificateStoreTokenResolver, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>

          <!--    Этот тэг задает список доверенных STS, т.е. тех от которых мы готовы получать токены.
                Опознаем мы их довольно просто - по thumbprint сертификата, которым они ставят подпись
                Информация о сертификате подписи добавляется в саму подпись, поэтому ничего регистрировать 
                дополнительно не надо, надо только указать строку thumbprint (имя произвольное)
          -->
          <issuerNameRegistry>
            <trustedIssuers>
              <add name="STS" thumbprint="9B14A55B4A435YYY8C88D14779F57193905C2"/>
            </trustedIssuers>
          </issuerNameRegistry>

        </securityTokenHandlerConfiguration>

      </securityTokenHandlers>
    </identityConfiguration>
  </system.identityModel>

</configuration>


Теперь переходим к клиенту.
Здесь тоже ничего специально делать не надо, просто создать ServiceReference для нашего WCF Service.

После создания перепроверьте, что все настройки соединений подхватились правильно.
Я привожу свой с комментариями, на что обратить внимание.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <system.serviceModel>
    <bindings>
      <!-- !!! Должно быть 2 биндинга: ws2007FederationHttpBinding и ws2007HttpBinding  !!! -->
      <ws2007FederationHttpBinding>
        <binding name="WS2007FederationHttpBinding_Service">
          <security mode="TransportWithMessageCredential">
            <message>
              <!-- !!! Обратите внимание на адрес. Он должен совпадать с "WS-Trust mixed mode security (user name)" 
                  на странице Application integration  !!! 
              -->
              <issuer address="https://мой_STS/IdSrv/issue/wstrust/mixed/username"
                  binding="ws2007HttpBinding" bindingConfiguration="https://мой_STS/IdSrv/issue/wstrust/mixed/username" />
              <issuerMetadata address="https://мой_STS/IdSrv/issue/wstrust/mex" />
              <tokenRequestParameters>
                <trust:SecondaryParameters xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
                  <!-- Тут я выпущу кусок -->
                </trust:SecondaryParameters>
              </tokenRequestParameters>
            </message>
          </security>
        </binding>
      </ws2007FederationHttpBinding>

      <ws2007HttpBinding>
        <!--  !!! Модель безопасности должна быть TransportWithMessageCredential
              а  clientCredentialType должен стоять в UserName  !!! -->
        <binding name="https://мой_STS/IdSrv/issue/wstrust/mixed/username">
          <security mode="TransportWithMessageCredential">
            <transport clientCredentialType="None" />
            <message clientCredentialType="UserName" establishSecurityContext="false" />
          </security>
        </binding>
      </ws2007HttpBinding>
    </bindings>

    <client>
      <!-- Ну а тут просто адрес наших WCF Service и указание биндинга с контрактом -->
      <endpoint address="https://мой_WCF_Service/WcfService/Service.svc"
          binding="ws2007FederationHttpBinding" bindingConfiguration="WS2007FederationHttpBinding_Service"
          contract="ServiceReference.Service" name="WS2007FederationHttpBinding_Service" />
    </client>
  </system.serviceModel>
</configuration>


Ну все, настройка закончена

Теперь пишем пробный код клиента
(создаем proxy и указываем User/Password в качестве credentials)
using (var client = new ServiceReference.ServiceClient())
{
    client.ClientCredentials.UserName.UserName = "User1";
    client.ClientCredentials.UserName.Password = "123456";

    Console.WriteLine(client.GetData());
}


Ну и осталось привести код сервиса
Там ничего сложного, просто показываю как добраться до переданных Claims. В отличие от WIF 3.5, WIF 4.5 не меняет Principal текущего потока (хотя может где-то и есть такая опция), а предлагает как и в целом в WCF все получать через OperationContext

namespace WcfService
{
    [ServiceContract]
    public class Service
    {
        [OperationContract]
        public string GetData()
        {
            var princinpal = OperationContext.Current.ClaimsPrincipal;
            var name = princinpal.Identity.Name;
            var email = princinpal.FindFirst(ClaimTypes.Email);
            var webPage = princinpal.FindFirst(ClaimTypes.Webpage);

            return string.Format("User: {0}, mail: {1}, web page: {2}", name, 
                email == null ? "" : email.Value,
                webPage == null ? "" : webPage.Value);
        }
    }
}



Надеюсь, все получилось достаточно доходчиво.
Если останутся вопросы — спрашивайте без смущения (если я не буду долго отвечать — спросите в личку, я не постоянно читаю RSDN, а так должно прийти уведомление).

По поводу второго варианта (отдельное получение токена и затем вручную добавление его в вызовы сервисов в качестве Credentials) — он тоже должен быть возможен (мы именно так и работаем), но с ходу накидать пример на WCF/WIF 4.5 я не смогу, а больше пока времени на исследования, увы, не осталось.
Re[2]: Вариант №1 "Чистый" WCF
От: WSA  
Дата: 25.01.14 15:00
Оценка:
Приветствую Михаил.

С ума сойти! Сейчас бум курить...
Re[3]: Вариант №1 "Чистый" WCF
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 27.01.14 07:52
Оценка:
Здравствуйте, WSA, Вы писали:

WSA>Сейчас бум курить...


Потом отпишите о результатах, по возможности.
Я постараюсь в течение недели выделить время и сделать еще один пример — с явным получением токена и дальнешей работой через него. Но обещать не буду — времени не хватает ни на что, как обычно.
Re[4]: Вариант №1 "Чистый" WCF
От: WSA  
Дата: 27.01.14 08:30
Оценка:
Михаил, отправил вам личное сообщение, но не понял тут как его судьбу просдедить. Вы получили что-то от меня?
Re[2]: Вариант №1 "Чистый" WCF
От: WSA  
Дата: 27.01.14 11:39
Оценка:
Приветствую Михаил,

Проект с сервисом создаю по шаблону VS2012: WCF Service Application.
После редактирования конфига с данными, приведёнными в статье для WCF сервиса и попытки обратиться к сервису через браузер, возникает ошибка
"Could not find a base address that matches scheme https for the endpoint with binding WS2007FederationHttpBinding. Registered base address schemes are [http]."

Что я делаю не так?
Re[3]: Вариант №1 "Чистый" WCF
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 27.01.14 12:22
Оценка:
Здравствуйте, WSA, Вы писали:

WSA>Что я делаю не так?

Обычно такая ошибка возникает, когда ост сервиса не может найти https среди доступных ему биндингов сайта (не биндингов WCF, а биндингов IIS-сайта!!!).
На сколько я помню, по умолчанию проект WCF Application хостится под IIS Express, у которого по умолчанию же SSL не настроен.

К сожалению, при всей удобстве работы с IIS Express, в данном конкретном случае я рекомендую переключиться на полноценный IIS — как минимум там проще и нагляднее настраивать SSL.
Re[4]: Вариант №1 "Чистый" WCF
От: WSA  
Дата: 27.01.14 12:26
Оценка:
МР>К сожалению, при всей удобстве работы с IIS Express, в данном конкретном случае я рекомендую переключиться на полноценный IIS — как минимум там проще и нагляднее настраивать SSL.

К сожалению в моём случае не получится перейти на IIS, т.к. используются self-hosted WCF — сервисы, которые крутятся в windows — службе.
В данном контекстк, видимо нужно будет укказать более явно адрес конечной точки... А сертификат руками привязать к порту?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.