我目前正在使用各种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 [...]。 No' Access-Control-Allow-Origin'标头出现在请求的资源上。起源' http://localhost:12345'因此不允许访问。
根据我对Ajax的了解,问题本身非常简单。 MVC站点已经忘记了当前会话,并且它要求客户端再次进行身份验证。我从Ajax请求得到的响应是" 302 Found",其中Location-header指向我们的IdentityServer。 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错误,指出客户端未被授权(错误的URI)。如果我部署站点(而不是运行localhost调试),我使用没有尾部斜杠的域名,我得到与调试时完全相同的行为。我注意到上面的错误消息中没有尾部斜杠,我认为这可能是问题,直到我在网站的部署版本中看到同样的事情。
我也建立了自己的政策提供者,如下:
public class MyCorsPolicyService : ICorsPolicyService
{
public Task<bool> IsOriginAllowedAsync(string origin)
{
return Task.FromResult(true);
}
}
...我将它插入IdentityServerServiceFactory,如下所示:
factory.CorsPolicyService = new Registration<ICorsPolicyService>(new MyCorsPolicyService());
这个想法是让它无论原点如何都返回真实。这也不起作用;与以前完全相同的结果。
我已经阅读了关于这一特定主题的十几个其他主题,但我无处可去。据我所知,当涉及到不同网站的设置时,我们没有做任何不寻常的事情。它几乎都是开箱即用的。有什么建议吗?
问题仍然存在。我现在尝试了一些新的策略。我在某地读过cookie身份验证对于Ajax请求不好,而且我应该使用bearer令牌。我在Ajax中设置了这个:
$(function () {
$(document).ajaxSend(function (event, request, settings) {
console.log("Setting bearer token.");
request.setRequestHeader("Authorization", "Bearer " + $bearerToken);
});
});
Chrome和Fiddler中的控制台都确认该令牌确实存在并由JQuery发送。我使用的令牌来自HttpContext.GetOwinContext()声明主体对象的access_token-property。身份验证。用户。
这没什么用。我仍然收到来自服务器的302响应,并且Fiddler显示该令牌未在以下Ajax请求(这是GET请求)上发送到IdentityServer。
从那里,我读了这个帖子: Handling CORS Preflight requests to ASP.NET MVC actions 我试图将这些代码放入IdentityServer的startup.cs中,但似乎没有&#34;预检&#34;要求进去。我在Fiddler看到的就是这个(从一开始):
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
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
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
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
XMLHttpRequest无法加载https://identityserver.domain.com/connect/authorize?client_id=Bar&blahblahblah。 No&#39; Access-Control-Allow-Origin&#39;标头出现在请求的资源上。起源&#39; http://localhost:12345&#39;因此不允许访问。
答案 0 :(得分:1)
我最近遇到过这个问题,它是由标题X-Requested-With与AJAX请求一起发送引起的。删除此标题或拦截它并使用401处理它将使您走上正确的轨道。
如果您没有此标头,则问题很可能是由触发Access-Control-Allow-Origin响应的不同标头引起的。
正如您所发现的,您在Identity Server中对CORS所做的任何事情都无法解决此问题。
答案 1 :(得分:1)
我也遇到了这个问题,UseTokenLifetime = false
没有解决问题,因为你在STS上放弃了令牌有效性。
当我尝试使用授权的api方法时,即使我在Owin上有效,我仍然有401。
我找到的解决方案是将UseTokenLifetime = true
保留为默认值,但是编写一个全局ajax错误处理程序(或角度http拦截器),如下所示:
$.ajaxSetup({
global: true,
error: function(xhr, status, err) {
if (xhr.status == -1) {
alert("You were idle too long, redirecting to STS") //or something like that
window.location.reload();
}
}});
触发身份验证工作流程。
答案 2 :(得分:0)
事实证明,问题出在MVC的客户端配置中。我错过了UseTokenLifetime属性,该属性应该设置为false。
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = "Bar",
Scope = "openid profile email phone roles",
UseTokenLifetime = false,
SignInAsAuthenticationType = "Cookies"
[...]
出于某种原因,IdentityServer将所有这些cookie设置为在分发后的5分钟内到期。此特定设置将覆盖IdentityServer的微小过期时间,而是使用aprox。 10个小时,或客户端应用程序中的默认值。
可以说这足以解决问题。然而,如果用户决定在网站上闲置10个小时,只点击Ajax按钮,它将不可避免地返回。
https://github.com/IdentityServer/IdentityServer3/issues/2424
答案 3 :(得分:0)
我使用OWIN Middleware for OpenIDConnect与另一个身份提供商有类似的问题。但是,行为发生在1小时而不是5分钟之后。解决方案是检查请求是否是AJAX请求,如果是,则强制它返回401而不是302.以下是执行此操作的代码:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = oktaOAuthClientId,
Authority = oidcAuthority,
RedirectUri = oidcRedirectUri,
ResponseType = oidcResponseType,
Scope = oauthScopes,
SignInAsAuthenticationType = "Cookies",
UseTokenLifetime = true,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
//...
},
RedirectToIdentityProvider = n => //token expired!
{
if (IsAjaxRequest(n.Request))
{
n.Response.StatusCode = 401;//for web api only!
n.Response.Headers.Remove("Set-Cookie");
n.State = NotificationResultState.HandledResponse;
}
return Task.CompletedTask;
},
}
});
然后,我使用Angular拦截器来检测401的statusCode,并重定向到身份验证页面。