【发布时间】:2016-04-18 12:43:31
【问题描述】:
我目前正在使用各种 Ajax 请求来保存、加载和自动完成数据的站点工作。它是使用 C#、MVC 和 JQuery 构建的。 MVC 控制器上的所有操作都需要对用户进行授权,我们使用 IdentityServer3 进行身份验证。使用NuGet安装,当前版本为2.3.0。
当我打开页面并按下按钮时,一切正常。当某个会话到期时,似乎会出现该问题。如果我闲置一段时间,并尝试使用 Ajax 函数,则会生成以下错误:
XMLHttpRequest 无法加载 https://identityserver.domain.com/connect/authorize?client_id=Bar&redirect_uri=http%3a%2f%2flocalhost%3a12345&response_mode=form_post&response_type=id_token+token&scope=openid+profile+email+phone+roles [...]。请求的资源上不存在“Access-Control-Allow-Origin”标头。 Origin 'http://localhost:12345' 因此不允许访问。
根据我对 Ajax 的了解,问题本身非常简单。 MVC 站点丢失了对当前会话的跟踪,它正在要求客户端再次进行身份验证。我从 Ajax 请求得到的响应是“302 Found”,带有指向我们的 IdentityServer 的 Location-header。 IdentityServer 恰好在另一个域上,虽然当您执行常规 HTTP 请求时它工作得很好,但它对于 Ajax 请求并不特别好。 “同源策略”直接阻止 Ajax 功能进行身份验证。如果我刷新页面,我将被重定向到 IdentityServer 并正常进行身份验证。几分钟后一切都会恢复正常。
解决方案可能是在来自 IdentityServer 的响应消息中添加一个额外的标头,明确指出此服务允许跨域请求。
我目前没有从 IdentityServer 获取此标头(在 Fiddler 中检查)。
According to the docs,默认开启。我检查过我们确实以这种方式启用了 CORS:
factory.CorsPolicyService = new Registration<ICorsPolicyService>(new DefaultCorsPolicyService { AllowAll = true });
这是我的一位客户:
new Client
{
Enabled = true,
ClientName = "Foo",
ClientId = "Bar",
ClientSecrets = new List<Secret>
{
new Secret("Cosmic")
},
Flow = Flows.Implicit,
RequireConsent = false,
AllowRememberConsent = true,
AccessTokenType = AccessTokenType.Jwt,
PostLogoutRedirectUris = new List<string>
{
"http://localhost:12345/",
"https://my.domain.com"
},
RedirectUris = new List<string>
{
"http://localhost:12345/",
"https://my.domain.com"
},
AllowAccessToAllScopes = true
}
这些设置不起作用。我注意到这里的 URI 中有一个额外的正斜杠,但是如果我删除它们,我会收到默认的 IdentityServer-error,指出客户端未授权(错误的 URI)。如果我部署站点(而不是运行 localhost 调试),我会使用不带斜杠的域名,我会得到与调试时完全相同的行为。我确实注意到上面的错误消息中没有斜杠,我认为这可能是问题所在,直到我在站点的已部署版本中看到相同的内容。
我还创建了自己的策略提供程序,如下所示:
public class MyCorsPolicyService : ICorsPolicyService
{
public Task<bool> IsOriginAllowedAsync(string origin)
{
return Task.FromResult(true);
}
}
...然后我将它插入 IdentityServerServiceFactory,如下所示:
factory.CorsPolicyService = new Registration<ICorsPolicyService>(new MyCorsPolicyService());
这个想法是让它返回 true 而不管来源如何。这也不起作用;结果和之前一模一样。
我已经阅读了关于这个特定主题的十几个其他主题,但我无处可去。据我所知,在设置不同站点时,我们没有做任何不寻常的事情。这一切都是开箱即用的。有什么建议吗?
----- 更新 -----
问题仍然存在。我现在尝试了一些新的策略。我在某处读到 cookie 身份验证对 Ajax 请求不利,我应该改用不记名令牌。我在 Ajax 中这样设置:
$(function () {
$(document).ajaxSend(function (event, request, settings) {
console.log("Setting bearer token.");
request.setRequestHeader("Authorization", "Bearer " + $bearerToken);
});
});
Chrome 和 Fiddler 中的控制台都确认令牌确实存在并由 JQuery 发送。我使用的令牌来自 HttpContext.GetOwinContext().Authentication.User 的声明主体对象上的 access_token-property。
这并没有多大作用。我仍然从服务器收到 302 响应,Fiddler 显示令牌没有通过以下 Ajax 请求(这是一个 GET 请求)发送到 IdentityServer。
从那里,我读到了这个帖子: Handling CORS Preflight requests to ASP.NET MVC actions 我试图将此代码放入 IdentityServer 的 startup.cs 中,但似乎没有“预检”请求进入。我在 Fiddler 中看到的只是这个(从一开始):
1 - 从客户端到 MVC 控制器的初始 Ajax 请求:
POST http://localhost:12345/my/url HTTP/1.1
Host: localhost:12345
Connection: keep-alive
Content-Length: pretty long
Authorization: Bearer <insert long token here>
Origin: http://localhost:12345
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
Referer: http://localhost:12345/my/url
Accept-Encoding: gzip, deflate
Accept-Language: nb-NO,nb;q=0.8,no;q=0.6,nn;q=0.4,en-US;q=0.2,en;q=0.2
Cookie: OpenIdConnect.nonce.<insert 30 000 lbs of hashed text here>
param=fish&morestuff=salmon&crossDomain=true
2 - 来自 MVC 控制器的重定向响应:
HTTP/1.1 302 Found
Cache-Control: private
Location: https://identityserver.domain.com/connect/authorize?client_id=Bar&redirect_uri=http%3a%2f%2flocalhost%3a12345%2f&response_mode=form_post&response_type=id_token+token&scope=openid+profile+email [...]
Server: Microsoft-IIS/10.0
X-AspNetMvc-Version: 5.2
X-AspNet-Version: 4.0.30319
Set-Cookie: OpenIdConnect.nonce.<lots of hashed text>
X-SourceFiles: <more hashed text>
X-Powered-By: ASP.NET
Date: Fri, 15 Jan 2016 12:23:08 GMT
Content-Length: 0
3 - 对 IdentityServer 的 Ajax 请求:
GET https://identityserver.domain.com/connect/authorize?client_id=Bar&redirect_uri=http%3a%2f%2flocalhost%3a12345%2f&response_mode=form_post&response_type=id_token+token&scope=openid+profile+email [...]
Host: identityserver.domain.com
Connection: keep-alive
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://localhost:12345
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://localhost:12345/my/url
Accept-Encoding: gzip, deflate, sdch
Accept-Language: nb-NO,nb;q=0.8,no;q=0.6,nn;q=0.4,en-US;q=0.2,en;q=0.2
4 - 来自 IdentityServer3 的响应
HTTP/1.1 302 Found
Content-Length: 0
Location: https://identityserver.domain.com/login?signin=<some hexadecimal id>
Server: Microsoft-IIS/8.5
Set-Cookie: SignInMessage.<many, many, many hashed bytes>; path=/; secure; HttpOnly
X-Powered-By: ASP.NET
Date: Fri, 15 Jan 2016 12:23:11 GMT
5 - Chrome 的崩溃
XMLHttpRequest 无法加载 https://identityserver.domain.com/connect/authorize?client_id=Bar&blahblahblah。请求的资源上不存在“Access-Control-Allow-Origin”标头。因此,不允许访问 Origin 'http://localhost:12345'。
【问题讨论】:
标签: ajax cors identityserver3