Не удается вызвать веб-службу с базовой аутентификацией с помощью WCF

Мне предоставлен веб-сервис, написанный на Java, на который я не могу вносить никаких изменений. Для доступа к любому из методов требуется аутентификация пользователя с базовой аутентификацией. Предлагаемый способ взаимодействия с этой службой в .NET заключается в использовании Visual Studio 2005 с установленной WSE 3.0.

Это проблема, поскольку в проекте уже используется Visual Studio 2008 (с таргетингом на .NET 2.0). Я мог бы сделать это в VS2005, однако я не хочу привязывать проект к VS2005 или делать это, создавая сборку в VS2005 и в том числе в VS2008-решении (которое в основном связывает проект с 2005 в любом случае для любых будущих изменений сборки). Я думаю, что любой из этих вариантов усложнит работу для новых разработчиков, заставив их установить WSE 3.0 и не позволит проекту использовать 2008 и функции в .NET 3.5 в будущем... т.е. я действительно верю, что использование WCF это путь.

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

Вот как выглядит мой код:

WebHttpBinding webBinding = new WebHttpBinding();
ChannelFactory<MyService> factory = 
     new ChannelFactory<MyService>(webBinding, new EndpointAddress("http://127.0.0.1:80/Service/Service/"));
factory.Endpoint.Behaviors.Add(new WebHttpBehavior());
factory.Credentials.UserName.UserName = "username";
factory.Credentials.UserName.Password = "password";

MyService proxy = factory.CreateChannel();
proxy.postSubmission(_postSubmission);

Это запустится и вызовет следующее исключение:

HTTP-запрос неавторизован с помощью схемы аутентификации клиента "Аноним". Заголовок аутентификации, полученный с сервера было "Основное царство = царство".

И это имеет внутреннее исключение:

Удаленный сервер возвратил ошибку: (401) Неавторизованный.

Любые мысли о том, что может вызвать эту проблему, будут с благодарностью.

Ответы

Ответ 1

Первый вопрос: это SOAP или служба Java на основе REST, которую вы пытаетесь вызвать?

Прямо сейчас, используя "webHttpBinding", вы используете подход на основе REST. Если служба Java является SOAP-службой, тогда вам нужно будет изменить привязку вместо "basicHttpBinding".

IF это служба на основе SOAP, вы должны попробовать следующее:

BasicHttpBinding binding = new BasicHttpBinding();

binding.SendTimeout = TimeSpan.FromSeconds(25);

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

EndpointAddress address = new EndpointAddress(your-url-here);

ChannelFactory<MyService> factory = 
             new ChannelFactory<MyService>(binding, address);

MyService proxy = factory.CreateChannel();

proxy.ClientCredentials.UserName.UserName = "username";
proxy.ClientCredentials.UserName.Password = "password";

Я использовал это с различными веб-службами, и он работает - большую часть времени.

Если это не сработает, вам нужно будет узнать больше о том, что ожидает веб-сервис Java и как отправить ему соответствующую информацию.

Марк

Ответ 2

Прежде всего, добавьте следующее в свой app.config или ваш web.config. (нет необходимости изменять это при перемещении по средам):

<system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_IConfigService">
                    <security mode="TransportCredentialOnly">
                        <transport clientCredentialType="Basic"/>
                    </security>
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost:55283/ConfigService.svc"
                binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IConfigService"
                contract="IConfigService" name="BasicHttpBinding_IService" />
        </client>
    </system.serviceModel>

Измените атрибут контракта на имя пространства имен. Обратите внимание на режим безопасности = TransportCredentialOnly

Теперь, чтобы программно изменить конечную точку и передать учетные данные, используйте следующий код:

            var myBinding = new BasicHttpBinding("BasicHttpBinding_IConfigService");
            var myEndpoint = new EndpointAddress("http://yourbaseurl/configservice.svc");
            var myChannelFactory = new ChannelFactory<IConfigService>(myBinding, myEndpoint);

            var credentialBehaviour = myChannelFactory.Endpoint.Behaviors.Find<ClientCredentials>();
            credentialBehaviour.UserName.UserName = @"username";
            credentialBehaviour.UserName.Password = @"password";

            IConfigService client = null;

            try
            {
                client = myChannelFactory.CreateChannel();
                var brands = client.YourServiceFunctionName();
                ((ICommunicationObject)client).Close();
            }
            catch (Exception ex)
            {
                if (client != null)
                {
                    ((ICommunicationObject)client).Abort();
                }
            }

Ответ 3

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

Несмотря на то, что он правильно установил режим безопасности = "Транспорт", он не установил clientCredentialType = "Basic". Я добавил к этому свой конфиг, и он все еще не работает. Затем я фактически удалил защиту сообщения, созданного инструментом, поскольку служба, с которой я связываюсь, является только SSL + Basic:

<message clientCredentialType="UserName" algorithmSuite="Default" />

Voila - это сработало.

Я не уверен, почему это повлияло на то, что элемент не задал уровень безопасности на уровне сообщений... но он это сделал.