在FormsAuthenticationTicket超时后重新初始化context.User

时间:2012-02-08 21:27:13

标签: asp.net cookies forms-authentication httpmodule formsauthenticationticket

在我们的应用程序中,我们实现了基于角色的表单身份验证。这是使用RoleModule处理的,我们将角色数据保存在cookie中, 每次我们从cookie中读取数据并实例化IPrincipal对象。此代码在Application_OnPostAcquireRequestState方法中执行:

 HttpApplication application = source as HttpApplication;
 HttpContext context = application.Context;

if (context.User.Identity.IsAuthenticated &&
      (!Roles.CookieRequireSSL || context.Request.IsSecureConnection))
{
//Read the roles data from the Roles cookie..

context.User = new CustomPrincipal(context.User.Identity, cookieValue);
Thread.CurrentPrincipal = context.User;
}

这会初始化context.User对象。每次向服务器发出请求时,都使用上述流程对用户进行身份验证。 在Application_EndRequest中,我们使用当前主体对象数据更新Roles cookie。

我们的FormsAuthentication_OnAuthenticate页面中有Global.asax方法,我们在其中读取Cookie,更新Cookie并续订 票如果已过期。此外,在此方法中,如果故障单过期,我们会尝试在会话对象中设置用户名值。

FormsAuthentication oldTicket = FormsAuthentication.Decrypt(context.Request.Cookies[FormsAuthentication.FormsCookieName].Value);
if(oldTicket != null)
{
    if(oldTicket.Expired)
    {
        try
        {
            HttpContext.Current.Session["UserName"] = userName;
        }
        catch
        {
            //Log error.
        }

    FormsAuthentication newTicket =  new FormsAuthenticationTicket(oldTicket.Version, oldTicket.Name, DateTime.Now,
            DateTime.Now.AddMinutes(30), oldTicket.IsPersistent, oldTicket.UserData);

    string encryptedTicket = FormsAuthentication.Encrypt(newTicket);
    HttpCookie httpCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
    if (isPersistent)
    {
        httpCookie.Expires = DateTime.Now.AddMinutes(300);
    }
}

以下是我在web.config中的表单设置:

   <forms defaultUrl="Default.aspx" domain="" loginUrl="Login.aspx" name=".ASPXFORMSAUTH" timeout="20" slidingExpiration="true" />

会话超时为20分钟。

问题:如果用户空闲时间超过30分钟(即FormsAuth故障单续订持续时间),则context.User.Identity.IsAuthenticated值为 角色模块为falsecontext.User设置为NULL。现在,当用户请求页面时,他被重定向到登录页面。然而 饼干还在那里。如果再次,用户尝试请求页面,context.User.IsAuthenticated属性设置为true,用户将被带到 各页。此外,在FormsAuthentication_OnAuthenticate方法中,当我尝试设置会话值时,它会引发错误,因为会话对象为NULL

我想在这里实现的是,在持久性cookie的情况下,在auth票证超时后,用户不应该被注销,即用户应该 重新认证。

我怎样才能做到这一点? 如果我没有错,设置context.User应解决目的,但我该如何解决呢?

其他信息:

在故障单过期后我尝试请求页面时,事件查看器会显示错误消息:

Event code: 4005 
Event message: Forms authentication failed for the request. Reason: The ticket supplied has expired. 
Event time: 08-02-2012 20:02:05 
Event time (UTC): 08-02-2012 14:32:05 
Event ID: 048e3238ade94fd6a7289bac36d130ef 
Event sequence: 76 
Event occurrence: 2 
Event detail code: 50202 

Process information: 
Process ID: 21692 
Process name: w3wp.exe 
Account name: IIS APPPOOL\ASP.NET v4.0 Classic 

我使用的是标准机器密钥设置,存储在web.config中,而不是自动生成的。此外,我检查了进程ID,并检查了所有错误。

1 个答案:

答案 0 :(得分:1)

我终于解决了这个问题。发生的事情是当FormsAuthentication票证超时时,FormsAuthentication_OnAuthenticate无法设置context.User对象,如MSDN documentation中针对Authenticate事件所指定的那样:

  

如果在此期间未指定User属性的值   FormsAuthentication_OnAuthenticate事件,由。提供的标识   使用cookie或URL中的表单身份验证票证。

原因是我没有使用用户名设置ticket.Name。这是一个空字符串。因此,可能是Authenticate事件无法获取用户身份并创建FormsIdentity实例的情况。作为一种解决方案,当我更新过期的故障单时,我还要创建一个GenericIdentity对象,然后使用它来设置context.User

IIdentity identity = new GenericIdentity(username, "Forms");
context.User = new CustomPrincipal(identity);