Prevent a page or handler from updating the FormsAuthentication ticket
I have several apps on a single domain that share the formsauthentication ticket for single sing-on. We also have javascript on each page that will warn the user 2 minutes prior to his session expiration and allow him to logout or extend his session. When the countdown reaches 0, they are automatically logged off. This all works great. However, it fails miserably when a user opens multiple browser windows or tabs to work on the various apps. If window A times out while the user is working in window B, they are signed out of B on the next request.
I have a solution, but I can't seem to implement it. Ba开发者_运维知识库sically, when the page is rendered, I write out the ticks of the ticket issue date. Then, when auto-logging out, I want to ajax call a handler to see if those ticks match the current formsauthentication ticket ticks. If they don't match, then I know another app has been refreshing the ticket and I won't log them out. The problem is that I can't create a generic handler that doesn't update the ticket because we use slidingexpiration = true.
function CompareTicket(ticks) {
$.getJSON('http://localhost/MyApp/CompareTicket.ashx?t=' + ticks, function (data) {
if (data == 0)
SessionEnd();
else
SessionExtend();
});
}
In the below handler code, data is returned as 0 if the value I'm sending in the querystring matches the ticks of the current ticket. Unfortunately, the mere act of requesting the handler will update the ticket and always send back the new tick count.
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "application/json";
context.Response.ContentEncoding = Encoding.UTF8;
long ticketTicks = ((FormsIdentity)context.User.Identity).Ticket.IssueDate.Ticks;
if (ticketTicks == 0)
context.Response.Write("0");
else
{
long ticks = 0;
if (long.TryParse(context.Request.QueryString["t"], out ticks))
{
if (ticketTicks == ticks)
context.Response.Write("0");
else
context.Response.Write(ticketTicks.ToString());
}
else
context.Response.Write("0");
}
}
Any ideas on how to get a JSON request from the server without extending the ticket?
In the asp.net request life cycle authentication modules execute before httphandlers. One workaround is to remove those authentication modules for specific url so when httphandler is hit it would not renew your ticket. something like this
<location path="/MyApp/CompareTicket">
<system.web>
<httpModules>
<remove name="FormsAuthenticationModule"/>
</httpModules>
</system.web>
</location>
To get this straight, you have more than one app on the domain sharing the same forms authentication token for the purposes of SSO. When one app expires the token, you want it to detect this in all other 'open' apps at the time.
Since the token is just a cookie, you can configure a continuouly running javascript function (say every 30 seconds) to detect the presence of the forms auth cookie. If it disapears (as it would be logging out/expiring), you can use the script to throw up some alert etc.
This will work regardless of how many windows are open, or which apps are running. So long as the cookie exists, you can assume (client-side) that the user is authenticated.
精彩评论