强制执行Windows身份验证时,是否可以处理WCF服务的“跨源资源共享”请求?
我的情况:
我已经设置了通过webHttpBinding公开的自托管WCF服务。
应该使用jQuery从浏览器直接调用此服务。实际上,这将限制我使用basicHttpBinding或webHttpBinding。在这种情况下,我使用webHttpBinding来调用服务操作。
HTML页面(将称为WCF服务)是从与WCF服务不同的端口在同一台计算机上的Web服务器提供的。这意味着我需要CORS支持才能在Firefox,Chrome等系统中正常工作。
用户在调用WCF服务时必须使用Windows身份验证进行身份验证。为此,我已将webHttpBinding配置为使用传输安全模式“ TransportCredentialsOnly”。
W3C规定在这种情况下应使用CORS。简而言之,这意味着浏览器将检测到我正在执行跨域请求。在将请求实际发送到我的WCF服务之前,它将向我的WCF服务URL发送一个所谓的“预检”请求。此预检请求使用HTTP方法“ OPTIONS”,并询问是否允许原始URL(=为我的HTML服务的Web服务器)将请求发送到我的服务URL。然后,浏览器在将实际请求发送到我的WCF服务之前需要HTTP 200响应(=“ OK”)。我的服务收到的其他任何答复都将阻止发送实际的请求。
目前WCF尚未内置CORS,因此我使用WCF扩展点来添加CORS兼容性。
我的自托管服务的App.Config的服务部分:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyApp.DefaultServiceBehavior">
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="True"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="MyApp.DefaultEndpointBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<webHttpBinding>
<binding name="MyApp.DefaultWebHttpBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows"/>
</security>
</binding>
</webHttpBinding>
</bindings>
<services>
<service
name="MyApp.FacadeLayer.LookupFacade"
behaviorConfiguration="MyApp.DefaultServiceBehavior"
>
<endpoint
contract="MyApp.Services.ILookupService"
binding="webHttpBinding"
bindingConfiguration="MyApp.DefaultWebHttpBinding"
address=""
behaviorConfiguration="MyApp.DefaultEndpointBehavior"
>
</endpoint>
<host>
<baseAddresses>
<add baseAddress="http://localhost/Temporary_Listen_Addresses/myapp/LookupService"/>
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
我已经实现了一个IDispatchMessageInspector,它可以响应预检消息:
public class CORSSupport : IDispatchMessageInspector
{
private Dictionary<string, string> requiredHeaders;
public CORSSupport(Dictionary<string, string> requiredHeaders)
{
this.requiredHeaders = requiredHeaders ?? new Dictionary<string, string>();
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
HttpRequestMessageProperty httpRequest = request.Properties["httpRequest"] as HttpRequestMessageProperty;
if (httpRequest.Method.ToUpper() == "OPTIONS")
instanceContext.Abort();
return httpRequest;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
HttpRequestMessageProperty httpRequest = correlationState as HttpRequestMessageProperty;
HttpResponseMessageProperty httpResponse = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
foreach (KeyValuePair<string, string> item in this.requiredHeaders)
httpResponse.Headers.Add(item.Key, item.Value);
string origin = httpRequest.Headers["origin"];
if (origin != null)
httpResponse.Headers.Add("Access-Control-Allow-Origin", origin);
if (httpRequest.Method.ToUpper() == "OPTIONS")
httpResponse.StatusCode = HttpStatusCode.NoContent;
}
}
通过自定义IServiceBehavior属性注册此IDispatchMessageInspector。
我这样通过jQuery调用服务:
$.ajax(
{
url: 'http://localhost/Temporary_Listen_Addresses/myapp/LookupService/SomeLookup',
type: 'GET',
xhrFields:
{
withCredentials: true
}
}
)
.done(function () { alert('Yay!'); })
.error(function () { alert('Nay!'); });
这在IE10和Chrome中有效(我看到一个消息框,说“是!”),但在Firefox中不起作用。在Firefox中,我得到一个“不!” 以及HTTP 401(未经授权)错误。
401是由于我在服务配置中设置的“ Windows身份验证”所致。身份验证的工作方式是浏览器首先发送没有任何身份验证信息的请求。然后,服务器用HTTP 401(未授权)进行回复,指示要使用的身份验证方法。然后,浏览器通常会重新提交包含用户凭证的请求(此请求将继续正常进行)。
不幸的是,W3C似乎表明不应将凭据传递到CORS飞行前消息中。因此,WCF使用HTTP 401进行回复。似乎Chrome确实以某种方式在预检请求标头中发送了凭据(根据W3C规范,这实际上是不正确的),而Firefox则没有。此外,W3C仅识别对预检请求的HTTP 200响应:任何其他响应(例如我收到的HTTP 401)仅表示CORS请求失败,并且实际的请求可能未提交。
我不知道如何使这种(简单的)方案起作用。有人可以帮忙吗?
尤里卡(一种)。似乎Firefox不喜欢我为服务指定的“协商”身份验证。当我将身份验证方案从“协商,匿名”更改为“ Ntlm,匿名”时,它似乎起作用:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyApp.DefaultServiceBehavior">
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="True"/>
<serviceAuthenticationManager authenticationSchemes="Ntlm, Anonymous"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="MyApp.DefaultEndpointBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<webHttpBinding>
<binding name="MyApp.DefaultWebHttpBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="InheritedFromHost"/>
</security>
</binding>
</webHttpBinding>
</bindings>
<services>
<service
name="MyApp.FacadeLayer.LookupFacade"
behaviorConfiguration="MyApp.DefaultServiceBehavior"
>
<endpoint
contract="MyApp.Services.ILookupService"
binding="webHttpBinding"
bindingConfiguration="MyApp.DefaultWebHttpBinding"
address=""
behaviorConfiguration="MyApp.DefaultEndpointBehavior"
>
</endpoint>
<host>
<baseAddresses>
<add baseAddress="http://localhost/Temporary_Listen_Addresses/myapp/LookupService"/>
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
我以为Firefox支持“协商”方案……任何人都知道为什么它不起作用?
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句