【问题标题】:Preventing FormsAuthentication expiry time from increasing防止 FormsAuthentication 过期时间增加
【发布时间】:2015-11-10 14:37:23
【问题描述】:

我有一个使用表单身份验证的相对简单的基于 WebForms 的站点:

<authentication mode="Forms">
  <forms loginUrl="login.aspx" defaultUrl="secure/home.aspx" name=".AdminSite" />
</authentication>

由于没有明确提及,slidingExpiration 默认设置为 true,因此只要用户仍在浏览网站,就不会注销。

但是,我希望特定页面增加到期时间。这可能在web.config 内还是在代码中?我看到的唯一建议提到将slidingExpiration 设置为false,这将适用于侧面。

身份验证 cookie 设置使用:

FormsAuthentication.RedirectFromLoginPage(username, False)

因此更改身份验证 cookie 本身是不切实际的。

【问题讨论】:

    标签: asp.net webforms forms-authentication


    【解决方案1】:

    滑动过期由FormsAuthentication 模块通过在必要时重新发出cookie 来实现。为了防止滑动,您需要防止cookie更新发生。

    这可以通过简单地从响应中删除 FormsAuthentication cookie 来完成。

    下面是一个非常简单的网络表单的代码。 aspx 页面有一个 div,它显示了 Page_Load 事件的输出。

    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            testDiv.InnerHtml = "Hi, cookie is: " + HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName].Value;
            testDiv.InnerHtml += "<br />";
            var ticket = FormsAuthentication.Decrypt( HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName].Value);
            testDiv.InnerHtml += "Expires: " + ticket.Expiration.ToString("yyyy-MM-dd HH:mm:ss");
    
            if(Response.Cookies.AllKeys.Contains(FormsAuthentication.FormsCookieName))
                testDiv.InnerHtml += "<br />Forms auth is trying to update the cookie in this response";
    
        }
        protected void Page_Prerender(object sender, EventArgs e)
        {
            if (Response.Cookies.AllKeys.Contains(FormsAuthentication.FormsCookieName))
                Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
        }
    }
    

    Page_Prerender 事件会从响应中删除FormsAuthentication cookie(如果存在),从而防止滑动。

    我通过将FormsAuthentication 的超时设置为两分钟来测试这一点。然后我开始调试并登录。然后我不断刷新有问题的页面。

    由于FormsAuthentication 不会更新 cookie,除非过期时间已过一半,因此在第一分钟,页面将继续显示相同的加密 cookie 和相同的过期时间。一分钟多一点后,页面将报告FormsAuthentication 正在尝试更新cookie。但是Page_Prerender 删除了 cookie,因此它不会被发送。再过一分钟,您将被重定向到登录页面。

    测试相同但删除 Page_Prerender 方法表明 cookie 已更改,并且过期时间会在大约一分钟后更新。

    【讨论】:

    • 这是一种有趣的方式,而且非常紧凑——我会试一试。
    • 效果很好,实现起来也很简单。谢谢。
    【解决方案2】:

    您可以将响应 cookie 的到期日期设置为请求 cookie 的到期日期,从而有效地覆盖系统对该特定页面所做的操作。

    【讨论】:

    • 不幸的是,cookie 是基于会话的而不是持久的,因此没有设置过期时间。
    【解决方案3】:

    经过一番思考,我不再尝试更改 cookie 或创建第二个 cookie 或通过更改 Session.Timeout 来覆盖 cookie。我认为使用 System.Timers 实际上可能更容易使用计时器。如果你愿意,定时器的方法总是可以放在一个单独的类中。

    using System.Timers;
    
    
    
    public partial class MyPage:Page
    {
    
        private System.Timers.Timer timer;
    
        protected void Page_Load(object sender, EventArgs e)
        {
            SetTimer();
        }
        private void SetTimer()
        {
            // Interval is set in milliseconds- set as you please.
            timer = new System.Timers.Timer(1000 * 60);
    
            timer.Elapsed += OnTimedEvent;
            timer.AutoReset = true;
            timer.Enabled = true;
        }
    
        // In this handler, stop the timer and call a method to clear all cookies.
        private void OnTimedEvent(object source, ElapsedEventArgs e)
        {
            timer.Stop();
            ClearAllCookies();
        }
    
        // Method to clear all cookies. There may be a simpler way to do this, you are vague about your cookies, so I supplied a clear all.
    
        public void ClearAllCookies()
        {
            HttpCookie cookie;
            string cookieName;
            int cookieCnt = Request.Cookies.Count;
            for(int i = 0; i < cookieCnt; i++)
            {
                cookieName = Request.Cookies[i].Name;
                cookie = new HttpCookie(cookieName);
                // This causes the cookie to expire
                cookie.Expires = DateTime.Now.AddDays(-1); 
                Response.Cookies.Add(cookie);
            }
    
            Response.Redirect("LogIn.aspx");
        }
    }
    

    编辑

    使用一种方法将用户注销。无论哪种方式,您都将结束会话,而无需在网站的其余部分期间调整用户的身份验证,除非在此特定页面上的会话超时时结束它。

    public void ForceLogOff(){
    
        Session.Clear();
        Session.Abandon();
        Session.RemoveAll();
    
        // Do here whatever you need to do. 
        AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
    
        Response.Redirect("LogIn.aspx");
    }
    

    如何结束会话取决于您。这为您提供了一种覆盖滑动过期问题的方法,并仅在一个页面内设置自定义超时。

    【讨论】:

    • 我已经在问题中阐明了身份验证cookie是如何设置的;它是基于会话的,到期时间为零,因此无法更改到期时间。
    • 我不确定为什么您的解决方案会清除 cookie。您的解决方案似乎等同于简单地将slidingExpiration 设置为false,这不是我要解决的问题 - 我只希望单个页面在访问时不会延长到期时间。
    • 我会的,谢谢;我可以看到它是如何应用的,我只是担心如果用户访问另一个页面然后稍后返回到这个页面,他们可能会立即退出。