我们在ServiceStack 4中使用其余API的CORS功能时遇到了一些麻烦。
由于SS会话在cookie中,我们希望将cookie告诉api,因此我们在攻击API的角度客户端中使用“WithCredentials”= true来制作AJAX calles。
由于Chrome(至少)不喜欢带有WithCredentials的Access-Control-Allow-Origin通配符,我们添加了一个预请求过滤器,以回显请求者在Access-Control-Allow-Origin标头中的来源,如这样:
private void ConfigureCors()
{
Plugins.Add(new CorsFeature(
allowedHeaders: "Content-Type",
allowCredentials: true,
allowedOrigins: ""));
PreRequestFilters.Add((httpReq, httpRes) =>
{
string origin = httpReq.Headers.Get("Origin");
if (origin != null)
{
httpRes.AddHeader(HttpHeaders.AllowOrigin, origin);
}
else
{
// Add the dev localhost header.
httpRes.AddHeader(HttpHeaders.AllowOrigin, "http://localhost:9000");
}
});
PreRequestFilters.Add((httpReq, httpRes) =>
{
//Handles Request and closes Responses after emitting global HTTP Headers
if (httpReq.Verb == "OPTIONS")
{
httpRes.EndRequest();
}
});
}
但是,我们在OPTIONS请求上遇到了障碍,因为SS服务在请求结束时没有发回Access-Control-Allow-Origin标头。这会让Chrome拒绝来电。
我们尝试在OPTIONS的预请求过滤器中放置一个显式标头,但它仍然没有为OPTIONS调用返回一个ACAO标头:
PreRequestFilters.Add((httpReq, httpRes) =>
{
//Handles Request and closes Responses after emitting global HTTP Headers
if (httpReq.Verb == "OPTIONS")
{
httpRes.AddHeader(HttpHeaders.AllowOrigin, "*");
httpRes.EndRequest();
}
});
之前似乎已经处理过,但我们在StackOverflow上找不到类似的东西。
我们在OPTIONS预请求过滤器上做错了吗?为什么它不返回Access-Control-Allow-Origin标头?
答案 0 :(得分:4)
您应该在allowOriginWhitelist
中添加列入白名单的域名,例如:
Plugins.Add(new CorsFeature(allowedHeaders:"Content-Type",
allowCredentials:true,
allowOriginWhitelist:new[]{"http://localhost:9000"}));
在v4.0.35中引入了一个问题,其中PreRequestFilters were being written in Custom HttpHandlers导致CorsFeature写出 Access-Control-Allow-Origin 标头两次导致浏览器拒绝它。现在已经解决了in this commit,该版本可从v4.0.36 +现在available on MyGet获得。
此最新版本已部署到http://test.servicestack.net演示中,该演示显示了此jsbin中使用ServiceStack的跨域身份验证:http://jsbin.com/korijigucu/1/edit
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.2/angular.min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
<script>
var apiBase = "http://test.servicestack.net";
var app = angular.module('app', []);
app.run(['$rootScope', '$http', function ($rootScope, $http) {
$rootScope.success = "running...";
$http
.post(apiBase + '/auth/credentials',
{ "UserName": "test", "Password": "test" },
{ withCredentials: true })
.success(function (data) {
$rootScope.success = "Login successful: " + JSON.stringify(data);
})
.error(function (data, status, headers, config) {
$rootScope.error = 'ERR:login';
});
}]);
</script>
</head>
<body>
<div style='color:green'>{{success}}</div>
<div style='color:red'>{{error}}</div>
</body>
</html>
CorsFeature registration in above Test project的源代码:
Plugins.Add(new CorsFeature(
allowOriginWhitelist: new[] {
"http://localhost", "http://localhost:56500",
"http://test.servicestack.net", "http://null.jsbin.com" },
allowCredentials: true,
allowedHeaders: "Content-Type, Allow, Authorization"));
答案 1 :(得分:1)
注意: 更新到4.0.36解决了下面描述的doubled-header问题,导致第二个预请求过滤器过时。 < / p>
我终于让它发挥作用了,但感觉就像一块垃圾。
我添加了如Demis建议的allowOriginWhitelist条目,它返回了标题(Access-Control-Allow-Origin:http://localhost:9000
)的双倍值,至少对于OPTIONS调用(它似乎适用于POST和GET):
Plugins.Add(new CorsFeature(
allowedHeaders: "Content-Type, Allow, Authorization",
allowCredentials: true,
allowOriginWhitelist: new[] { "http://localhost:9000", "http://www.productiondomain.com", "https://www.productiondomain.com" }));
所以我根据我们使用的早期过滤器添加了以下预请求过滤器:
PreRequestFilters.Add((httpReq, httpRes) =>
{
//Handles Request and closes Responses after emitting global HTTP Headers
if (httpReq.Verb == "OPTIONS")
{
string origin = httpReq.Headers.Get("Origin");
if (origin != null)
{
httpRes.AddHeader(HttpHeaders.AllowOrigin, origin);
}
else
{
// Add the dev localhost header.
httpRes.AddHeader(HttpHeaders.AllowOrigin, "http://localhost:9000");
}
httpRes.EndRequest();
}
});
这已经解决了这个问题,但感觉就像一个kludge。有谁知道为什么这个简单的代码会在响应中产生双倍的ACAO标题?
Plugins.Add(new CorsFeature(
allowedHeaders: "Content-Type, Allow, Authorization",
allowCredentials: true,
allowOriginWhitelist: new[] { "http://localhost:9000", "http://www.productiondomain.com", "https://www.productiondomain.com" }));