我们在生产环境中得到与此线程完全相同的错误。 [WIF Security Token Caching
有人解决了这个错误吗? 消息:ID4243:无法创建SecurityToken。在令牌缓存中找不到令牌,并且在上下文中找不到cookie。
以下是有关我们设置的一些信息:
•我们在.NET Framework 4.5.1中使用内置Windows Identity Framework
•问题几乎总是与从RelyingParty#X更改为RelyingParty #Y相关联(例如,当用户点击RP#Y他没有要求时退出时) - 当他在此事件后再次登录时,他被带到RP#Y
里面他要求的页面•我们正在使用e.SessionToken.IsReferenceMode = true; //在服务器上缓存,以获得更小的cookie
•通过使用IsReferenceMode = true,我们的FedAuth cookie存储了一个存储在我们数据库中的实际令牌的“指针”
•我们正在使用我们自己的DatabaseSecurityTokenCache,它覆盖了SessionSecurityTokenCache中的函数。通过使用DatabaseSecurityTokenCache和IsSessionMode = true,我们对服务器场友好(但我们也保证在所有登录会话中都在同一服务器内)所以如果由于某种原因应用程序池死了,我们'能够通过DatabaseSecurityTokenCache从数据库中获取令牌。我已经通过在会话中间完全杀死IIS来验证这一点(使用“net stop WAS”并使用“net start W3SVC”重新启动它,我们仍然可以从DatabaseSecurityTokenCache获取令牌)。我也试过通过简单地使用开箱即用的SessionSecurityTokenCache来做同样的事情,并且会分别失败(如预期的那样)
•默认令牌生存期为20分钟(但如果用户可以将其更改为40或60分钟) - 这只会在用户下次登录时生效(并且我们90%的用户只是使用默认的20分钟生命周期)
•我们正在使用证书(在所有服务器上都相同)来加密FedAuth cookie,而不是机器密钥(如果使用服务器场,使用不同的机器密钥,那将是灾难性的)
•所以所有服务器都可以解密从其他服务器加密的cookie。
•我们在RelyingParty4和RelyingParty5(两个不同的依赖方)中有一个带倒计时的javascript,用作“超时脚本”,以防用户离开他的计算机无人看管......他将在令牌时退出即将到期 - (减去)30秒(例如20分钟 - 30秒= 19,5分钟),空闲时间。这是保护我们非常敏感的银行信息,因此当用户回到他的机器时,他将需要再次登录。例如我们还使用滑动会话([http://www.cloudidentity.com/blog/2013/05/08/sliding-sessions-for-wif-4-5/]),当我们滑动时,客户端的javascript中的时间也会更新,以匹配令牌的长度减去30秒。这30秒用于确保注销时会话仍处于活动状态,因此它比令牌/会话的生命周期短一点。如果满足这个条件,我们目前正在滑动:总寿命/ 2 .... 20/2
•如果用户正在进行任何活动(即他正在四处走动,做一些工作),我们只会滑动。我们在minute10 +中滑动(如果令牌生命周期为20分钟),如上例所示
•我们多次调试了这个问题,这是我们得到的WIF错误:异常:System.IdentityModel.Tokens.SecurityTokenException消息:ID4243:无法创建SecurityToken。在令牌缓存中找不到令牌,并且在上下文中未找到cookie。源:Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler.ReadToken(XmlReader reader,SecurityTokenResolver tokenResolver)中的Microsoft.IdentityModel,位于Microsoft.IdentityModel.Web.SessionAuthenticationModule的Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler.ReadToken(Byte []标记,SecurityTokenResolver tokenResolver)。 ReadSessionTokenFromCookie(Byte [] sessionCookie)位于System.Web.HttpApplication.SyncEventExecutionStep.System的Microsoft.IdentityModel.Web.SessionAuthenticationModule.OnAuthenticateRequest(Object sender,EventArgs eventArgs)的Microsoft.IdentityModel.Web.SessionAuthenticationModule.TryReadSessionTokenFromCookie(SessionSecurityToken& sessionToken)。 System.Web.HttpApplication.ExecuteStep(IExecutionStep step,Boolean& completedSynchronously)中的Web.HttpApplication.IExecutionStep.Execute()
•我们已经能够通过使用旧的FedAuth cookie和这个插件重新生成错误:(注意!我们不确定这是否与PROD上发生的情况相同,但至少它在我们的Logging系统中给出了同样的错误。这很好,但我认为你应该添加一些步骤,告诉我们如何修改FedAuth cookie的内容,以便在本地实现这个问题.-你可以使用这个: 从以前的会话中获取FedAuth cookie的值很简单(在同一台机器上!不是来自另一台机器,这将无法工作)并将其粘贴到FedAuth cookie的值并刷新页面.- < / p>
用于修改Cookie的插件,在Chrome中称为“编辑此Cookie”: - 如果我们将此cookie的内容更改为上一个会话的值,并点击刷新(Chrome中的CTRL + R),我们就会得到臭名昭着的TokenSecurityException ID4243,并且RP会调用一个名为immrated的FederatedSignout,因为我们无法从这种情况中恢复过来。
退出....
我还应该提一下,我们采用了微软MSDN的文章标记为&#34;重要&#34;在IsReferenceMode上认真对待并将其添加到我们的
SessionAuthenticationModule_SessionSecurityTokenCreated事件:
e.SessionToken.IsReferenceMode = true;
取自MSDN:
重要! 要在参考模式下运行,Microsoft建议为global.asax.cs文件中的WSFederationAuthenticationModule.SessionSecurityTokenCreated事件提供处理程序,并在SessionSecurityTokenCreatedEventArgs.SessionToken属性中传递的令牌上设置SessionSecurityToken.IsReferenceMode属性。这将确保会话令牌在每个请求的参考模式下运行,并且优于仅在会话身份验证模块上设置SessionAuthenticationModule.IsReferenceMode属性。
以下是我们整个SessionAuthenticationModule_SessionSecurityTokenReceived, 请检查我提出的评论......它解释了所有事情的作用:
void SessionAuthenticationModule_SessionSecurityTokenReceived(object sender, SessionSecurityTokenReceivedEventArgs e)
{
if (e.SessionToken.ClaimsPrincipal != null)
{
DateTime now = DateTime.UtcNow;
DateTime validTo = e.SessionToken.ValidTo;
DateTime validFrom = e.SessionToken.ValidFrom;
TimeSpan lifespan = new TimeSpan(validTo.Ticks - validFrom.Ticks);
double keyEffectiveLifespan = new TimeSpan(e.SessionToken.KeyExpirationTime.Ticks - e.SessionToken.KeyEffectiveTime.Ticks).TotalMinutes;
double halfSpan = lifespan.TotalMinutes / 2;
if (validFrom.AddMinutes(halfSpan) < now && now < validTo)
{
SessionAuthenticationModule sam = sender as SessionAuthenticationModule;
// This will ensure a re-issue of the token, with an extended lifetime, ie "slide". Id deletes the current token from our databasetoken cache (with overriden Remove of the SessionSecurityTokenCache ) and writes a new one into the cache with the overriden AddOrUpdate of the SessionSecurityTokenCache.
// it will also write the token back into the cookie ( just the pointer to the cookie, because it's stored in database-cache ) because the IsReferenceMode = True is set
e.ReissueCookie = true; // Will force the DatabaseSecurityTokenCache'ið to clean up the cache with this, handler.Configuration.Caches.SessionSecurityTokenCache.Remove(key); internally in WIF's SessioAuthenticationModule
e.SessionToken = sam.CreateSessionSecurityToken(
e.SessionToken.ClaimsPrincipal,
e.SessionToken.Context,
now,
now.AddMinutes(lifespan.TotalMinutes),
false); // Make persistent, þannig að kakan lifir EKKI af browser-close / tab-lokun:
{
e.SessionToken.IsReferenceMode = true; // Cache on server
}
// Not needed, because if ReissueCookie = true; is set, it WILL to a WriteSessionTokenToCookie internally in WIF
//FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(e.SessionToken); // <---- er þetta e.t.v. bara það sem við þurfum ? Nei, á ekki að þurfa, er gert þegar tóki er búinn til með CreateSessionSecurityToken
}
else if (validTo < now)
{
// Fix
// http://blogs.planbsoftware.co.nz/?p=521
var sessionAuthenticationModule = (SessionAuthenticationModule)sender;
sessionAuthenticationModule.DeleteSessionTokenCookie(); // <--- is this really needed like the article says ? http://blogs.planbsoftware.co.nz/?p=521
e.Cancel = true; // This will allow a silent-login if the STS cookie is still valid, e.g. switching between RP's where we're switching from an active RP to a RP which has it's cookie outdated, but the STS's session is still alive. We don't want to prompt the user for a new login, beucase the STS session is still OK!
}
}
答案 0 :(得分:6)
这篇文章帮助了我,所以它可以帮助你和那些有这种错误的人。
void Application_OnError()
{
var ex = Context.Error;
if (ex is SecurityTokenException){
Context.ClearError();
if (FederatedAuthentication.SessionAuthenticationModule != null){
FederatedAuthentication.SessionAuthenticationModule.SignOut();
}
Response.Redirect("~/");
}
}
来自this链接。
希望它有用!
答案 1 :(得分:3)
----------更新,这是Lord02如何解决这个问题-----------
问题在于,当用户使用过时的cookie(来自之前的会话,即如果他们没有从我们的系统退出...而只是关闭选项卡)然后再次登录时, 我们在SessionMode = true的cookie ...尝试去DatabaseTokenCache从数据库中获取整个令牌,但正如我所说,我们的SSIS进程删除了所有比12小时更旧的令牌(过时的令牌!)所以我们不要#39; t有大量的孤儿令牌,这些令牌在我们的数据库中已经过时,并且不可用......只是占用了我们数据库中的空间。 因此,在删除完成后,每天晚上,DatabaseTokenCache GET的函数将不会返回有效的令牌...并且用户因为以下内容而退出:ID4243:无法创建SecurityToken。在令牌缓存中找不到令牌,并且在上下文中找不到cookie。
因此,我没有删除数据库中的Tokens,而是创建了一个特殊的处理程序,它在RP的网站上拦截了这个错误......并将用户重定向回STS,然后创建一个全新的令牌,将其写入DatabaseTokenCacheStore,如下所示
当cookie设置为“参考模式”并且缓存中不存在令牌时,抛出ID4243的异常 - 我可以确认这是设计和设计WIF不会将调用重定向到STS(重新启动身份验证过程)
为了克服这个问题,我拦截了这个异常并做出了适当的反应。 如果在我为此创建的customSessionAuthModule中出现此错误,我会重定向到颁发者:
public class CustomSessionAuthenticationModule : SessionAuthenticationModule
{
protected override void OnAuthenticateRequest(object sender, EventArgs eventArgs)
{
try
{
base.OnAuthenticateRequest(sender, eventArgs);
}
catch (SecurityTokenException exc)
{
// ID4243: Could not create a SecurityToken. A token was not found in the token cache and no cookie was found in the context.
if (exc.Message.IndexOf("ID4243", StringComparison.OrdinalIgnoreCase) >= 0)
{
// Returning directly without setting any token will cause the FederationAuthenticationModule
// to redirect back to the token issuer.
return;
}
else
{
throw;
}
}
}
}