意识到有很多与表单身份验证和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命中并将某些内容填充到会话状态以标记为“已验证”(以避免每次调用该方法时进行昂贵的身份验证尝试,结果发现每页几次。
提前致谢,
约翰
答案 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 ...
所以,接下来要做一些布线和一些调查,我可以把它放到床上。
感谢阅读(即使我自己弄明白了)...... :)