关于ASP.NET表单身份验证和会话的滑动到期 [英] Concerning the sliding expiration of ASP.NET's forms authentication and session

查看:47
本文介绍了关于ASP.NET表单身份验证和会话的滑动到期的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们有一个使用本机表单身份验证和会话功能的ASP.NET 4.5 WebForms应用程序.两者都具有20分钟的超时时间以及滑动到期时间.

We have a ASP.NET 4.5 WebForms application using the native forms authentication and session functionality. Both have a timeout of 20 minutes with sliding expiration.

想象一下以下情况.用户在我们的应用程序中工作了一段时间,然后继续执行其他操作,从而使我们的应用程序空闲了20分钟.然后,用户返回到我们的应用程序以编写报告.但是,当用户尝试保存时,将使用登录屏幕来对待他/她,并且该报告将丢失.

Imagine the following scenario. A user has worked in our application for a while and then proceeds to do some other things, leaving our application idle for 20 minutes. The user then returns to our application to write a report. However, when the user tries to save, he/she is treated with the login screen, and the report is lost.

显然,这是不需要的.替代这种情况,我们希望在身份验证或会话过期时将浏览器重定向到登录页面.为了实现这一点,我们构建了一个Web Api服务,可以调用该服务来检查是否是这种情况.

Obviously, this is unwanted. Instead of this scenario, we want the browser to be redirected to the login page the moment either authentication or session has expired. To realize this, we have build a Web Api service that can be called to check whether this is the case.

public class SessionIsActiveController : ApiController
{
    /// <summary>
    /// Gets a value defining whether the session that belongs with the current HTTP request is still active or not.
    /// </summary>
    /// <returns>True if the session, that belongs with the current HTTP request, is still active; false, otherwise./returns>
    public bool GetSessionIsActive()
    {
        CookieHeaderValue cookies = Request.Headers.GetCookies().FirstOrDefault();
        if (cookies != null && cookies["authTicket"] != null && !string.IsNullOrEmpty(cookies["authTicket"].Value) && cookies["sessionId"] != null && !string.IsNullOrEmpty(cookies["sessionId"].Value))
        {
            var authenticationTicket = FormsAuthentication.Decrypt(cookies["authTicket"].Value);
            if (authenticationTicket.Expired) return false;
            using (var asdc = new ASPStateDataContext()) // LINQ2SQL connection to the database where our session objects are stored
            {
                var expirationDate = SessionManager.FetchSessionExpirationDate(cookies["sessionId"].Value + ApplicationIdInHex, asdc);
                if (expirationDate == null || DateTime.Now.ToUniversalTime() > expirationDate.Value) return false;
            }
            return true;
        }
        return false;
    }
}

客户端每10秒调用一次此Web Api服务,以检查身份验证或会话是否已过期.如果是这样,脚本会将浏览器重定向到登录页面.就像魅力一样.

This Web Api service is called every 10 seconds by the client to check if either authentication or session has expired. If so, the script redirects the browser to the login page. This works like a charm.

但是,调用此服务会触发身份验证和会话的滑动到期.因此,从本质上讲,创建永无止境的身份验证和会话.我已经在服务启动时设置了一个断点,以检查是否触发它的我们自己的功能之一.但这不是事实,它似乎在服务执行之前在ASP.NET的更深处发生.

However, calling this service triggers the sliding expiration of both authentication and session. Thus, essentially, creating never ending authentication and session. I have set a breakpoint at the start of the service to check if it is one of our own functions that triggers this. But this is not the case, it seems to occur somewhere deeper in ASP.NET, before the execution of the service.

  1. 是否可以针对特定请求禁用对ASP.NET身份验证和会话滑动过期的触发?
  2. 如果没有,解决这种情况的最佳实践是什么?

推荐答案

  1. 这似乎是不可能的.启用滑动到期后,将始终触发它.如果有一种方法可以访问会话而不进行扩展,那么我们将无法找到它.

  1. This seems to be impossible. Once sliding expiration is enabled, it is always triggered. If there is a way to access the session without extending it, we have not been able to find it.

那么如何解决这种情况?我们为问题最初提出的方案提出了以下替代方案.实际上,此方法效率更高,因为它不需要每隔x秒钟就使用网络服务拨打电话一次.

So how to tackle this scenario? We came up with the following alternative solution to the one originally proposed in the question. This one is actually more efficient because it doesn't use a web service to phone home every x seconds.

因此,我们希望有一种方法来知道ASP.NET的表单身份验证或会话何时过期,以便我们可以主动注销用户.每个页面上只有一个简单的JavaScript计时器(<由Khalid Abuhakmeh提出的)是不够的,因为用户可以使用该应用程序同时在多个浏览器窗口/标签中.

So we want to have a way to know when either ASP.NET's forms authentication or session has expired, so we can pro-actively logout the user. A simple javascript timer on every page (as proposed by Khalid Abuhakmeh) would not suffice because the user could be working with the application in multiple browser windows/tabs at the same time.

我们使此问题更简单的第一个决定是使会话的过期时间比表单身份验证的过期时间长几分钟.这样,会话将永远不会在进行表单身份验证之前过期.如果下一次用户尝试登录时有一个旧的会话,我们将放弃它以强制执行一个新的会话.

The first decision we made to make this problem simpler is to make the expiration time of the session a few minutes longer than the expiration time of the forms authentication. This way, the session will never expire before the forms authentication. If there is a lingering old session the next time the user tries to log in, we abandon it to force a fresh new one.

好的,所以现在我们只需要考虑表单身份验证的有效期即可.

All right, so now we only have to take the forms authentication expiration into account.

接下来,我们决定禁用表单身份验证的自动滑动过期时间(在web.config中设置),并为其创建自己的版本.

Next, we decided to disable the forms authentication's automatic sliding expiration (as set in the web.config) and create our own version of it.

public static void RenewAuthenticationTicket(HttpContext currentContext)
{
    var authenticationTicketCookie = currentContext.Request.Cookies["AuthTicketNameHere"];
    var oldAuthTicket = FormsAuthentication.Decrypt(authenticationTicketCookie.Value);
    var newAuthTicket = oldAuthTicket;
    newAuthTicket = FormsAuthentication.RenewTicketIfOld(oldAuthTicket); //This triggers the regular sliding expiration functionality.
    if (newAuthTicket != oldAuthTicket)
    {
        //Add the renewed authentication ticket cookie to the response.
        authenticationTicketCookie.Value = FormsAuthentication.Encrypt(newAuthTicket);
        authenticationTicketCookie.Domain = FormsAuthentication.CookieDomain;
        authenticationTicketCookie.Path = FormsAuthentication.FormsCookiePath;
        authenticationTicketCookie.HttpOnly = true;
        authenticationTicketCookie.Secure = FormsAuthentication.RequireSSL;
        currentContext.Response.Cookies.Add(authenticationTicketCookie);
        //Here we have the opportunity to do some extra stuff.
        SetAuthenticationExpirationTicket(currentContext);
    }
}

我们从应用程序的BasePage类的 OnPreRenderComplete 事件中调用此方法,其他所有页面均从该事件继承.它的功能与普通的滑动到期功能完全相同,但是我们有机会做一些额外的工作;就像调用我们的 SetAuthenticationExpirationTicket 方法.

We call this method from the OnPreRenderComplete event in our application's BasePage class, from which every other page inherits. It does exactly the same thing as the normal sliding expiration functionality, but we get the opportunity to do some extra stuff; like call our SetAuthenticationExpirationTicket method.

public static void SetAuthenticationExpirationTicket(HttpContext currentContext)
{
    //Take the current time, in UTC, and add the forms authentication timeout (plus one second for some elbow room ;-)
    var expirationDateTimeInUtc = DateTime.UtcNow.AddMinutes(FormsAuthentication.Timeout.TotalMinutes).AddSeconds(1);
    var authenticationExpirationTicketCookie = new HttpCookie("AuthenticationExpirationTicket");
    //The value of the cookie will be the expiration date formatted as milliseconds since 01.01.1970.
    authenticationExpirationTicketCookie.Value = expirationDateTimeInUtc.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds.ToString("F0");
    authenticationExpirationTicketCookie.HttpOnly = false; //This is important, otherwise we cannot retrieve this cookie in javascript.
    authenticationExpirationTicketCookie.Secure = FormsAuthentication.RequireSSL;
    currentContext.Response.Cookies.Add(authenticationExpirationTicketCookie);
}

现在,我们可以使用一个额外的Cookie,该Cookie始终代表正确的表单身份验证到期时间,即使用户在不同的浏览器窗口/选项卡中工作也是如此.毕竟,Cookie具有广泛的浏览器范围.现在剩下的只有一个JavaScript函数来验证cookie的值.

Now we have an extra cookie at our disposal that always represents the correct forms authentication expiration time, even if the user works in different browser windows/tabs. After all, cookies have a browser wide scope. Now the only thing left is a javascript function to verify the cookie's value.

function CheckAuthenticationExpiration() {
    var c = $.cookie("AuthenticationExpirationTicket");
    if (c != null && c != "" && !isNaN(c)) {
        var now = new Date();
        var ms = parseInt(c, 10);
        var expiration = new Date().setTime(ms);
        if (now > expiration) location.reload(true);
    }
}

(请注意,我们使用jQuery Cookie插件进行检索cookie.)

(Note that we use jQuery Cookie Plugin to retrieve the cookie.)

将此功能间隔一段时间,用户将在其表单身份验证到期时注销.Voilà:-)这种实现的额外好处是,您现在可以控制何时扩展表单身份验证的过期时间.如果您想要一堆不会延长有效期的Web服务,请不要为其调用 RenewAuthenticationTicket 方法.

Put this function in an interval, and users will be logged out the moment his or her forms authentication has expired. Voilà :-) An extra perk of this implementation is that you now have control over when the forms authentication's expiration gets extended. If you want a bunch of web services that don't extend the expiration, just don't call the RenewAuthenticationTicket method for them.

如果要添加任何内容,请发表评论!

Please drop a comment if you have anything to add!

这篇关于关于ASP.NET表单身份验证和会话的滑动到期的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆