C# .NET中的Cookie和会话过期
我有一个MVC4单应用程序页面。在登录页面中有3个字段:用户、密码和“记住我”复选框。 C#登录代码如下:C# .NET中的Cookie和会话过期,c#,asp.net-mvc-4,session,cookies,single-page-application,C#,Asp.net Mvc 4,Session,Cookies,Single Page Application,我有一个MVC4单应用程序页面。在登录页面中有3个字段:用户、密码和“记住我”复选框。 C#登录代码如下: if (WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe)) { FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe); return Json(new {
if (WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
return Json(new { success = true, redirect = returnUrl });
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
我想这样做:
- 如果用户使用“记住我”true登录,cookie将一直保留,直到用户注销
- 如果用户使用“记住我”false登录-cookie将在3小时后过期
<authentication mode="Forms">
<forms loginUrl="~/" timeout="180" cookieless="UseCookies" slidingExpiration="false" />
</authentication>
<sessionState timeout="180" />
问题是当用户在短时间内(通常为10-30分钟)不触摸页面时,然后用户尝试在页面中执行某些操作-出现错误
“此请求的授权已被拒绝。”
(虽然sessionStation
的时间超过30分钟!)
用户刷新页面后-如果cookie尚未过期-一切正常。但我当然不想让用户每隔15分钟左右刷新一次页面,这是一个单页应用程序
因此,我尝试将slidingExpiration
更改为“true”,并每17分钟创建一次ping(使用ajax),它确实停止了会话过期和令人讨厌的消息-但cookie在3小时后没有过期(我使用“记住我”false登录)
我能做什么?我会仔细检查您的IIS设置。查看应用程序池空闲超时值的设置。默认为20分钟。当应用程序池闲置20分钟(没有用户访问应用程序池)时,您将失去会话状态(会话中存储的所有数据将被清除) 如果有更多的用户,这个问题会自行解决,但假设您是唯一的测试人员,将此值增加到180分钟以上将阻止超时,或者您可以将该值设置为零以完全禁用应用程序池空闲超时 有关在IIS Express中检查应用程序池超时的信息,请参阅此答案 请注意,死掉的应用程序池可能需要几秒钟才能重新生成。无论如何,增加该值可能是有益的。这将防止用户在不幸的情况下不得不等待应用程序池重新启动时体验极其缓慢 更新 要允许未单击“记住我”的用户更改超时,您可以创建一个自定义属性,也可以通过C#修改表单身份验证超时。下面是关于通过代码设置超时的好参考。如果您希望超时正好在3小时到期,即使用户有活动,也要确保web配置中的slidingExpiration为false。如果您希望超时在用户最后一次活动3小时后过期,请确保slidingExpiration为true。因此,在设置cookie之前,请尝试以下操作(确保检查OpenWebConfiguration路径):
右键单击IIS管理控制台中的应用程序池并查找“空闲超时(分钟)”
您可以将设置调整为0(零),这将有效地禁用超时,以便应用程序池永远不会因为空闲而关闭。我通过创建自己的加密cookie来解决这个问题,该cookie将保留用户的会话(如果存在)。在操作筛选器属性中,检查用户的会话是否已过期,检查此cookie。如果cookie存在,请验证信息并重新建立会话。如果用户没有会话、没有cookie或加密的凭据不正确,请将用户重定向到登录页面。由于所有内容都在代码中处理,因此在服务器设置上没有猜测工作 选中“记住我”登录时:
if (RememberMe ?? false)
{
var authCookie = new HttpCookie(Config.AuthorizationCookie);
authCookie.Values.Add("ID", Crypto.EncryptStringAES(UserSession.Current.Account.ID.ToString(), Config.SharedSecret));
authCookie.Expires = DateTime.Now.AddDays(30);
AuthorizationCookie = authCookie;
}
Response.AppendCookie(AuthorizationCookie);
不带会话的页面访问(在附加到必要控制器和操作的属性内完成):
必要时重新建立会话(在数据库连接内完成)
最后恢复会话:
if (_account != null)
{
// set the current account
UserSession.Current.Account = _account;
if(_sessionRestored)
{
UserSession.Current.Refresh();
}
}
您的代码可能看起来有点不同,但这是我如何操作的关键。我能看到的唯一问题是,如果您在会话中存储了一些数据,它将在20分钟后被清除。听起来不像是OPS的案例,但确实想考虑这个可能性。NathReNeCo好点,我不想在这个项目上跨任何时候坚持任何事情。将这些信息存储在数据库中或使用cookie似乎是一个不错的方法。我想这可能是OP想要的“最终恢复会话”的位置和时间?我不知道具体要做什么,visual studio找不到“Config”和“UserSession”,例如Config和UserSession只是我为运行项目而创建的类。听起来你想要一些开箱即用的东西,我建议的是更多的定制解决方案。好的,它可以解决20分钟到期的问题,但我能在记住我是真(将永远保持不变)和记住我是假(只停留3小时)之间做出不同吗?你可以把逻辑放在cookies中,由于我使用了“[Authorize(Roles=“Role1”)]”属性——我希望使用原始cookie,而不是处理另一个cookie OK,它可以解决20分钟过期的问题,而无需ping,但我可以在记住我为真(这将“永远”)和记住我为假之间做出不同吗(仅停留3小时)?你可能需要一个代码实现。我会说提高你的应用程序池空闲超时,因为这可能是一个好主意。然后使用“操作筛选器”属性检查“记住我”假用户的登录时间是否超过3小时。如果是,请将其启动,如果不是,让他们继续。执行检查的一种方法是存储登录信息n cookie中的时间,然后在属性中验证未超过三个小时。为答案添加了另一个备选答案,请参见上文。是否有方法使“rememberMe=true”的SlidingExpiration为true,使“rememberMe=false”的SlidingExpiration为false?这将解决一个问题
_account = UserSession.Current.Account;
// check if there is currently an account in the session
if(_account == null)
{
// check the user authorization cookie for a user id
HttpCookie authCookie = HttpContext.Current.Request.Cookies[Config.AuthorizationCookie] ?? new HttpCookie(Config.AuthorizationCookie);
if (authCookie.HasKeys && !String.IsNullOrEmpty(authCookie["ID"]))
{
// decrypt the user id for database lookup
_userID = Crypto.DecryptStringAES(authCookie.Values["ID"], Config.SharedSecret);
}
}
// called within a database connection's using statement
protected override void Query(DatabaseContext db)
{
// check the database for the user account
if(_account == null && !String.IsNullOrEmpty(_userID))
{
int tempID;
int? id;
id = int.TryParse(_userID, out tempID) ? tempID : (int?)null;
if(id.HasValue)
{
_sessionRestored = true;
_account = db.User.Find(id);
if(_account != null)
{
_account.LastLogon = DateTime.UtcNow;
db.SaveChanges();
}
}
}
}
if (_account != null)
{
// set the current account
UserSession.Current.Account = _account;
if(_sessionRestored)
{
UserSession.Current.Refresh();
}
}