You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Web Form应用:“记住我”功能实现故障求助

ASP.NET Web Form "记住我"功能失效问题排查

勾选"记住我"复选框后功能未按预期工作,登出后无法记住用户名。以下是现有实现代码:

登录页(login.aspx)前台代码

<tr>
    <td>Remember me</td>
    <td>
        <asp:CheckBox ID="chkRememberMe" runat="server" />
    </td>
    <td></td>
</tr>
<td>
    <asp:Button ID="btnSignIn" runat="server" Text="Sign In" OnClick="btnSignIn_Click" ValidationGroup="signin"/>
</td>
<td colspan="2">
    <asp:CustomValidator ID="cusValWrongLogin" runat="server" ErrorMessage="The user name or the password (or both) are incorrect."></asp:CustomValidator>
</td>
</tr>

登录页后台(login.aspx.cs)代码

private void manageCookieInfo()
{
    // Retrieve the authentication cookie
    HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
    if (authCookie != null)
    {
        // Decrypt the authentication ticket stored in the cookie
        FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);

        // Check if the authentication ticket is not expired
        if (!authTicket.Expired)
        {
            string encryptedUsername = authTicket.Name;
            string username = DecryptUsername(encryptedUsername);
            txtusername.Text = username;
            chkRememberMe.Checked = true;
        }
        else
        {
            // Uncheck the "remember me" checkbox if no cookie is found
            chkRememberMe.Checked = false; 
        }
    }
}

protected void btnSignIn_Click(object sender, EventArgs e)
{
    try
    {
        string username = txtusername.Text.ToString();
        string password = txtpassword.Text.ToString();
        string btnText = btnSignIn.Text;

        string storedPassword = (contact.Rows[0][18]).ToString();

        bool validated = VerifyHashedPassword(password, storedPassword);

        int saltLength = SaltValueSize * UnicodeEncoding.CharSize;

        // Strip the salt value off the front of the stored password.
        string saltValue = storedPassword.Substring(0, saltLength);

        string hashedPassword = HashPassword(password, saltValue);

        int userID = MyApp.DataService.Contact.SelectLogin(username, hashedPassword);

        if (!btnText.Contains("Change") && userID > 0 && validated) // authenticated
        {

            if (Request.QueryString["ReturnUrl"] == null)
            {
                Session["FromLogin"] = "Yes";
            }
            Session["User"] = username;
            Session["LoginFirst"] = "Yes";
            MyApp.DataService.Contact.UpContactupdateType(userID, 1); 

            // Storing the password in a cookie
            if (chkRememberMe != null && chkRememberMe.Checked == true)
            {
                // Timeout in minutes (30 days)
                int timeout = 43200;

                // Encrypt the username
                string encryptedUsername = EncryptUsername(username);

                var ticket = new FormsAuthenticationTicket(encryptedUsername, false, timeout);
                string encryptedTicket = FormsAuthentication.Encrypt(ticket);

                // Set the values of the custom cookies
                HttpCookie userIdCookie = new HttpCookie("userid");
                userIdCookie.Value = encryptedUsername;
                userIdCookie.Expires = DateTime.Now.AddMinutes(timeout);
                userIdCookie.HttpOnly = true;
                userIdCookie.Secure = true;
                Response.Cookies.Add(userIdCookie);

                HttpCookie pwdCookie = new HttpCookie("pwd");
                pwdCookie.Value = encryptedTicket;
                pwdCookie.Expires = DateTime.Now.AddMinutes(timeout);
                pwdCookie.HttpOnly = true;
                pwdCookie.Secure = true;
                Response.Cookies.Add(pwdCookie);
            }
            else
            {
                Response.Cookies["userid"].Expires = DateTime.Now.AddDays(-1);
                Response.Cookies["pwd"].Expires = DateTime.Now.AddDays(-1);
            }

            if (Request.QueryString["ReturnUrl"] != null)
            {
                FormsAuthentication.RedirectFromLoginPage(username, false);
            }
            else
            {
                FormsAuthentication.RedirectFromLoginPage(username, false);
                Response.Redirect("~/Portal/default.aspx", false);
            }
        }
    }
    catch (Exception ex)
    {
        Response.AppendToLog("***** Exception during login ***** " + ex.StackTrace);
    }
}

private string EncryptUsername(string username)
{
    // Encryption using Base64 encoding
    byte[] usernameBytes = Encoding.UTF8.GetBytes(username);
    string encryptedUsername = Convert.ToBase64String(usernameBytes);
    return encryptedUsername;
}

private string DecryptUsername(string encryptedUsername)
{
    // Decryption using Base64 decoding
    try
    {
        byte[] encryptedUsernameBytes = Convert.FromBase64String(encryptedUsername);
        string username = Encoding.UTF8.GetString(encryptedUsernameBytes);
        return username;
    }
    catch (FormatException)
    {
        // Handle invalid Base64 string error
        return string.Empty;
    }
}

登出页代码

public partial class Logout : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        LogoutUser();
    }
    private void LogoutUser()
    {
        // Perform logout operations
        // Sign out the user
        System.Web.Security.FormsAuthentication.SignOut();

        // Clear the authentication cookies
        if (Request.Cookies["userid"] != null)
        {
            HttpCookie useridCookie = new HttpCookie("userid");
            useridCookie.Expires = DateTime.Now.AddDays(-1);
            Response.Cookies.Add(useridCookie);
        }

        if (Request.Cookies["pwd"] != null)
        {
            HttpCookie pwdCookie = new HttpCookie("pwd");
            pwdCookie.Expires = DateTime.Now.AddDays(-1);
            Response.Cookies.Add(pwdCookie);
        }


        // Clear the session
        Session.Clear();
        Session.Abandon();

        // Redirect to the login page or any other desired page
        Response.Redirect("~/Login.aspx");
    }
}

Web.config配置

<authentication mode="Forms">
    <forms name="Cookie" loginUrl="~/Login.aspx" protection="All" timeout="20" path="/" defaultUrl="portal/default.aspx"/>
</authentication>

问题分析

  1. 登出逻辑错误:当前登出代码会删除存储"记住我"用户名的userid Cookie,导致登出后无法读取已保存的用户名。只有用户下次登录时取消勾选"记住我",才应该删除该Cookie。
  2. 读取Cookie目标错误:manageCookieInfo方法尝试读取Forms认证的默认Cookie,但实际存储用户名的是自定义的userid Cookie,页面加载时无法正确读取记住的用户名。
  3. 跳转方法冲突:btnSignIn_Click中同时调用FormsAuthentication.RedirectFromLoginPage和Response.Redirect,后者会覆盖前者的跳转逻辑;且RedirectFromLoginPage的持久化参数设为false,未利用框架自带的持久化功能。
  4. Secure属性限制:如果站点未使用HTTPS协议,Secure = true会导致Cookie无法被浏览器保存,因为该属性要求Cookie仅通过HTTPS传输。

修复方案

1. 修正manageCookieInfo方法,读取正确的Cookie

private void manageCookieInfo()
{
    // 读取自定义的userid Cookie
    HttpCookie useridCookie = Request.Cookies["userid"];
    if (useridCookie != null && !string.IsNullOrEmpty(useridCookie.Value))
    {
        try
        {
            string username = DecryptUsername(useridCookie.Value);
            if (!string.IsNullOrEmpty(username))
            {
                txtusername.Text = username;
                chkRememberMe.Checked = true;
            }
        }
        catch
        {
            // 解密失败则清除无效Cookie
            useridCookie.Expires = DateTime.Now.AddDays(-1);
            Response.Cookies.Add(useridCookie);
            chkRememberMe.Checked = false;
        }
    }
    else
    {
        chkRememberMe.Checked = false;
    }
}

2. 修正登出逻辑,保留"记住我"Cookie

private void LogoutUser()
{
    // 仅登出Forms认证并清除会话
    System.Web.Security.FormsAuthentication.SignOut();
    Session.Clear();
    Session.Abandon();

    // 清除Forms认证Cookie(SignOut已处理,此处为冗余保险)
    HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
    if (authCookie != null)
    {
        authCookie.Expires = DateTime.Now.AddDays(-1);
        Response.Cookies.Add(authCookie);
    }

    // 保留userid和pwd Cookie,仅在用户取消记住我时删除
    Response.Redirect("~/Login.aspx");
}

3. 简化登录逻辑(可选,更规范)

去掉自定义Cookie,直接利用FormsAuthentication的内置持久化功能:

protected void btnSignIn_Click(object sender, EventArgs e)
{
    try
    {
        string username = txtusername.Text.ToString();
        string password = txtpassword.Text.ToString();
        string btnText = btnSignIn.Text;

        string storedPassword = (contact.Rows[0][18]).ToString();
        bool validated = VerifyHashedPassword(password, storedPassword);
        int saltLength = SaltValueSize * UnicodeEncoding.CharSize;
        string saltValue = storedPassword.Substring(0, saltLength);
        string hashedPassword = HashPassword(password, saltValue);
        int userID = MyApp.DataService.Contact.SelectLogin(username, hashedPassword);

        if (!btnText.Contains("Change") && userID > 0 && validated)
        {
            if (Request.QueryString["ReturnUrl"] == null)
            {
                Session["FromLogin"] = "Yes";
            }
            Session["User"] = username;
            Session["LoginFirst"] = "Yes";
            MyApp.DataService.Contact.UpContactupdateType(userID, 1); 

            // 用复选框状态控制是否持久化Cookie
            bool isPersistent = chkRememberMe?.Checked ?? false;
            if (Request.QueryString["ReturnUrl"] != null)
            {
                FormsAuthentication.RedirectFromLoginPage(username, isPersistent);
            }
            else
            {
                FormsAuthentication.SetAuthCookie(username, isPersistent);
                Response.Redirect("~/Portal/default.aspx", false);
            }
        }
    }
    catch (Exception ex)
    {
        Response.AppendToLog("***** Exception during login ***** " + ex.StackTrace);
    }
}

对应修改manageCookieInfo方法:

private void manageCookieInfo()
{
    HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
    if (authCookie != null)
    {
        try
        {
            FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
            if (!authTicket.Expired)
            {
                txtusername.Text = authTicket.Name;
                chkRememberMe.Checked = true;
            }
        }
        catch
        {
            chkRememberMe.Checked = false;
        }
    }
    else
    {
        chkRememberMe.Checked = false;
    }
}

4. 调整Cookie的Secure属性(非HTTPS站点)

如果站点使用HTTP协议,将Secure属性改为动态判断:

userIdCookie.Secure = Request.IsSecureConnection;
pwdCookie.Secure = Request.IsSecureConnection;

内容的提问来源于stack exchange,提问作者Snooper_A

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 00:13:11