Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Распределённые приложения и сеть > Проблема с авторизацией клиента в WCF-сервисе


Автор: cha0ss 15.9.2009, 16:27
Здравствуйте, возникла необходимость подключения к WCF-сервису не через Windows-аутентификацию. 
Для этого я динамически создаю binding и endpointadress, но не нашел где все таки прописывать имя пользователя и пароль под которым я хочу запустить метод.

Код

                            WSHttpBinding bind = new WSHttpBinding(SecurityMode.Message);
                            EndpointAddress address = new EndpointAddress("http://komp:2222/BDService");
                            BDFunc.bdc = new BDClient(bind, address);
                            BDFunc.bdc.Open();



Автор: mihryak 15.9.2009, 17:06
http://msdn.microsoft.com/ru-ru/library/ms553830.aspx
только вот как-то мало ты конфигуришь binding
так как минимум понадобится строка
Код

binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;

Автор: cha0ss 16.9.2009, 11:14
Код

                            //Создаем binding
                            WSHttpBinding bind = new WSHttpBinding(SecurityMode.Message);
                            bind.Security.Message.ClientCredentialType = MessageCredentialType.UserName;

                            //Создаем endpoint
                            EndpointIdentity identity = EndpointIdentity.CreateDnsIdentity("localhost");
                            EndpointAddress address = new EndpointAddress(new Uri("http://komp:2222/BDService"), identity, (System.ServiceModel.Channels.AddressHeaderCollection)null);

                            BDFunc.bdc = new BDClient(bind, address);
                            bdc.ClientCredentials.UserName.UserName = "login";
                            bdc.ClientCredentials.UserName.Password = "pass";
                            BDFunc.bdc.Open();


при открытии соединения получаю следующую ошибку:
{"Не удалось открыть безопасный канал, так как произошла ошибка при согласовании безопасности с удаленной стороной. Возможно, это связано с отсутствием или неправильным указанием EndpointIdentity в EndpointAddress, использованном для создания канала. Убедитесь, что EndpointIdentity, указанный или подразумеваемый в EndpointAddress, правильно идентифицирует удаленную сторону. "}

пытался менять CreateDnsIdentity на CreateUpnIdentity, не помогает - в ответ все таже ошибка.

Автор: mihryak 21.9.2009, 15:52
я не опоздал? smile
впрочем, кому-нибудь ещё может пригодиться пример self-hosted wcf service'а с юзернеймовой аутентификацией, с кастомным юзерским валидатором, без сертификатов и ssl (транспортная секьюрность и секьюрность сообщений отключена), сконфиругированный кодом, а не app.config'ом (вроде бы, не забыл ничего smile)
итак, сам сервис (обычно в отдельной общей сборке)
Код

    [ServiceContract]
    public interface IService
    {
        [OperationContract]
        string GetData(int value);
    }

его реализация (в самом host application)
Код

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class Service : IService
    {
        public string GetData(int value)
        {
            return value.ToString();
        }
    }

валидатор пользователей (там же)
Код

    class UserNameValidator : UserNamePasswordValidator
    {
        public override void Validate(string userName, string password)
        {
            if (userName != "user" ||
                password != "password")
            {
                throw new SecurityTokenException("Authentication failed");
            }
        }
    }

prorgam.cs host application'а
Код

        static void Main()
        {
            Console.WriteLine("Starting...");

            Service service = new Service();
            Uri address = new Uri("http://localhost:5556/WcfClient/Service/");
            using (ServiceHost host = new ServiceHost(service, address))
            {
                WSHttpBinding binding = new WSHttpBinding(SecurityMode.None);
                binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
                binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
                host.AddServiceEndpoint(typeof (IService), binding, address);

                host.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
                host.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new UserNameValidator();

                host.Open();

                Console.WriteLine("Started");
                Console.WriteLine("Press any key to quit...");
                Console.ReadKey();
            }
        }

program.cs client apllication'а
Код

        static void Main(string[] args)
        {
            Console.WriteLine("Press any key to call service...");
            Console.ReadKey();

            WSHttpBinding binding = new WSHttpBinding(SecurityMode.None);
            binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
            binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
            EndpointAddress address = new EndpointAddress("http://localhost:5556/WcfClient/Service/");
            using (var factory = new ChannelFactory<IService>(binding, address))
            {
                factory.Credentials.UserName.UserName = "username";
                factory.Credentials.UserName.Password = "password";
                IService service = factory.CreateChannel();
                Console.WriteLine("Service response: {0}", service.GetData(25));
            }

            Console.WriteLine("Press any key to quit...");
            Console.ReadKey();
        }

Автор: cha0ss 22.9.2009, 09:03
Спасибо, это обязательно пригодится!
Я сделал по другому, в моей программе оказалось достаточным Windows-аутентификации под текущим пользователем + возможность ввести другие логин / пароль (но также под Windows-аутентификацией).

Автор: v_enom 24.9.2009, 00:33
mihryak, 
покажи пожалуйста, как проверяешь на сервере, правильность пароля и имени (например из базы с пользователями на сервере берешь)....
я понимаю что функцией  void Validate(string userName, string password)
но где она вызывается? или она как-то по-особому работает, что сама все делает??? в общем где она проявляется на серваке, опиши, а?
и кто возвращает ошибку, в случае неверной валидации????

и немного не понятна эта запись 
 
Код

host.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new UserNameValidator();

где она вызывается? (я создаю wcf сервер через окна IDE ).
у меня есть файлы WcfUploaderIService.cs(с контрактами) и WcfUploaderService.cs (с кодом)
где писать все это добро
Код


 host.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
                host.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new UserNameValidator();




и еще... а как на практике реализуется открытие части функций с помощью 
Код

[PrincipalPermission(SecurityAction.Demand, Name @"MAxhineName\гость")]


т.е. что надо выставить/прописать в client application (а может еще и в конфиге и сервере), чтобы все, кто не авторизовались по валидации все же могла что-то там делать....например только "смотреть таблицу из бд"
и т.д.

Автор: mihryak 24.9.2009, 16:05
Цитата(v_enom @  24.9.2009,  01:33 Найти цитируемый пост)
покажи пожалуйста, как проверяешь на сервере, правильность пароля и имени (например из базы с пользователями на сервере берешь)....
я понимаю что функцией  void Validate(string userName, string password)
но где она вызывается? или она как-то по-особому работает, что сама все делает??? в общем где она проявляется на серваке, опиши, а?
и кто возвращает ошибку, в случае неверной валидации????

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

host.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;

и задать сам экземпляр валидатора
Код

host.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new UserNameValidator();

Сам метод класса вызывается автоматически инфраструктурой WCF, всё что требуется - просто его указать

Вообще, кастомные валидаторы не рекомендуются, см. http://www.owasp.org/index.php/WCF_Security_Best_Practices, лучше прикрутить MembershipProvider

Цитата(v_enom @  24.9.2009,  01:33 Найти цитируемый пост)
где писать все это добро

Зависит от того, так ты стартуешь сервис, кто его хостит. Если как у меня (self-hosted wcf service) - то либо в prorgam.cs прописать мой код, либо в app.config, а если хостит IIS, то, насколько знаю, только конфигом

Цитата(v_enom @  24.9.2009,  01:33 Найти цитируемый пост)
и еще... а как на практике реализуется открытие части функций с помощью 
[PrincipalPermission(SecurityAction.Demand, Name @"MAxhineName\гость")]

Это уже относится не к аутентификации (принятие или непринятие пользователя), а к авторизации (раздача уже принятому пользователю его прав). См. всё ту же ссылку с best practices.
Для кастомной проверки ролей (не рекомендуется)
1. пометить метод атрибутом
Код

        [PrincipalPermission(SecurityAction.Demand, Role = "guest")]
        public string GetData(int value)
        {
            return value.ToString();
        }

2. сделать ещё один класс-валидатор
Код

    public class AuthorizationPolicy : IAuthorizationPolicy
    {
        private readonly string _id = Guid.NewGuid().ToString();
        public string Id
        {
            get { return _id; }
        }
        public bool Evaluate(EvaluationContext evaluationContext, ref object state)
        {
            if (evaluationContext.Properties.ContainsKey("Identities"))
            {
                IList<IIdentity> identities =
                    evaluationContext.Properties["Identities"] as IList<IIdentity>;
                if (identities != null && identities.Count > 0)
                {
                    evaluationContext.Properties["Principal"] = new CustomPrincipal(identities[0]);
                    return true;
                }
            }
            return false;
        }
        public ClaimSet Issuer
        {
            get { return ClaimSet.System; }
        }
        private class CustomPrincipal : IPrincipal
        {
            private readonly IIdentity _identity;
            public CustomPrincipal(IIdentity identity)
            {
                _identity = identity;
            }
            public IIdentity Identity
            {
                get { return _identity; }
            }
            public bool IsInRole(string role)
            {
                return CheckIdentityRole(Identity, role);
            }
        }
    }

3. сконфигурить хост, указав, что надо использовать его
Код

                host.Authorization.PrincipalPermissionMode = PrincipalPermissionMode.Custom;
                host.Authorization.ExternalAuthorizationPolicies =
                    new ReadOnlyCollection<IAuthorizationPolicy>(
                        new List<IAuthorizationPolicy> {new AuthorizationPolicy()});


Каждый частный случай реализации аутентификации-авторизации требует отдельного рассмотрения, то, что у меня - лишь демо-пример, причём не лучший

Автор: v_enom 24.9.2009, 20:54
mihryak, 
спасибо, поковыряв твои примеры и книги можно уже разобраться, как сделать валидацию...

Автор: NikeDeForest 15.10.2009, 07:38
А почему проверка ролей не рекомендуется? И что рекомендуется тогда, заявки (claims)?

Автор: anikss 10.11.2009, 22:49
Цитата(mihryak @  21.9.2009,  15:52 Найти цитируемый пост)
я не опоздал? 
впрочем, кому-нибудь ещё может пригодиться пример self-hosted wcf service'а с юзернеймовой аутентификацией, с кастомным юзерским валидатором, без сертификатов и ssl (транспортная секьюрность и секьюрность сообщений отключена), сконфиругированный кодом, а не app.config'ом (вроде бы, не забыл ничего )


Здравствуйте! Проверил ваш пример, проверка пароля не производитс, и при указании неправильного пароля операция все равно выполняется.
Подскажите пожалуйста, как же все-таки сделать аутентификацию по логину и паролю без сертификата?

Автор: EagleOne 27.11.2009, 21:51
Цитата

Здравствуйте! Проверил ваш пример, проверка пароля не производитс, и при указании неправильного пароля операция все равно выполняется.
Подскажите пожалуйста, как же все-таки сделать аутентификацию по логину и паролю без сертификата?


1. Ага, не производится, потому что в приведенном примере контекст безопасности не будет содержать идентификационной информации....

Цитата

как же все-таки сделать аутентификацию по логину и паролю без сертификата


Можно в коде приведенном выше заменить

Код

binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;


на

Код

binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;


Вот тогда идентификационная информацию уйдет в http. В этом случае при неправильном логине или пароле будет вываливаться, что 
HTTP запросы запрещены для данной схемы проверки подлинности... 


Цитата

операция все равно выполняется


2. Так чё бы ей не выполняться то... Аутентификация это не авторизация... А чтобы определять выполняться для определенного пользователя
тот или иной метод, нужно писать авторизацию на основе ролей или заявок... 
-------------------------------------------------------------------------------------------------------------------

А теперь собсно мой вопрос к гуру WCF-а и безопасности в нем smile

У меня случай такой:
Пользователи на мой WCF-сервис попадают через инет по логину и паролю (т.е. все что должно требоваться от пользвателя  это тока знание логина и пароля...).
Данные должны шифроваться - т.е. пользователь через http аналайзер не должен увидеть ни данных, ни логина и пароля... 
Авторизация должна быть на основе ролей.... Использовать поставщика ролей ASP.NET никакой возможности нет Кастомный провайдер ролей писать тоже очень не
желательно... 

Т.е. основной преемлимый вариант - писать кастомные методы для сопоставления пользователя ролям (с этим кстате проблем нет).

Проблема в том как передать идентификационную информацию сервису  - в UserNameValidator, причем так чтоб её другие не сперли... Какую привязку лучше для
этого использовать, как её конфигурировать, как сделать шифрование... ?
Смотрел уже и MSDN и в других источниках... - информация преподнесена ацтой как (это я про MSDN и про ReMIX), да ещё и в примерах ошибки...    :(

 

Автор: Medic 1.3.2010, 06:10
А как можно UserName/Password авторизацию реализовать при basicHttpBinding (SilverLight только его поддерживает)?
WCF сервис хостится вне IIS.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)