ASP.NET会话超时后的推送重定向

26

我正在寻找有关会话过期时自动推送用户(即无需后台提交)的网站技术的教程、博客文章或帮助。感谢任何帮助。


1
“推送用户”是什么意思? - Robert C. Barth
10个回答

28
通常情况下,您可以设置会话超时时间,并且还可以添加一个页面头部,以便在会话超时之前自动将当前页面重定向到一个页面,在该页面中清除会话。

来自http://aspalliance.com/1621_Implementing_a_Session_Timeout_Page_in_ASPNET.2

namespace SessionExpirePage
{
    public partial class Secure : System.Web.UI.MasterPage
    {
        public int SessionLengthMinutes
        {
            get { return Session.Timeout; }
        }
        public string SessionExpireDestinationUrl
        {
            get { return "/SessionExpired.aspx"; }
        }
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            this.PageHead.Controls.Add(new LiteralControl(
                String.Format("<meta http-equiv='refresh' content='{0};url={1}'>", 
                SessionLengthMinutes*60, SessionExpireDestinationUrl)));
        }
    }
}

SessionExpireDestinationUrl应该链接到一个页面,在这个页面上你可以清除session和任何其他用户数据。

当刷新头过期时,它会自动将用户重定向到那个页面。


11

你无法通过网站直接“推送”客户端。你的网站可以响应来自客户端的请求,但仅限于此。

这意味着你需要编写一些客户端脚本(JavaScript),用于判断用户何时超时,可能是通过将当前时间与他们在站点cookie中最近的时间进行比较(每次用户访问站点页面时更新该时间),然后在差异大于一定量时进行重定向。

(我注意到有些人建议只需创建一个脚本,在页面上停留一定时间后即可转发用户。这在简单情况下可以工作,但如果用户在站点上开了两个窗口,并且一个窗口使用非常频繁,而另一个窗口则不是很频繁,那么不是很频繁的窗口将突然将用户重定向到转发页面,尽管用户已经一直在该站点上。此外,它与服务器端保持同步的任何会话保持都不是真正同步的。另一方面,它肯定更容易编写,如果这足够好,那就太棒了!)


7
在 <HEAD> 部分使用如下 META 刷新标签:
<meta http-equiv="refresh" content="0000; URL=target_page.html">

在这里,0000代表您的会话超时时间(以秒为单位),而target_page.html则是要重定向到的页面的地址。


3
你知道吗?我因为那个而爱你 :) 简单、易懂而且实用,对于像我这样的asp.net初学者完美无缺 :) - AlexK

4

使用自定义页面类和JavaScript也可以实现。

创建一个自定义的PageBase类,并将常用功能代码写入该类中。通过此类,我们可以将常用函数共享给其他网页。在此类中,我们需要继承System.Web.UI.Page类。请将以下代码放入PageBase类中:

PageBase.cs

namespace AutoRedirect
{
    public class PageBase : System.Web.UI.Page
    {
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            AutoRedirect();
        }

        public void AutoRedirect()
        {
            int int_MilliSecondsTimeOut = (this.Session.Timeout * 60000);
            string str_Script = @"
               <script type='text/javascript'> 
                   intervalset = window.setInterval('Redirect()'," +
                       int_MilliSecondsTimeOut.ToString() + @");
                   function Redirect()
                   {
                       window.location.href='/login.aspx'; 
                   }
               </script>";

           ClientScript.RegisterClientScriptBlock(this.GetType(), "Redirect", str_Script);
        }
    }
}

上面的 AutoRedirect function 将会在会话过期时重定向到登录页面,使用了 javascript window.setInterval。这个 window.setInterval 函数会按照指定的时间间隔重复执行一个 JavaScript 函数。在这里我们将时间间隔配置为会话超时时间。一旦达到会话过期时间,就会自动执行 Redirect 函数并控制转移到登录页面。

OriginalPage.aspx.cs

namespace appStore
{
    public partial class OriginalPage: Basepage
    {
        protected void Page_Load(object sender, EventArgs e)
        {
        }     
    }
}

OriginalPage.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="OriginalPage.aspx.cs" Inherits="AutoRedirect.OriginalPage" %>

Web.config

<system.web>    
    <sessionState mode="InProc" timeout="3"></sessionState>
</system.web>

注意: 使用Javascript的优点在于您可以在跳转前在提示框中显示自定义消息,这对用户非常有意义。 如果您不想使用Javascript,您也可以选择元重定向。
public void AutoRedirect()
{
    this.Header.Controls.Add(new LiteralControl(
        String.Format("<meta http-equiv='refresh' content='{0};url={1}'>",
            this.Session.Timeout * 60, "login.aspx")));
}

3

只需将以下代码片段复制并粘贴到您的Web.Config文件中:

<authentication mode="Forms">
  <forms loginUrl="~/Login.aspx" slidingExpiration="true" timeout="29" />
</authentication>

<sessionState timeout="30" mode="InProc" cookieless="false" />

你可以将这行代码放到你的 Site.Master 中:
Response.AppendHeader("Refresh", 
                      Convert.ToString((Session.Timeout * 60)) + 
                      ";URL=~/Login.aspx");

Response.AppendHeader("Refresh", Convert.ToString((Session.Timeout * 60)) + ";URL=" & System.Web.Security.FormsAuthentication.LoginUrl);响应.追加头("刷新", Convert.ToString((Session.Timeout * 60)) + ";URL=" & System.Web.Security.FormsAuthentication.LoginUrl); - Jeff Mergler

2
我是一名初学者,正在使用MVC3 ASP.net。我尝试了很多方法来解决我的会话问题(因为我在代码中使用了Session变量,在超时后,当我继续使用它时,我没有会话值)。我发现我的问题在配置文件中。认证和sessionState之间的超时时间应该非常接近。所以它们同时被清除了。//添加超时1和2进行测试...至少应该为29和30。
我也使用了其他方法,也可以解决这个问题:
从以下内容开始:
    protected void Session_Start(object src, EventArgs e)
    {
        if (Context.Session != null)
        {
            if (Context.Session.IsNewSession)//|| Context.Session.Count==0)
            {
                string sCookieHeader = Request.Headers["Cookie"];
                if ((null != sCookieHeader) && (sCookieHeader.IndexOf("ASP.NET_SessionId") >= 0))
                {
                    //if (Request.IsAuthenticated)
                     FormsAuthentication.SignOut();
                     Response.Redirect("/Account/LogOn");
                }
            }
        }

    }

    protected void Session_End(object sender, EventArgs e)
    {
     //Code that runs when a session ends. 
     //Note: The Session_End event is raised only when the sessionstate mode 
     //is set to InProc in the Web.config file. If session mode is set to StateServer
      //or SQLServer, the event is not raised. 
        Session.Clear();          
    }

并且:

public class SessionExpireFilterAttribute : ActionFilterAttribute
{

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        HttpContext ctx = HttpContext.Current;

        // check if session is supported
        if (ctx.Session != null)
        {

            // check if a new session id was generated
            if (ctx.Session.IsNewSession)
            {
                // If it says it is a new session, but an existing cookie exists, then it must
                // have timed out
                string sessionCookie = ctx.Request.Headers["Cookie"];
                if ((null != sessionCookie) && (sessionCookie.IndexOf("ASP.NET_SessionId") >= 0))
                {
                    ctx.Response.Redirect("~/Home/LogOn");
                }
            }
        }

        base.OnActionExecuting(filterContext);
    }
}

甚至还与Ajax一起工作,解决会话问题:

    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (Session.Count == 0 || Session["CouncilID"] == null)
            Response.Redirect("/Account/LogOn");

        if (Request.IsAjaxRequest() && (!Request.IsAuthenticated || User == null))
        {
            filterContext.RequestContext.HttpContext.Response.StatusCode = 401;
        }
        else
        {
            base.OnActionExecuting(filterContext);
        }
    }

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class AuthorizeUserAttribute : AuthorizeAttribute
    {
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            if (!httpContext.Request.IsAjaxRequest())
            {//validate http request.
                if (!httpContext.Request.IsAuthenticated
                    || httpContext.Session["User"] == null)
                {
                    FormsAuthentication.SignOut();
                    httpContext.Response.Redirect("~/?returnurl=" + httpContext.Request.Url.ToString());
                    return false;
                }
            }
            return true;
        }

        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            if (filterContext.HttpContext.Request.IsAjaxRequest())
            {
                filterContext.Result = new JsonResult
                {
                    Data = new
                    {
                        // put whatever data you want which will be sent
                        // to the client
                        message = "sorry, but you were logged out"
                    },
                    JsonRequestBehavior = JsonRequestBehavior.AllowGet
                };
            }
            else
            {
                base.HandleUnauthorizedRequest(filterContext);
            }
        }

    }

1

当然,您需要在控制器类或特定操作中使用[Authorize]

[Authorize]
public class MailController : Controller
{
}

1

如果您使用以下的登录控制器,它将在登录之前将您发送到请求的URL:

   [HttpPost]
    public ActionResult LogOn(LogOnModel model, string returnUrl)
    {

        if (ModelState.IsValid)
        {
            if (Membership.ValidateUser(model.UserName, model.Password))
            {

                FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);

                if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
                    && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
                {
                    //return Redirect(returnUrl);
                    if (!String.IsNullOrEmpty(returnUrl))
                    {
                        return Redirect(returnUrl);
                    }
                    else
                    {
                      return RedirectToAction("Index", "Home");
                    }

                }
                else
                {
                    return RedirectToAction("Index", "Home");
                }
            }
            else
            {
                ModelState.AddModelError("", "The user name or password provided is incorrect.");
            }
        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }

1

很遗憾,这是做不到的。会话超时只发生在服务器端,直到用户执行某种后台操作才能检测到。

然而,您可以注入一些HTML或JavaScript头代码,以在与会话超时相同的时间框架内自动将用户推送到注销页面。这并不能保证完美同步,如果您的用户正在进行一些耗时的操作而您没有重置计时器,则可能会遇到问题。

我通常将此代码添加到我的Page_Load事件中以实现此目的。

' Register Javascript timeout event to redirect to the login page after inactivity
  Page.ClientScript.RegisterStartupScript(Me.GetType, "TimeoutScript", _
                                              "setTimeout(""top.location.href = 'Login.aspx'""," & _
                                               ConfigurationManager.AppSettings("SessionTimeoutMilliseconds") & ");", True)

0
这对于AJAX请求来说会变得棘手,正如Zhaph - Ben Duguid所指出的那样。以下是我使用AJAX(使用Telerik Web控件,但我相信它们是使用ASP.NET AJAX工具包构建的)使其与AJAX一起工作的解决方案。
简而言之,我自己编写了滑动过期会话类型的东西。
在我的Site.Master中,我在每个postback上更新一个会话变量(postback或AJAX请求,因为AJAX请求仍然会触发Page_Load事件):
protected void Page_Load(object sender, EventArgs e)
    {
        if (!this.IsPostBack)
        {
            if (this.Request.IsAuthenticated)
                this.pnlSessionKeepAlive.Visible = true;
            else
                this.pnlSessionKeepAlive.Visible = false;
        }

        if (this.Session["SessionStartDateTime"] != null)
            this.Session["SessionStartDateTime"] = DateTime.Now;
        else
            this.Session.Add("SessionStartDateTime", DateTime.Now);
    }

然后在我的site.master标记中,我包含了一个iframe,其中包含一个ASPX页面,我在“幕后”使用它来检查并查看我的自定义滑动过期是否已过期:

<asp:Panel runat="server" ID="pnlSessionKeepAlive" Visible="false">
 <iframe id="frame1" runat="server" src="../SessionExpire.aspx" frameborder="0" width="0" height="0" / >
 </asp:Panel>

现在在我的SessionExpire.aspx页面中,我定期刷新页面并检查时间戳是否过期,如果是,则重定向到我的logout.aspx页面,然后确定将用户发送回哪个登录页面:

public partial class SessionExpire : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        /* We have to do all of this because we need to redirect to 2 different login pages. The default .NET
         * implementation does not allow us to specify which page to redirect expired sessions, its a fixed value.
         */
        if (this.Session["SessionStartDateTime"] != null)
        {
            DateTime StartTime = new DateTime();
            bool IsValid = DateTime.TryParse(this.Session["SessionStartDateTime"].ToString(), out StartTime);
            if (IsValid)
            {
                int MaxSessionTimeout = Convert.ToInt32(ConfigurationManager.AppSettings["SessionKeepAliveMins"]);
                IsValid = (DateTime.Now.Subtract(StartTime).TotalMinutes < MaxSessionTimeout);
            }

            // either their session expired or their sliding session timeout has expired. Now log them out and redirect to the correct
            // login page.
            if (!IsValid)
                this.Logout();
        }
        else
            this.Logout();

        // check every 60 seconds to see if the session has expired yet.
        Response.AddHeader("Refresh", Convert.ToString(60));
    }

    private void Logout()
    {
        this.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "TimeoutScript",
                    "setTimeout(\"top.location.href = '../Public/Logout.aspx'\",\"1000\");", true);
    }
}

非常感谢上面发布信息的人,这让我找到了解决方案,希望能帮助其他人。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接