A-用户应该保持记录,直到超时设置不做任何事情.如果他们重新加载页面,超时必须重新启动倒计时.
B-如果他们点击“记住我”检查,他们应该保持连接,直到他们注销,无论他们关闭浏览器还是重新启动计算机.
我遇到的问题是登录时,我的电脑上没有看到任何cookie:
> Cookie在哪里?是内存cookie吗?
>会话过期会怎么样?我想保持他们记录,除非超时完成.
>如果应用程序池被回收,会发生什么?
另外我有另一个问题:当他们点击“记住我”检查(案例B),我希望他们记录,直到他们点击注销按钮.这一次我看到一个cookie,但是看起来他们只是为了超时而保持联系…所以Rememeber我有什么区别?
我想完全分离身份验证和会话.我想要通过cookie控制的身份验证,如果接近不是很差.
谢谢你的帮助.
解决方法
表单身份验证使用内存中的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操作等)时,您需要“ping”您的Web应用程序,以使您的FormsAuth票证保持最新.请注意,在ping服务器以保持最新时,您需要小心,因为您不希望洪泛服务器的请求,例如,移动光标或点击事物.除了初始页面加载和AJAX请求之外,还有一个除了初始化脚本之外的添加,将resetRedirect添加到文档上的鼠标点击.
$(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中存储角色,所以你不必再看看.是的,这是可能的,但有一些考虑.
>您应该限制您在票中放入的数据量,因为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,GetUserRolesString(),true)); } else { var ticket = new FormsAuthenticationTicket(2,DateTime.Now.AddMinutes(FormsAuthentication.Timeout),false,FormsAuthentication.FormsCookieName); var cookie = new HttpCookie(FormsAuthentication.FormsCookieName,FormsAuthentication.Encrypt(ticket)) { Domain = FormsAuthentication.CookieDomain,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); } }