这可能是一个很简单的问题,但几个小时后试图了解它是如何工作的ASP.NET 4.0我仍然不知道。
我使用窗体身份验证。 我上有一个登录控件的登录页面。
这正是我需要的,当用户登录:
A-用户应该保持登录状态,直到什么都不做的超时设置。 如果他们重新加载页面超时必须重新开始倒计时。
B-如果他们点击了“记住我”检查,直到他们退出,他们应该保持联系,不管他们关闭浏览器或重新启动计算机。
我的问题是,当他们登录我没有看到我的计算机上的任何的cookie:
- 哪里饼干吗? 是内存cookie?
- 如果会话过期,会发生什么? 我想,让他们登录,除非超时完成。
- 如果应用程序池被回收,会发生什么?
我也有一个问题:当他们点击“记住我”复选框(案例B)我想他们登录,直到他们点击退出按钮。 这一次,我看到一个cookie,但看起来他们仅保持为连接超时...所以有什么rememeber我还是不之间的区别...
我想完全认证和会话分开。 我想通过身份验证控制的cookie,如果不是非常不好接近。
感谢helping-。
处理非永久性的滑动过期门票
窗体身份验证使用内存中的cookie的票,除非你把它持久的(例如, FormsAuthentication.SetAuthCookie(username, true)
将使其持久)。 默认情况下,该票采用的是侧滑到期。 每一个请求被处理时,票证将与新的截止日期被向下发送。 一旦该日到期,该cookie和票据都无效,用户将被重定向到登录页面。
窗体身份验证没有内置在处理重定向已经被渲染坐的时间长于超时页面。 您将需要这个自己添加。 最简单地说,你将需要与文档加载启动一个定时器,使用JavaScript。
<script type="text/javascript">
var redirectTimeout = <%FormsAuthentication.Timeout.TotalMilliseconds%>
var redirectTimeoutHandle = setTimeout(function() { window.location.href = '<%FormsAuthentication.LoginUrl%>'; }, redirectTimeout);
</script>
通过上述,如果您的网页不刷新或改变,或redirectTimeoutHandle
不另行予以取消(用clearTimeout(redirectTimeoutHandle);
),它会被重定向到登录页面。 该FormsAuth票应该已经过期,所以你不应该做的事情。
这里的技巧是你的网站是否确实AJAX的工作,或者你考虑其他客户端事件作为活动的用户活动(移动或点击鼠标等)。 你将不得不手动跟踪这些事件和它们发生时,重置redirectTimeoutHandle
。 例如,我有一个使用AJAX严重,所以页面物理上不经常更新网站。 由于我使用jQuery,我可以有它的每一个AJAX请求发出的时间,这应该,实际上,导致页面被重定向,如果他们坐在一个单页上,不要做任何更新复位超时。
这里有一个完整的初始化脚本。
$(function() {
var _redirectTimeout = 30*1000; // thirty minute timeout
var _redirectUrl = '/Accounts/Login'; // login URL
var _redirectHandle = null;
function resetRedirect() {
if (_redirectHandle) clearTimeout(_redirectHandle);
_redirectHandle = setTimeout(function() { window.location.href = _redirectUrl; }, _redirectTimeout);
}
$.ajaxSetup({complete: function() { resetRedirect(); } }); // reset idle redirect when an AJAX request completes
resetRedirect(); // start idle redirect timer initially.
});
只需发送一个AJAX请求,客户端超时和票(在cookie的形式)都将被更新,用户应该罚款。
但是,如果用户活动不会导致FormsAuth票进行更新时,用户会出现他们要求新的一页下一次被注销(无论是通过浏览或通过AJAX)。 在这种情况下,你会当用户活动与AJAX调用,比如,一个定制的处理器,一个MVC动作等,让您的FormsAuth票最新出现需要“平”的Web应用程序。 请注意,您需要ping服务器时,以跟上最新的,因为你不想与洪水的要求,因为他们,比如说,移动光标或点击的东西服务器要小心。 下面是一个除了初始化脚本以上,增加了resetRedirect
鼠标点击的文件,除了初始页面加载和AJAX请求。
$(function() {
$(document).on('click', function() {
$.ajax({url: '/ping.ashx', cache: false, type: 'GET' }); // because of the $.ajaxSetup above, this call should result in the FormsAuth ticket being updated, as well as the client redirect handle.
});
});
处理“永久”门票
你需要的票被发送到客户端作为一个永久性的Cookie,以一个任意长的超时。 你应该能够在你的登录逻辑单独离开客户端代码和web.config文件,因为他们,但处理用户的偏爱永久票。 在这里,你将需要修改的车票。 下面是在登录页面做这样的事情的逻辑:
// assumes we have already successfully authenticated
if (rememberMe)
{
var ticket = new FormsAuthenticationTicket(2, userName, DateTime.Now, DateTime.Now.AddYears(50), true,
string.Empty, FormsAuthentication.FormsCookiePath);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket))
{
Domain = FormsAuthentication.CookieDomain,
Expires = DateTime.Now.AddYears(50),
HttpOnly = true,
Secure = FormsAuthentication.RequireSSL,
Path = FormsAuthentication.FormsCookiePath
};
Response.Cookies.Add(cookie);
Response.Redirect(FormsAuthentication.GetRedirectUrl(userName, true));
}
else
{
FormsAuthentication.RedirectFromLoginPage(userName, false);
}
奖励:在票务存储角色
你问,如果你可以存储在票/ cookie的角色,这样你就不必再找一找。 是的,这是可能的,但也有一些注意事项。
- 您应该限制数据的你把票量,因为饼干只能是如此之大
- 你应该考虑是不是应该的角色在客户端缓存。
为了详细说明2:
你不应该含蓄地相信你接收来自用户的要求。 例如,如果用户登录并为管理员,并检查“记住我”,从而接受一个持久的,长期的票,他们将永远的管理员(或者直到cookie过期或者被删除)。 如果有人在你的数据库角色删除它们,应用程序将仍然认为他们是一个联系,如果他们有旧票。 所以,你可能会更好获得用户的角色每一次,但在高速缓存应用程序实例的角色一段时间,以尽量减少数据库的工作。
从技术上讲,这也是该票本身的问题。 同样,你不应该相信,仅仅是因为他们有一个有效的票证的帐户仍然有效。 您可以使用类似的逻辑作为角色:检查由票据引用的用户仍然存在,并且是有效的(它没有被锁定,禁用或删除)通过查询你的实际的数据库,只是缓存数据库结果一段时间以提高性能。 这是我在我的应用程序,这里的门票将被视为身份的要求做(同样,用户名/密码是另一种类型的索赔)。 这里是在的global.asax.cs简化逻辑(或在HTTP模块):
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
var application = (HttpApplication)sender;
var context = application.Context;
EnsureContextUser(context);
}
private void EnsureContextUser(HttpContext context)
{
var unauthorizedUser = new GenericPrincipal(new GenericIdentity(string.Empty, string.Empty), new string[0]);
var user = context.User;
if (user != null && user.Identity.IsAuthenticated && user.Identity is FormsIdentity)
{
var ticket = ((FormsIdentity)user.Identity).Ticket;
context.User = IsUserStillActive(context, ticket.Name) ? new GenericPrincipal(user.Identity, GetRolesForUser(context, ticket.Name)) : unauthorizedUser;
return;
}
context.User = unauthorizedUser;
}
private bool IsUserStillActive(HttpContext context, string username)
{
var cacheKey = "IsActiveFor" + username;
var isActive = context.Cache[cacheKey] as bool?
if (!isActive.HasValue)
{
// TODO: look up account status from database
// isActive = ???
context.Cache[cacheKey] = isActive;
}
return isActive.GetValueOrDefault();
}
private string[] GetRolesForUser(HttpContext context, string username)
{
var cacheKey = "RolesFor" + username;
var roles = context.Cache[cacheKey] as string[];
if (roles == null)
{
// TODO: lookup roles from database
// roles = ???
context.Cache[cacheKey] = roles;
}
return roles;
}
当然,你可以决定你不关心任何的是,只是要信任票,并储存在票证上的角色也是如此。 首先,我们从上面的更新您的登录逻辑:
// assumes we have already successfully authenticated
if (rememberMe)
{
var ticket = new FormsAuthenticationTicket(2, userName, DateTime.Now, DateTime.Now.AddYears(50), true, GetUserRolesString(), FormsAuthentication.FormsCookiePath);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket))
{
Domain = FormsAuthentication.CookieDomain,
Expires = DateTime.Now.AddYears(50),
HttpOnly = true,
Secure = FormsAuthentication.RequireSSL,
Path = FormsAuthentication.FormsCookiePath
};
Response.Cookies.Add(cookie);
Response.Redirect(FormsAuthentication.GetRedirectUrl(userName, true));
}
else
{
var ticket = new FormsAuthenticationTicket(2, userName, DateTime.Now, DateTime.Now.AddMinutes(FormsAuthentication.Timeout), false, GetUserRolesString(), FormsAuthentication.FormsCookieName);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket))
{
Domain = FormsAuthentication.CookieDomain,
HttpOnly = true,
Secure = FormsAuthentication.RequireSSL,
Path = FormsAuthentication.FormsCookiePath
};
Response.Cookies.Add(cookie);
Response.Redirect(FormsAuthentication.GetRedirectUrl(userName, false));
}
添加方法:
private string GetUserRolesString(string userName)
{
// TODO: get roles from db and concatenate into string
}
更新您的global.asax.cs得到角色了机票和更新HttpContext.User中的:
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
var application = (HttpApplication)sender;
var context = application.Context;
if (context.User != null && context.User.Identity.IsAuthenticated && context.User.Identity is FormsIdentity)
{
var roles = ((FormsIdentity)context.User.Identity).Ticket.Data.Split(",");
context.User = new GenericPrincipal(context.User.Identity, roles);
}
}
对于你将要设置的会话超时变量,但是长你想要保持登录状态的用户超时属性指定分配给会话对象的应用程序,以分钟为单位的超时时间。 如果用户不刷新或超时期限内请求一个页面,会话结束。
对于B部分,我建议存储在一个会话变量值(或Cookie,但不驻留在服务器上),并检查在Global.asax文件中Session_End中事件的价值。 如果它被设置,然后更新会话。
当浏览器关闭时,Session_End中的事件不火,它触发时,服务器没有得到来自用户在特定时间段(默认为20分钟)的请求。