意识到有很多与表单身份验证和 cookie 的持久性有关的问题,但是花了一天的大部分时间钻研,我仍然遇到困难。
我的问题
我正在开发一个 Web 应用程序(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>
...除了这些饼干恶作剧之外,在各个方面都很好用...
一旦我在 login.aspx 页面中针对数据库(工作正常)对用户的登录名和密码进行了身份验证,我就会手动创建身份验证 cookie。如果用户选中“保持登录”复选框,则使用以下方法生成 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 页面)它就会停止命中。
所以,为这个冗长的问题道歉,但我真的在这里受苦。
我检查过/尝试过的事情
确保代码正在执行 - 是 浏览器设置 - 即退出时不删除 cookie - 确认不删除 尝试不同的超时 - 例如与 web.config 超时相同或不同,似乎并不重要......当然至少有二十或三十个不同的先前问题,但无济于事。
系统/开发环境
Windows 7 64 位,VS2010(但 proj 是 3.5),SQL Server 2008 Express。
在我的开发服务器上,这个问题仍然存在,所以我不确定它是否一定是环境问题——那台机器是运行 SQL 2008R2 的 WS2008R2 机器——同样的问题仍然存在。
有没有人在任何地方对我可以在这里尝试的事情有任何想法?我有一种预感,我可以通过拦截 global.asax.cs 中的初始 Application_AuthenticateRequest 命中并将某些内容填充到会话状态中以标记为“已验证”(以避免每次调用该方法时进行昂贵的身份验证尝试,结果是每页要多次。
提前致谢,
约翰