我已经构建了一个带有Spring MVC的REST API后端,并使用Spring Security的基本身份验证进行保护。
我想从Javascript客户端对REST API进行跨域ajax调用。我不想使用 JSONP,因为我不想局限于 GET 调用。我使用 CORS,并且我在服务器端放置了正确的标头。
假设我的 REST API 在域 localhost:8087 上,我的客户端在 localhost:8086 上,这是跨域调用。
在我的Javascript客户端中,我使用jQuery进行ajax调用:
<script>
$.ajax ({
url: "http://localhost:8087/SpringMVC/users/user1",
beforeSend: function (xhr) { xhr.setRequestHeader ("Authorization", "Basic xxxxxxxxxxxx"); },
success: function(val) { console.log(val); alert("success" + val); },
error: function(val) { console.log(val); alert("error" + val); }
});
</script>
我的问题是jQuery不会在HTTP请求中发送授权标头,我不知道为什么。我不明白,因为我是在 beforeSend 方法中做的,所以它应该在 HTTP 请求中。结果:我有一个401错误。
当我尝试来自同一域的脚本时 localhost:8087,它不再是跨域的,我没有问题。
怎么可能?
我的脚本只是一个测试。我不打算将我的用户名/密码放在客户端。但是我想测试如何对基本的受身份验证保护的REST API进行ajax调用。我想我必须在服务器端发送以保护我的用户名/密码,REST API 会给我发回一个 cookie,我不再需要在下一次对 REST API 的 ajax 调用时传递用户名/密码。我说的对吗?
我已经使用Chrome Advanced REST客户端测试了我的REST API,它就是这样工作的。对于第一个请求,我需要传递授权标头。那么就不需要了。它是否也应该像我的 JavaScript Web 客户端一样工作?我打算使用Node.JS和Backbone来构建它。
多谢。
编辑2 :似乎真的是CORS浏览器的问题。我已经在服务器端添加了 OPTIONS 方法的标题 Access-Control-Allow-Methods,它适用于 Chrome。我可以访问 JSON 响应,不再有错误。但我仍然需要对下一个请求使用授权标头。如何告诉jQuery使用发送的cookie?
当我尝试使用 Firefox 11 时,我无法访问 json 响应,并且出现错误:
"NetworkError: 401 Non-Autorisé - http://localhost:8087/SpringMVC/users/user1"
显然,Chrome和Firefox对待跨域请求的方式略有不同。在执行跨域请求之前,他们使用 HTTP OPTIONS 方法执行所谓的"预检"请求。Chrome 和 Firefox 之间的区别在于,Chrome 还会发送带有凭据的授权标头,而 Firefox 不会。
然后,它仍然是一个 Spring 安全配置问题。我的网址/users/* 对所有 HTTP 方法都是安全的,包括选项。在 Firefox 的情况下,由于未发送授权标头,因此我的请求未获得授权。如果我将我的安全网址/users/*限制为GET方法,那么它非常适合Firefox。所以我只需要在我的 Spring 安全配置中添加它:
<intercept-url pattern="/users/*" access="isAuthenticated()" method="GET"/>
之后,我可以选择:我可以在 intercept-url 中添加其他要保护的方法,除了 OPTIONS,或者我可以在我的 Spring MVC 控制器中将 HTTP 方法调用限制为 GET,这甚至可以根据 Javadoc 处理我的 OPTIONS 调用。我选择了第二种解决方案。但是,如果有人找到一种解决方案来强制Firefox发送像Chrome这样的凭据,那就太好了,我会选择这个。
rico提供的Spring Security配置方案的另一个选项是:
<http ... use-expressions="true">
<intercept-url pattern="/users/*" access="permitAll" method="OPTIONS"/>
<intercept-url pattern="/users/*" access="isAuthenticated()"/>
...
</http>
HTTP OPTIONS 请求将始终通过身份验证,而任何其他 HTTP 方法都不会。
请注意<http>
元素中设置为 true 的 use-expressions
XML 属性。然后,Spring Security 将期望<intercept-url>
元素的access
属性包含 Spring EL 表达式,如 permitAll 和 isAuthenticated()。