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>
问题分析
- 登出逻辑错误:当前登出代码会删除存储"记住我"用户名的
useridCookie,导致登出后无法读取已保存的用户名。只有用户下次登录时取消勾选"记住我",才应该删除该Cookie。 - 读取Cookie目标错误:
manageCookieInfo方法尝试读取Forms认证的默认Cookie,但实际存储用户名的是自定义的useridCookie,页面加载时无法正确读取记住的用户名。 - 跳转方法冲突:
btnSignIn_Click中同时调用FormsAuthentication.RedirectFromLoginPage和Response.Redirect,后者会覆盖前者的跳转逻辑;且RedirectFromLoginPage的持久化参数设为false,未利用框架自带的持久化功能。 - 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
相关产品推荐
相关产品推荐

