Не удается вызвать веб-службу с базовой аутентификацией с помощью 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 - это сработало.
Я не уверен, почему это повлияло на то, что элемент не задал уровень безопасности на уровне сообщений... но он это сделал.