ASP.NET窗体身份验证超时 [英] ASP.NET Forms Authentication timeout

查看:190
本文介绍了ASP.NET窗体身份验证超时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这可能是一个非常简单的问题,但经过几个小时试图理解它是如何工作在ASP.NET 4.0中我还是不知道。

This could be a very simple question, but after a few hours trying to understand how this works on ASP.NET 4.0 I still don't know.

我使用窗体身份验证。我上有一个登录控件的登录页面。

I'm using Forms Authentication. I have a login page with a login control on it.

这是我需要什么,当用户登录:

This is what I need when users login:

A-用户应该保持登录,直到没有做任何事情的超时设置。如果他们重新加载页面的超时重启倒计时。

A- The users should stay logged until the don't do anything for the timeout set. If they reload a page the timeout has to restart the countdown.

B-如果他们点击记住我检查它们应该直到他们退出保​​持联系,不管他们关闭浏览器或重新启动计算机。

B- If they click the "Remember Me" check they should stay connected until they logout, no matter if they close the browser or reboot the computer.

我的问题是,当他们登录我没有看到我的电脑上任何Cookie:

The problem I have is when they login I don't see any cookie on my computer:


  1. 哪里饼干吗?是内存cookie?

  2. 如果会话过期,会发生什么?我想,让他们记录的,除非超时完成。

  3. 会发生什么,如果应用程序池被回收?

我也有一个问题:当他们点击记住我复选框(案例B)我想他们登录,直到他们点击注销按钮。这一次,我看到一个cookie,但看起来他们仅保持为连接超时...所以有什么rememeber我还是不...

Also I have another problem: when they click the "remember me" check (case B) I'd like them logged until they click on the logout button. This time I do see a cookie, but it looks like they stay connected only for the timeout...so what is the difference between the rememeber me or not...

我想分开完全认证和会话。我想通过身份验证控制的cookie,如果不是非常不好接近。

I'd like to separate completely Authentication and Session. I'd like Authentication controlled by cookies if is not very bad approaching.

感谢您的帮助。 -

Thanks for helping-.

推荐答案

处理非永久,滑动过期门票

窗体身份验证使用内存中的cookie的票,除非你让它持续性(例如, FormsAuthentication.SetAuthCookie(用户名,真实)将使其持久) 。默认情况下,门票采用了滑动过期。每一个请求被处理时,车票将与新的截止日期被送到了下来。一旦该日到期,饼干和机票都无效,用户将被重定向到登录页面。

Forms Authentication uses an in-memory cookie for the ticket, unless you make it persistent (for example, FormsAuthentication.SetAuthCookie(username, true) will make it persistent). By default, the ticket uses a sliding expiration. Each time a request is processed, the ticket will be sent down with a new expiration date. Once that date expires, the cookie and the ticket are both invalid and the user will be redirected to the login page.

表单验证没有内置在处理重定向那些已经被渲染坐超过超时长的页面。你需要把它添加自己。最简单地说,你需要开始与文档加载定时器,使用JavaScript。

Forms Authentication has no built-in handling for redirecting pages that have already been rendered, that sit longer than the timeout. You will need to add this yourself. At the simplest level, you will need to start a timer with the document loads, using 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票应该已经过期,所以你不应该做的事情。

With the above, if your page is not refreshed or changed, or redirectTimeoutHandle is not otherwise cancelled (with clearTimeout(redirectTimeoutHandle);), it will be redirected to the login page. The FormsAuth ticket should have already expired so you shouldn't have to do anything with that.

这里的技巧是你的网站是否确实没有AJAX的工作,或者你考虑其他客户端事件作为活动的用户活动(移动或点击鼠标等)。你将不得不手动跟踪这些事件,并在它们出现时,重置 redirectTimeoutHandle 。例如,我有一个使用AJAX严重,所以页面没有物理刷新往往一个网站。由于我使用jQuery,我可以有它的每一个AJAX请求发出的时间,这应该,实际上,导致页面,如果他们坐在一个页面上,并没有做任何更新被重定向复位超时。

The trick here is whether or not your site does AJAX work, or you consider other client-side events as active user activity (moving or clicking the mouse, etc). You will have to track those events manually and when they occur, reset the redirectTimeoutHandle. For example, I have a site that uses AJAX heavily, so the page doesn't physically refresh often. Since I use jQuery, I can have it reset the timeout every time an AJAX request is issued, which should, in effect, result in the page being redirected if they sit on a single page and don't do any updates.

下面是一个完整的初始化脚本。

Here's a complete initialization script.

$(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的形式)都将被更新,用户应该罚款。

By simply sending an AJAX request, the client-side timeout and the ticket (in the form of a cookie) will both be updated, and your user should be fine.

然而,如果用户的活动不会导致FormsAuth票被更新时,用户将出现它们要求新的页面的下一次要记录的输出(或者通过导航或通过AJAX)。在这种情况下,你会当用户活动与AJAX调用,也就是说,一个自定义处理程序,一个MVC动作等,让您的FormsAuth票最新发生需要平您的Web应用程序。请注意,您需要ping服务器时,以跟上最新的,因为你不希望产生大量的请求,因为他们,说,移动光标或点击的东西服务器要小心。下面是一个除了上面,增加了 resetRedirect 来对文档的鼠标点击,除了初始页面加载和AJAX请求初始化脚本。

However, if user activity does not cause the FormsAuth ticket to be updated, the user will appear to be logged out the next time they request a new page (either by navigating or via AJAX). In that case, you'll need to "ping" your web application when user activity occurs with an AJAX call to, say, a custom handler, an MVC action, etc. to keep your FormsAuth ticket up to date. Please note that you need to be careful when pinging the server to keep up-to-date, as you don't want to flood the server with requests as they, say, move the cursor around or click on things. Here's an addition to the init script above that adds resetRedirect to mouse clicks on the document, in addition to the initial page load and AJAX requests.

$(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,以任意长的超时。你应该能够离开客户端code和web.config文件,因为他们,但在你的登录逻辑分开处理用户的preference一个永久性的票。在这里,你将需要修改的车票。下面是逻辑在登录页面做这样的事情:

You need the ticket to be sent to the client as a persistent cookie, with an arbitrarily long timeout. You should be able to leave the client code and web.config as they are, but handle the user's preference for a permanent ticket separately in your login logic. Here, you'll need to modify the ticket. Below is logic in a login page to do such a thing:

// 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的角色,这样你就不必再找一找。是的,这是可能的,但也有一些注意事项。

You asked if you can store roles in the ticket/cookie so you don't have to look them up again. Yes, that is possible, but there are some considerations.


  1. 您应该限制数据,你把票量,因为饼干只能是如此之大,

  2. 您应该考虑是否应该角色在客户端进行缓存。

要详细说明2:

您不应该说你接收来自用户的绝对信任索赔。例如,如果用户登录,是管理员,并检查记住我,从而获得一个持久的,长期的票,他们将永远是管理员(或直到该cookie到期或擦除)。如果有人在你的数据库中的角色删除它们,应用程序将仍然认为他们是一个联系,如果他们有旧票。因此,你可能会更好获得用户的角色的每一次,但缓存在应用实例中的角色的一段时间,以减少数据库的工作。

You shouldn't implicitly trust claims that you receive from a user. For example, if a user logs in and is an Admin, and checks "remember me" thus receiving a persistent, long-term ticket, they will be an Admin forever (or until that cookie expires or is erased). If someone removes them from that role in your database, the application will still think they are an Admin if they have the old ticket. So, you may be better off getting the user's roles every time, but caching the roles in the application instance for a period of time to minimize database work.

从技术上讲,这也是为票券本身的问题。同样,你不应该相信,仅仅是因为他们有一个有效的车票该帐户仍然有效。您可以使用类似的逻辑作为角色:检查受票引用的用户仍然存在,并且是有效的(它没有被锁定,禁用或删除)通过查询你的实际的数据库,只是缓存数据库结果一段时间,以提高性能。这是我在我的应用程序,这里的门票被视为身份声明做的(类似,用户名/密码是另一种类型的索赔)。这是在简化的global.asax.cs逻辑(或在HTTP模块):

Technically, this is also an issue for the ticket itself. Again, you shouldn't trust that just because they have a valid ticket that the account is still valid. You can use similar logic as the roles: Check that the user referenced by the ticket still exists and is valid (that it's not locked out, disabled, or deleted) by querying your actual database, and just caching the db results for a period of time to improve performance. This is what I do in my applications, where the ticket is treated as an identity claim (similarly, username/password is another type of claim). Here is simplified logic in the global.asax.cs (or in an HTTP module):

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;
}

当然,你可以决定你不关心任何的,只是要信任票,并存储在票中的作用也是如此。首先,我们从上面更新您的登录逻辑:

Of course, you may decide you don't care about any of that and just want to trust the ticket, and store the roles in the ticket as well. First, we update your login logic from above:

// 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));
}

Add方法:

   private string GetUserRolesString(string userName)
   {
        // TODO: get roles from db and concatenate into string
   }

更新您的global.asax.cs让角色出票和更新HttpContext.User中的:

Update your global.asax.cs to get roles out of ticket and update 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);
  }
}

这篇关于ASP.NET窗体身份验证超时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆