使用 transportCredentialOnly 安全性对 RESTful WCF 服务的跨域 Ajax JSON POST 支持

Posted

技术标签:

【中文标题】使用 transportCredentialOnly 安全性对 RESTful WCF 服务的跨域 Ajax JSON POST 支持【英文标题】:Cross domain Ajax JSON POST support against RESTful WCF service using transportCredentialOnly security 【发布时间】:2012-07-05 01:33:08 【问题描述】:

我以前就这个主题发过帖子,但是在处理其他事情一年之后,我又设法再次陷入困境。我将尝试简要概述该场景以及当前使事情正常进行的尝试:

在主机上托管 html、JS 等的 IIS Web 服务器:iis.mycompany.com(简称 foo) WCF RESTful Web 服务通过主机上的 Windows 服务托管:wcf.mycompany.com(称为 bar

foo 提供的 javascript 通过对 bar 上的 WCF 服务进行 RESTful ajax 调用(GETPOST,具体取决于操作)来工作,显然这些是跨域调用,因为它们不在同一主机上。

Javascript 使用 jQuery (1.7.2) 框架来操作 DOM 并对 bar 执行 ajax 调用,POSTS 的预期内容类型是 JSON,而来自 @ 的响应987654329@ 也应该是JSON (application/json)。

Bar 将其 WCF 服务配置为使用 TransportCredentialOnly 作为安全模式,传输客户端凭据类型为 NTLM,因此只有经过身份验证的用户才能联系服务。

bar 的 WCF 服务已使用 WCF 扩展添加了对 CORS 的支持:

http://blogs.msdn.com/b/carlosfigueira/archive/2012/05/15/implementing-cors-support-in-wcf.aspx

我们添加了额外的标题,并根据大量互联网文章修改了帖子中已经包含的一些标题:

property.Headers.Add("Access-Control-Allow-Headers", "Accept, Content-Type");
property.Headers.Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
property.Headers.Add("Access-Control-Max-Age", "172800");
property.Headers.Add("Access-Control-Allow-Origin", "http://iis.mycompany.com");
property.Headers.Add("Access-Control-Allow-Credentials", "true");
property.Headers.Add("Content-type", "application/json");

提供有关启用 CORS 的信息的站点建议应将 Access-Control-Allow-Origin 响应标头设置为 "*",但是在我们的情况下这是不可能的,因为我们使用以下设置进行 jQuery ajax 调用:

$.ajaxSetup(
  cache: "false",
  crossDomain: true,
  xhrFields: 
    withCredentials: true
  
);

事实证明,当您在 ajax 调用中使用 "withCredentials" 时,您不能将 "*" 用于接受的来源:

https://developer.mozilla.org/en/http_access_control

"重要提示:响应凭据请求时,服务器 必须指定域,并且不能使用通配符。”

目前在我们的开发实验室中,这并不重要,因为我们可以将请求硬编码到 IIS (foo) 服务器 URL。

现在的主要问题似乎是尝试 POST 请求(GET 正在使用上述配置工作)。当浏览器尝试POST 进程时,它首先向服务器发送一个OPTIONS 标头,请求允许OPTIONS 用于后续发布。这是我们希望看到我们在 CORS 支持 WCF 扩展中配置的标头被传回的地方,但是我们还没有做到这一点;在响应返回为 “401 Unauthorized” 之前,我相信这与请求 NTLM 的传输安全绑定配置有关,但我不确定。

另外,我对此不是很有经验,但在执行跨域请求时,我没有看到太多关于 POST 使用 application/json 内容类型而不是 text/plain 的信息。

我知道人们可能会建议 JSONP 作为一个真正的解决方案,我并不反对不同的方法,实际上我鼓励任何人提出最佳做法,因为这将有助于其他人稍后阅读此问题。但是,请在提出替代方案之前尝试回答问题。

非常感谢任何做出贡献的人。

皮特斯基 :)

更新:

Chrome (20.x.x) 似乎不会遇到不协商 NTLM 以从服务器检索 OPTIONS 标头响应的问题,但 Firefox (13.0.1) 会。

我们还注意到有人已经在 Firefox 论坛上发布了一个错误,我们已将信息添加到:

http://bugzilla.mozilla.org/show_bug.cgi?id=751552

请投票支持在 bugzilla 网站上修复此错误!

使用下面的代码,我们可以观察网络跟踪,看看 Firefox 失败而 Chrome 工作正常:

var url = "http://myWebServiceServer/InstantMessagingService/chat/message/send";
var data = ' "remoteUserUri" : "sip:foo.bar@mydomain.com", "message" : "This is my message" ';            
var request = new XMLHttpRequest();                     
request.open("POST", url, true);
request.withCredentials = true;
request.setRequestHeader("Content-Type", "application/json");                   
request.send(data);                     
console.log(request);

另外说明,IE8 不支持 XMLHttpRequest 进行跨域调用,而是支持它自己的神奇 XDomainRequest 对象,因此我们在更改客户端代码以处理 IE8 与世界案例。 (感谢 IE8)。

/me 表示 Mozilla 修复了 Firefox 错误。

更新 2:

经过一番挖掘,似乎 IE8 的 XDomainRequest 不能用于发出必须协商 NTLM 的跨域请求,这基本上意味着由于 Web 浏览器的限制,我们的 WCF 绑定的安全性无法使用。

http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx

“不会随请求发送身份验证或 cookie”

所以,我想我们现在已经做到了这一点。看起来我们将不得不创建自己的自定义令牌身份验证并将其传递给 WCF 服务cookie,或者在 IE8 的情况下,POST 它带有 JSON。然后,WCF 服务必须处理解密数据并使用它来代替我们之前通过 NTLM 身份验证访问的ServiceSecurityContext.Current.WindowsIdentity

【问题讨论】:

【参考方案1】:

我知道您说过您希望问题本身得到解决,但您可以考虑使用“反向代理”。

我不知道您使用的是什么技术,但我们使用 Apache Web 服务器并在需要身份验证的不同服务器上运行 Java RESTful API。有一段时间,我们搞砸了 JSONP 和 CORS,但并不满意。

最后,我们设置了一个 Apache 反向代理,它创造了奇迹。 Web 浏览器认为它正在与自己的域进行通信并采取适当的行动。 RESTful API 不知道它是通过代理使用的。因此,一切正常。 Apache 做到了所有的魔法。

希望所有的网络服务器都具备像 Apache 的反向代理这样的功能。 以下是有关该功能的一些文档:http://httpd.apache.org/docs/2.2/mod/mod_proxy.html

我们所要做的就是确保安装了 mod_proxy 模块,然后将以下行添加到我们的 Apache 配置文件中:

ProxyPass /restapi http://restfulserver.com/restapi
ProxyPassReverse /restapi http://restfulserver.com/restapi

然后重启网络服务器,瞧!

【讨论】:

以上是关于使用 transportCredentialOnly 安全性对 RESTful WCF 服务的跨域 Ajax JSON POST 支持的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)