WCF REST服务中的CORS支持

19

我有一个托管在 Windows 服务中的 WCF REST 服务,我希望在每个响应中发送 Access-Control-Allow-Origin HTTP 标头(作为 CORS 的一部分)。

我的尝试解决方法是在 IDispatchMessageInspector 实现中使用以下代码:

public void BeforeSendReply(ref Message reply, object correlationState)
{
    var httpResponse = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
    if (httpResponse != null)
    {
        // test of CORS
        httpResponse.Headers["Access-Control-Allow-Origin"] = "*";
    }
}

通常这个方法应该会生效, 但不幸的是我的服务也使用HTTP基本认证, 这意味着当请求没有Authorization头部时,WCF会自动发送一个需要凭证的401响应。不幸的是在这个初始交换过程中,WCF不会调用我的IDispatchMessageInspector,因此Access-Control-Allow-Origin头部不会被添加到初始交换中。

当我尝试从浏览器调用服务时出现问题。CORS规定,只有当源域与Access-Control-Allow-Origin响应头部中列出的域匹配(*代表所有域)时,才允许跨源请求。不幸的是,当浏览器看到没有Access-Control-Allow-Origin头部的初始401响应时,就会阻止访问(根据同源策略)。

有没有办法在WCF自动发送的初始401响应中添加头部?


你有没有在这方面取得任何进展? - Jason Kleban
4个回答

25
这个人救了我的一天。
我将在这里放置他的一些笔记,以防那个网页某天消失。 (我讨厌找到“你的答案就在这里”的链接,然后链接已经失效。) http://blogs.microsoft.co.il/blogs/idof/archive/2011/07.aspx
<behaviors> 
  <endpointBehaviors> 
    <behavior name="webSupport"> 
      <webHttp /> 
      <CorsSupport /> 
    </behavior> 
  </endpointBehaviors> 
</behaviors> 
<extensions> 
  <behaviorExtensions> 
    <add name="CorsSupport" type="WebHttpCors.CorsSupportBehaviorElement, WebHttpCors, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> 
  </behaviorExtensions> 
</extensions> 
<services> 
  <service name="Service.JSonService"> 
    <endpoint address="http://localhost:8080" behaviorConfiguration="webSupport” binding="webHttpBinding" contract="Service.IJSonService" /> 
  </service> 
</services>

现在,您需要找到他的可下载库文件,名为“WebHttpCors.dll”。

但以上内容足以帮助您通过谷歌/必应搜索解决问题。

在我的情况下,让我感到困惑的是IE可以工作,但Firefox无法工作。

我的起始页面是:

http://localhost:53692/test/WCFCallTestViaJQ14.htm

我的服务地址是:

http://localhost:8002/MyWCFService/MyWCFMethodByWebGet?state=NC&city=Raleigh

所以我有本地主机<<-->>本地主机的流量。

**** 但端口不同。(53692和8002)****

IE可以正常使用。Firefox不能正常使用。

然后你要记住每个浏览器在处理其.Send()请求时都有不同的方式(在JQUERY内部)。

现在一切都讲得通了。

//JavaScript snipplet from JQuery library

if (window.XMLHttpRequest) {

    returnObject = new XMLHttpRequest();

} else if (window.ActiveXObject) {

    returnObject = new ActiveXObject("Microsoft.XMLHTTP");

} else {

msg = "Your browser doesn't support AJAX!";

}

以下是我一直在谷歌/必应搜索的一些关键词和短语,最终让我找到了一些有用信息。

    Result: [Exception... "Component returned failure code: 0x80040111 (NS_ERROR_NOT_AVAILABLE) [nsIXMLHttpRequest.statusText]" nsresult: "0x80040111 (NS_ERROR_NOT_AVAILABLE)" location: "JS frame :: http://localhost:53692/test/WCFCallTestViaJQ14.htm :: HandleJQueryError :: line 326" data: no]


XMLHttpRequest Send "NS_ERROR_FAILURE"

JQuery Ajax WCF Self Hosted CORS JSON

现在,你需要阅读他的博客来理解代码正在做什么:
例如,他说:
“Access-Control-Allow-Origin”头部的值为“*”
这可能是你想要的,也可能不是。 你可能希望更好地控制这个值(头部)和其他值(方法和来源)。
开发环境是一回事。(使用所有的*符号)
生产环境则不同,你可能希望将这些*值调整为更具有辨别力的值。简而言之,你需要理解CORS在安全方面实际上为你做了什么,而不是添加一个让所有东西都能进入的行为。
  allowed-origins: '*'
  allowed-headers: '*'
  allowed-methods: '*'

点赞支持所有细节。继续努力对抗链接失效! - codekaizen

5
要实现您想要的功能,您需要自己处理授权,这可以通过实现和注册HttpModule来实现...在那里,您将发出401以及任何您想要的http头...甚至在SO上有一个示例实现-请参见Adding basic HTTP auth to a WCF REST service 编辑-根据OP的评论:
由于OP的评论表示他正在自行托管解决方案,因此解决方案不在于HTTPModule,而实际上是在IDispatchMessageInspector.BeforeSendReplyIDispatchMessageInspector.AfterReceiveRequest中进行处理。
授权必须配置为“None”,并在IDispatchMessageInspector中进行自定义实现/处理-这样您就可以在发出401时添加任何标头。否则,基本身份验证的运行时处理将在正确/积极身份验证之前不调用您的IDispatchMessageInspector
尽管这很有效,但请注意,这意味着您需要自己实施安全敏感的代码,并因此需要采取适当的措施来确保其正确实施...

感谢您的回复。我的服务需要是一个自托管的Windows服务,而不是IIS托管的。我的服务中的HTTP基本身份验证功能非常好用。CORS也很好用(假设我禁用基本身份验证)。问题只在自托管服务上混合使用HTTP基本身份验证和CORS时才会出现。如果我将服务托管在IIS中,我将配置IIS在每个请求上发送CORS响应头,并使用HttpModule进行HTTP基本身份验证。 - Kevin
好的 - 看到我的编辑了吗...你的解决方案已经完成了一半...你需要实现另一个方法并更改配置... - Yahia
设置clientCredentialType="None"并手动进行检查几乎可以工作。您可以发送401回复,但WCF不允许您设置WWW-Authenticate响应头。在代码中尝试设置WWW-Authenticate会导致WCF返回504错误。如果没有WWW-Authenticate,浏览器将不会提示凭据。 - Kevin
抱歉,但这对我来说就是终点了 - 我没有更多的想法...除了在IIS中托管并使用HttpModule方法... - Yahia
是的,我怀疑这可能是不可能的。我也尝试使用HttpListener而不是WCF,但它有相同的WWW-Authenticate问题。感谢您提供的想法。 - Kevin
我也遇到了同样的问题。我需要在IIS中进行托管。我肯定会研究一下http处理程序的实现。但是有没有其他选择,因为我是WCF、REST等方面的初学者,所以现在还不能深入实现。就像Kevin所说的那样,如果我们直接从浏览器访问,我的服务将不会提示凭据。 - Akhil

1

在 WCF 服务中被调用的第一个方法中添加以下行代码对我很有帮助。

WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*");

需要导入以下内容。
System.ServiceModel.Web;

参考此处以获取原始答案


-1

我尝试了很多方法,但是一无所获,最后突然发现头信息只能通过OPTIONS请求发送,然后我在这里找到了一些有用的SO代码here!这个代码完全解决了我的问题。

实际上,重点在于您必须在OPTIONS请求中添加头信息以及200 OK响应,这就是此链接所做的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接