.NET 6调用SOAP服务时Basic认证出现Realm错误
解决.NET 6 SOAP客户端Basic认证授权错误
遇到的错误:
The HTTP request is unauthorized with client authentication scheme 'Basic'. The authentication header received from the server was 'Basic realm="Custom realm"'.
这个问题通常是因为WCF客户端在服务器返回带自定义realm的Basic认证挑战时,未自动重新发送凭证导致的。以下是几种可行的解决方法:
方法1:手动添加Authorization请求头
通过实现IClientMessageInspector手动构造Basic认证头,确保请求每次都携带凭证:
先实现消息检查器和端点行为:
public class BasicAuthInspector : IClientMessageInspector { private readonly string _credentials; public BasicAuthInspector(string username, string password) { _credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{username}:{password}")); } public object BeforeSendRequest(ref Message request, IClientChannel channel) { if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out var propObj) && propObj is HttpRequestMessageProperty httpRequest) { httpRequest.Headers["Authorization"] = $"Basic {_credentials}"; } else { var newHttpRequest = new HttpRequestMessageProperty(); newHttpRequest.Headers["Authorization"] = $"Basic {_credentials}"; request.Properties.Add(HttpRequestMessageProperty.Name, newHttpRequest); } return null; } public void AfterReceiveReply(ref Message reply, object correlationState) { } } public class BasicAuthEndpointBehavior : IEndpointBehavior { private readonly string _username; private readonly string _password; public BasicAuthEndpointBehavior(string username, string password) { _username = username; _password = password; } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { clientRuntime.ClientMessageInspectors.Add(new BasicAuthInspector(_username, _password)); } public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { } public void Validate(ServiceEndpoint endpoint) { } }
修改客户端创建代码,添加这个行为:
var binding = CreateHttpsBinding(); using (var wsClient = new ExternalService(binding, new EndpointAddress(url))) { wsClient.Endpoint.Behaviors.Add(new BasicAuthEndpointBehavior(param.Username, param.Password)); await wsClient.OpenAsync(); var response = await wsClient.SomeCall(input); }
方法2:显式指定认证Realm
如果服务器要求匹配特定realm,直接设置客户端凭证的Realm属性,确保和服务器返回的一致:
var binding = CreateHttpsBinding(); using (var wsClient = new ExternalService(binding, new EndpointAddress(url))) { wsClient.ClientCredentials.UserName.UserName = param.Username; wsClient.ClientCredentials.UserName.Password = param.Password; wsClient.ClientCredentials.UserName.Realm = "Custom realm"; await wsClient.OpenAsync(); var response = await wsClient.SomeCall(input); }
方法3:配置HttpClientHandler预认证
修改绑定的HttpTransportBindingElement,使用自定义HttpClientHandler开启预认证,确保凭证在第一次请求就发送:
private HttpBindingBase CreateHttpsBinding(string username, string password) { var binding = new BasicHttpsBinding(BasicHttpsSecurityMode.Transport); binding.Security.Mode = BasicHttpsSecurityMode.Transport; binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic; binding.MessageEncoding = WSMessageEncoding.Mtom; binding.TransferMode = System.ServiceModel.TransferMode.Buffered; binding.MaxBufferSize = int.MaxValue; binding.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max; binding.MaxReceivedMessageSize = int.MaxValue; var transport = binding.CreateBindingElements().Find<HttpTransportBindingElement>(); if (transport != null) { var handler = new HttpClientHandler { Credentials = new NetworkCredential(username, password), PreAuthenticate = true }; transport.HttpClientHandler = handler; } return binding; }
调用时传递用户名密码给绑定创建方法:
var binding = CreateHttpsBinding(param.Username, param.Password); using (var wsClient = new ExternalService(binding, new EndpointAddress(url))) { await wsClient.OpenAsync(); var response = await wsClient.SomeCall(input); }
内容的提问来源于stack exchange,提问作者gpanagopoulos
相关产品推荐
相关产品推荐

