表单身份验证和身份验证cookie不持久

时间:2011-01-18 10:19:01

标签: c# cookies login persist formsauthentication

意识到有很多与表单身份验证和Cookie持久性相关的问题,但是我花了大半天的时间来钻研,我仍然遇到了困难。

我的问题

我正在开发一个网络应用程序(VS2010,但webapp是f / w 3.5),它限制了对经过身份验证的用户访问应用程序的某些部分(而其他部分是打开的)。我的问题是我的身份验证cookie在关闭浏览器后似乎没有持久存在。

我的方法

我编写了一个简单的login.aspx页面,该页面在web.config中配置如下:

<authentication mode="Forms">

 

...并且各个页面的行为声明如下:

<location path="ClientManageAccount.aspx">
     <system.web>
        <authorization>
           <deny users="?" />
        </authorization>
     </system.web>
</location>

......除了这些饼干诡计外,各方面都运作良好......

我验证了用户的登录信息后,手动创建了身份验证Cookie。 login.aspx页面中的数据库密码(工作正常)。如果用户选中“保持登录状态”复选框,则使用以下方法生成cookie:

    private void GenerateAuthenticationCookie(int expiryInMinutes, Guid userGuid)
    {
        DateTime cookieExpiration = DateTime.Now.AddMinutes(expiryInMinutes); // change to months for production
        var authenticationTicket =
            new FormsAuthenticationTicket(
                2,
                userGuid.ToString(), 
                DateTime.Now,
                cookieExpiration,
                true, 
                string.Empty,
                FormsAuthentication.FormsCookiePath);

        // ticket must be encrypted
        string encryptedTicket = FormsAuthentication.Encrypt(authenticationTicket);

        // create cookie to contain encrypted auth ticket
        var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
        authCookie.Expires = authenticationTicket.Expiration;
        authCookie.Path = FormsAuthentication.FormsCookiePath;

        // clear out existing cookie for good measure (probably overkill) then add
        HttpContext.Current.Response.Cookies.Remove(FormsAuthentication.FormsCookieName); 
        HttpContext.Current.Response.Cookies.Add(authCookie);
    }

这里的目标是我将用户Guid存储在auth cookie中,然后我将其用于将用户对象恢复到会话中(这也是在login.aspx页面中,我的想法是我想要从我创建的auth cookie中提取用户guid,并使用它将相应的用户记录填充到会话中并重定向到所请求的页面):

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            TryAutoLogin();
        }            
    }

    private void TryAutoLogin()
    {
        HttpCookie cookie = HttpContext.Current.Request.Cookies.Get(FormsAuthentication.FormsCookieName);

        if (cookie != null)
        {
            FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);

            if (ticket != null)
            {
                if (ticket.Name.Length > 0)
                {
                    try
                    {
                        Guid userGuid = new Guid(ticket.Name);
                        KUser user = UserFunctions.GetUserFromUserGuid(userGuid);

                        if (user != null) Session["User"] = user;

                        FormsAuthentication.RedirectFromLoginPage(userGuid.ToString(), true);
                    }
                    catch (Exception anyException)
                    {
                        // don't do anything for now - do something smart later :-)                        }
                }
            }
        }
    }

最后,这是我的login.aspx页面上的登录按钮的代码:

    protected void Submit_OnClick(object sender, EventArgs e)
    {
        long userId = 0;
        UserAuthenticationStatus status;

        status = (UserAuthenticationStatus)UserFunctions.UserAuthenticates(EmailAddress.Text, Password.Text, ref userId);

        switch (status)
        {
            case UserAuthenticationStatus.Authenticated:
                //email address and password match, account ok, so log this user in
                KUser user = UserFunctions.GetUser(userId);
                Session["User"] = user;

                if (ChkRememberMe.Checked)
                {
                    GenerateAuthenticationCookie(15, user.UserGuid); // 15 minutes

                    FormsAuthentication.RedirectFromLoginPage(user.UserGuid.ToString(), true);
                }
                else
                {
                    FormsAuthentication.RedirectFromLoginPage(user.UserGuid.ToString(), false);
                }
                break;
            case UserAuthenticationStatus.AuthButLocked:
                // email/pwd match but account is locked so do something
                ShowLockedAccountMessage();
                break;
            case UserAuthenticationStatus.EmailFoundIncorrectPassword:
            case UserAuthenticationStatus.EmailNotFound:
            case UserAuthenticationStatus.Unknown:
                // either the email wasn't found, or the password was incorrect or there was some other problem
                // present message stating this and offer chance to register
                ShowFailedLoginMessage();
                break;
            default:
                ShowUnavailableMessage();
                break;
        }
    }

正如您所看到的,没有什么特别复杂的事情发生,但尽管在GenerateAuthenticationCookie(..)中创建的authCookie正确创建(据我所知)它不会持续存在。

我注意到的一件事是,如果我将一些代码放入global.asax.cs中的Application_AuthenticateRequest方法,例如:

    protected void Application_AuthenticateRequest(object sender, EventArgs e)
    {
        HttpCookie cookie = HttpContext.Current.Request.Cookies.Get(FormsAuthentication.FormsCookieName);

        if (cookie != null)
        {
            int x = 4;  // just a dummy line so that I can set a breakpoint
        }
    }

...在新的浏览器会话之后,有时会遇到断点,但是一旦我离开启动页面就停止被击中(在这种情况下,虚拟的start.aspx页面仅用于开发和测试)。 / p>

所以,为问题道歉,但我真的在这里受苦。

我检查/尝试过的事情

确保代码正在执行 - 是 浏览器设置 - 即退出时不删除cookie - 确认没有删除 尝试不同的超时 - 例如等于或不同于web.config超时,似乎没关系...... ......当然至少有二十或三十个不同的先前问题,但无济于事。

系统/开发环境

Windows 7 64位,VS2010(但proj是3.5),SQL Server 2008 Express。

在我的开发服务器上,这个问题仍然存在,所以我不确定它是否一定是环境的 - 该机器是运行SQL 2008R2的WS2008R2盒子 - 同样的问题仍然存在。

有没有人,在任何地方,对我能在这里尝试的事情有任何想法?我有一个预感我可以通过拦截global.asax.cs中的初始Application_AuthenticateRequest命中并将某些内容填充到会话状态以标记为“已验证”(以避免每次调用该方法时进行昂贵的身份验证尝试,结果发现每页几次。

提前致谢,

约翰

1 个答案:

答案 0 :(得分:3)

好吧,花了所有时间写这篇文章,我有一点清晰,并意识到(a)我不需要对Page_Load()进行任何检查(如果这样做正常)根本不会调用login.aspx页面,并且(b)我应该能够从Session_Start获取cookie - 这是我重新定位TryAutoLogin代码的地方。

这本身就是向前迈出了一步,但是尽管检索了cookie并因此检测到了用户guid,我发现我仍然被惩罚回到login.aspx页面。

在这一点上,我回想起我有一个父母版主页和两个子母版页 - 我为非认证页(例如主页)设置了一个,而另一个用于需要认证的页面。我隐约回想起会话超时的问题,并将以下代码放在OnInit覆盖中:

       if (Session["User"] == null)
       {
              FormsAuthentication.SignOut();
              FormsAuthentication.RedirectToLoginPage();
              Response.End();
       }

......本身并没有那么糟糕(并且在超时时避免了一个令人讨厌的错误)但是在start.aspx页面上,我发现了这个宝石:

Session.Clear();

...在Page_Load!

所以,发生的事情是我无意中清除了我放置了新恢复的用户记录的会话。这意味着授权母版页的OnInit覆盖然后检测到用户对象的缺失并且 - ta dah! - 将用户签名,然后删除授权cookie ...

所以,接下来要做一些布线和一些调查,我可以把它放到床上。

感谢阅读(即使我自己弄明白了)...... :)