带有自定义ASP.NET FormsAuthentication的奇怪超时

Ah, life. For me, in what I tend to do day to day, it always seems to come down to debugging weird stuff. So, here's something weird that happened today (actually it's been happening over the last week in QA).

啊,生活。 对我来说,在我日常工作中,似乎总是归结于调试怪异的东西。 因此,今天发生了一些奇怪的事情(实际上,这是在质量检查的最后一周发生的)。

Someone logs into an ASP.NET application successfully and does some stuff. They wait for 10.5 minutes. That means no clicking, just waiting. Then they click and get the next page successfully. Then the click on the NEXT (the second since they've been waiting) and get kicked out to the login page.

有人成功登录到ASP.NET应用程序并执行某些操作。 他们等待10.5分钟。 那意味着没有点击,只有等待。 然后,他们单击并成功获取下一页。 然后单击NEXT(他们一直在等待第二个),然后被踢出到登录页面。

Since our FormsAuthentication stuff not only authorizes the user into ASP.NET but also carries with it tokens into other "session-like" systems, folks dug around in those systems initially looking at Audit data, logs, everything. It just doesn't make sense.

由于我们的FormsAuthentication东西不仅授权用户使用ASP.NET,而且还将令牌携带到其他“类似于会话的”系统中,因此人们在这些系统中最初研究的是审核数据,日志和所有内容。 只是没有意义。

The FormsAuthentication timeout is set to 20 minutes and folks are getting "logged out" at 10.5 minutes. They set the timeout to 600 minutes and folks get kicked at 10.5 minutes. Is the timeout value being ignored?

FormsAuthentication超时设置为20分钟,而人们在10.5分钟时被“注销”。 他们将超时设置为600分钟,而人们则被踢到10.5分钟。 超时值是否被忽略?

As with most things, we return to first principles and pull out ieHttpHeaders. Why? Because in the context of a Web Application, clicking and looking at HTML only tells you that you clicked and look at some HTML.

与大多数事情一样,我们返回第一原理并提取ieHttpHeaders 。 为什么? 因为在Web应用程序的上下文中,单击并查看HTML只会告诉您单击并查看一些HTML。

TIP: When using ieHttpHeaders or any sniffers, turn OFF Images in your browser. Chances are they aren't the issue (although they might well be) but initially you'll save yourself some clutter in initial debugging.)

提示:使用ieHttpHeaders或任何嗅探器时,请在浏览器中关闭图像。 可能不是问题所在(尽管可能是问题所在),但是起初您会在初始调试中省去一些麻烦。)

So, here's what we saw (abridged and #commented):

因此,这就是我们看到的内容(摘要和注释):

#LOGGING INPOST /fooapp/Login.aspx HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)
Host: foo.corillian.net
Content-Length: 104
FI=fooapp&Destination=Accounts%2Ffooapp%2Fsummary.aspx&UserName=BAR

#在登录POST /fooapp/Login.aspx HTTP / 1.1 内容类型:application / x-www-form-urlencoded 接受编码:gzip,放气用户代理:Mozilla / 4.0(兼容; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322) 主持人:foo.corillian.net 内容长度:104 FI = fooapp&Destination = Accounts%2Ffooapp%2Fsummary.aspx&UserName = BAR

#LOGIN COOL, REDIRECTING and setting NEW AUTH COOKIEHTTP/1.1 302 FoundDate: Mon, 11 Jul 2005 18:54:31 GMTServer: Microsoft-IIS/6.0Location: http://foo.corillian.net/fooapp/somepath.aspxSet-Cookie: AuthenticationTicket=32EFESNIPCC65879; path=/Content-Type: text/html; charset=utf-8Content-Length: 182

#LOGIN COOL,重新定向和设置新AUTH COOKIE HTTP / 1.1 302找到日期:2005年7月11日星期一18:54:31 GMT服务器:Microsoft-IIS / 6.0位置: http : //foo.corillian.net/fooapp/somepath.aspx Set-Cookie:AuthenticationTicket = 32EFESNIPCC65879 ; path = / Content-Type:text / html; charset = utf-8Content-Length:182

#GOING WHERE YOU SAID, RETURNING NEW AUTH COOKIE
GET /fooapp/somepath.aspx HTTP/1.1
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)
Host: foo.corillian.net
Cookie: AuthenticationTicket=32EFESNIPCC65879

#前往您所说的地方,返回新的AUTH COOKIE GET /fooapp/somepath.aspx HTTP / 1.1 接受编码:gzip,放气用户代理:Mozilla / 4.0(兼容; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322) 主持人:foo.corillian.net Cookie:AuthenticationTicket = 32EFESNIPCC65879

#GOT TO WHERE I WAS GOING
HTTP/1.1 200 OK
Date: Mon, 11 Jul 2005 18:54:32 GMT
Server: Microsoft-IIS/6.0
Content-Type: text/html; charset=utf-8
Content-Length: 18224

#我要去的地方HTTP / 1.1 200 OK 日期:2005年7月11日星期一18:54:32 GMT 伺服器:Microsoft-IIS / 6.0 内容类型:text / html; 字符集= utf-8 内容长度:18224

WAIT 10.5 MINUTES HERE

等待10.5分钟

#CLICK AND GO TO A NEW PAGEGET /fooapp/newpage.aspx HTTP/1.1Referer: http://foo.corillian.net/fooapp/somepath.aspxUser-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)Host: foo.corillian.netCookie: AuthenticationTicket=32EFESNIPCC65879;

#单击并转到新页面以获取/fooapp/newpage.aspx HTTP / 1.1参考网址: http : //foo.corillian.net/fooapp/somepath.aspx用户代理:Mozilla / 4.0(兼容; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)主机:foo.corillian.netCookie:AuthenticationTicket = 32EFESNIPCC65879 ;

#GOT TO THAT NEW PAGE
HTTP/1.1 200 OK
Date: Mon, 11 Jul 2005 19:05:18 GMT
Server: Microsoft-IIS/6.0
Set-Cookie: AuthenticationTicket=AB4665AB0B7495; path=/; secure
Content-Type: text/html; charset=utf-8
Content-Length: 75510

#转到该新页面HTTP / 1.1 200 OK 日期:2005年7月11日星期一19:05:18 GMT 伺服器:Microsoft-IIS / 6.0 Set-Cookie:AuthenticationTicket = AB4665AB0B7495 ; 路径= /; 安全内容类型:text / html; 字符集= utf-8 内容长度:75510

#CLICK AND GO TO THE SECOND PAGE SINCE WE WAITED
GET /fooapp/secondpage.aspx HTTP/1.1
Referer:
http://foo.corillian.net/fooapp/newpage.aspx
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)
Host: foo.corillian.net
Connection: Keep-Alive

#Wicked and go to第二页,因为我们已经等待GET /fooapp/secondpage.aspx HTTP / 1.1 引荐来源: http //foo.corillian.net/fooapp/newpage.aspx 用户代理:Mozilla / 4.0(兼容; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322) 主持人:foo.corillian.net 连接:保持活动

#NO COOKIES WERE PASSED IN, REDIRECTED TO LOGIN!HTTP/1.1 302 FoundDate: Mon, 11 Jul 2005 19:05:27 GMTServer: Microsoft-IIS/6.0Location: http://foo.corillian.net/fooapp/login.aspx?ReturnUrl=%2ffooapp%2fnewpage.aspxContent-Type: text/html; charset=utf-8Content-Length: 209

#未输入Cookie,请重新登录! HTTP / 1.1 302找到日期:2005年7月11日星期一19:05:27 GMT服务器:Microsoft-IIS / 6.0位置: http : //foo.corillian.net/fooapp/login.aspx? ReturnUrl=%2ffooapp%2fnewpage.aspx内容-类型:text / html; charset = utf-8Content-Length:209

Where did it all go wrong? Well, we waited 10.5 minutes the clicked. Notice after we waited, we passed in the AuthenticationTicket we had, "32EFESNIPCC65879." This is the one that we generated ourselves in Login.aspx via:

哪里出错了? 好吧,我们等待了10.5分钟的点击次数。 请注意,在等待之后,我们传入了AuthenticationTicket,即“ 32EFESNIPCC65879” 。 这是我们在Login.aspx中通过以下方式生成的代码:

FormsAuthenticationTicket authTicket = new
 FormsAuthenticationTicket authTicket = new
    FormsAuthenticationTicket(1,   //version
    userName,                    // user name
    userName,                    // user name
    DateTime.Now,                  //creation
    DateTime.Now.AddMinutes(Timeout),  //Expiration
    DateTime.Now.AddMinutes(Timeout),  //Expiration
    false};
 
 
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
HttpCookie authCookie = new HttpCookie(
FormsAuthentication.FormsCookieName,encryptedTicket);   
 HttpCookie authCookie = new HttpCookie(
FormsAuthentication.FormsCookieName,encryptedTicket);   
HttpContext.Current.Response.Cookies.Add(authCookie);

This is a pretty typical thing to do when you're doing FormsAuthentication because you want might more control of the Ticket, or you might want to include user specific data in the cookie, etc.

在执行FormsAuthentication时,这是很典型的事情,因为您可能希望对票证有更多的控制,或者可能希望在cookie中包含用户特定的数据,等等。

But, after we waited and passed in the value we had, we got issued a NEW AuthenticationTicket with the value "AB4665AB0B7495" which is OK. Because the age of our original AuthenticationTicket was over 50% of the 20 minute timeout, the FormsAuthenticationModule was kind enough to renew the ticket. How nice of them. And a good thing too. They did it because the <forms> section of our web.config had slidingExpiration="true."

但是,在我们等待并传递了我们拥有的值之后,我们得到了一个新的AuthenticationTicket,其值为“ AB4665AB0B7495 ”,这是可以的。 由于原始AuthenticationTicket的使用期限超过20分钟超时的50%,因此FormsAuthenticationModule足以续订该票证。 他们真好。 也是一件好事。 他们之所以这样做,是因为我们的web.config的<forms>部分具有slideExpiration =“ true”。

However, then the next GET request includes No AuthenticationTicket at all! Why not? Notice the difference between the first issuance and the second. The second issuing of the cookie included "secure" at its end. Why? Because of this setting in the web.config:

但是,下一个GET请求根本不包含AuthenticationTicket! 为什么不? 注意第一次发行和第二次发行之间的区别。 Cookie的第二次发行在其末尾包含“安全”。 为什么? 由于web.config中的此设置:

<forms requireSSL="true"
    slidingExpiration="true"
    loginUrl="~/login.aspx"
    name="AuthenticationTicket"
    protection="All"
    timeout="20" />

<forms requireSSL =“ true” slideExpiration =“ true” loginUrl =“〜/ login.aspx” name =“ AuthenticationTicket” protection =“全部” timeout =“ 20” />

Ah! The first time the AuthenticationTicket was issued, it was issued by our custom code, and we didn't respect this setting! Later - 10.5 minutes later - when FormsAuthentication saw fit to issue a new cookie, they DID respect this flag.

啊! 第一次发出AuthenticationTicket时,它是由我们的自定义代码发出的,因此我们不遵守此设置! 稍后-10.5分钟后-当FormsAuthentication认为适合发出新的cookie时,他们DID尊重此标志。

Why was this a problem? Because in development were weren't running under SSL (https://). Had we been running under SSL this never would have been found. Had we gone live without SLL we would have seen flaky (read: impossible to reproduce) bugs with folks getting timeouts that didn't jive with our timeout settings.

为什么这是一个问题? 因为在开发中没有在SSL(https://)下运行。 如果我们一直在SSL下运行,那就永远找不到。 如果我们在没有SLL的情况下上线,我们会看到一些不稳定的错误(阅读:无法重现),因为人们收到的超时不符合我们的超时设置。

The Fix - respect the RequiresSSL flag in the inital issuing of the AuthenticationTicket and we would have seen the problem in development immediately upon login:

修复-在AuthenticationTicket的初始发行中遵守RequiresSSL标志,登录后我们会立即看到开发中的问题:

// Create the authentication ticket            
 // Create the authentication ticket            
FormsAuthenticationTicket authTicket = new
    FormsAuthenticationTicket(1,   //version
    FormsAuthenticationTicket(1,   //version
    userName,                    // user name
    DateTime.Now,                  //creation
    DateTime.Now,                  //creation
    DateTime.Now.AddMinutes(Timeout),  //Expiration
    false}
    false }
 
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName,encryptedTicket);    
 
 
//Make sure we mark the cookie as "Secure" if RequireSSL is set in the web.config.
// If we don't, the FIRST issuing of this cookie with not be secure 
 // If we don't, the FIRST issuing of this cookie with not be secure 
// (as we are the ones that did it) while the second issuing (when it's
// beign refreshed) will be secure. That would cause intermitant problems with 
 // beign refreshed) will be secure. That would cause intermitant problems with 
// timeout-like behaviors around "timeout/2" minutes into the user's session.
authCookie.Secure = FormsAuthentication.RequireSSL;
 authCookie.Secure = FormsAuthentication.RequireSSL;
HttpContext.Current.Response.Cookies.Add(authCookie);

And then I went to lunch.

然后我去吃午餐。

翻译自: https://www.hanselman.com/blog/weird-timeouts-with-custom-aspnet-formsauthentication

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值