可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have an asp.net website that uses forms authentication. There are a few things I keep in sessions like username, userID, email, etc.
I allow the user to stay logged into the website by setting a long expiration date on the authentication cookie. So it's very common for the session to expire while the user is still authenticated.
The problem I am running into is that sometimes the user's session times out but they're still authenticated. So for example, one of my user pages (which requires authentication) will say "Welcome Mike" when their session is active but once it expires it will say "Welcome [blank]" because the info is no longer in the session, yet they are still authenticated.
What's the best way to handle this? Should I resync the session info when the info is no longer there? Or should I move the user info (username, userID, email) into cookies and not worry about session timeouts?
I do not want to set the session length to something like 60 minutes or more. What i want is for my users to be able to login once and not worry about having to login again until they explicitly logout.
回答1:
Avoid using session as much as you can, if you can get away without seesion it makes multi-server deployments qutie a bit easier. Probably, Name and email are easy candidates for cookies. It's easy to fake a cookie, so userID may not be a good idea depending on your security needs.
The forms authentication cookies are encrypted and you can add extra data to those cookies (See details below). It's probably hackable but not nearly as easily as a simple cookie.
Here is the code I have used in the past slightly modified to remove some project specific details. Call this in the LoggedIn event of the login control.
void AddUserIDToAuthCookie(string userID) { //There is no way to directly set the userdata portion of a FormAuthenticationTicket //without re-writing the login portion of the Login control // //I find it easier to pull the cookie that the Login control inserted out //and create a new cookie with the userdata set HttpCookie authCookie = Response.Cookies[AUTH_COOKIE]; if(authCookie == null) { return; } Response.Cookies.Remove(AUTH_COOKIE); FormsAuthenticationTicket oldTicket = FormsAuthentication.Decrypt(authCookie.Value); var newTicket = new FormsAuthenticationTicket(oldTicket.Version, oldTicket.Name, oldTicket.IssueDate, oldTicket.Expiration, oldTicket.IsPersistent, userID, oldTicket.CookiePath); authCookie.Value = FormsAuthentication.Encrypt(newTicket); Response.Cookies.Add(authCookie); }
FYI, I copied this from an old project and edited it here to remove some project specific bits, so it may not compile, but it'll be very close.
To get the ID in your webpage...
FormsAuthenticationTicket ticket = ((FormsIdentity) Page.User.Identity).Ticket; string id = ticket.UserData;
I used this mechanism to store an id that was not part of the aspnetdb user data. If all your identity data is handled by the aspnetdb, you may only need to access the Page.User.Identity object.
回答2:
Personally, I would keep the 20 minute default and add a "keep alive" functionality to your site. Make a simple javascript that polls, say heartbeat.aspx, every 5 minutes to keep the session alive. This will extend the session and authentication without keeping crazy authentication tokens.
There are a few examples (bad in my opinion) of how to do this. I ended up using something based on AjaxLines's session timeout prevention. Instead of using the ajax library, though, I simply used an xhtml request directly. Nothing is really needed more than a timed javascript call to a GET
on the heartbeat page.
回答3:
Technically due to asp.net session timeout your user should not be logged out. This should/is controlled by the forms authentication cookie.
All the essential information related to user authentication should be kept in the forms authentication ticket's USERDATA property. This value should not be kept in session as session.
Only keep those value in session which is recreatable.
So, when you create forms authetication cookie you can pass all the essential information as part of the ticket.
The quick thing you can do is to keep the timeout of both session and forms authentication same. This will be a good practice to do.
The session timeout is automatically extended with each request to the site.
Whereas a Forms authentication extends its time only after 50% of the time has elapsed.
Here's a detailed info on this.. Forms Authentication FAQ
The HttpModule is a good place to do though. In this way your timeout will always be in sync, though there may be some slight gap.
Also keep the timeout of both session and forms authentication same. This will be a good practice to do.
The session timeout is automatically extended with each request to the site.
Whereas a Forms authentication extends its time only after 50% of the time has elapsed.
The simplest thing to do is to extend the Forms authentication time in a custom HttpModule or base page, what ever your design is.
In this way your timeout will always be in sync, though there may be some slight gap.
回答4:
Without actually trying anything myself, there are a few things I'd check out.
Use a method/overload of the FormsAuthentication class that allows you to set a persistent cookie. Though, IMHO it is common courtesy to allow your users to choose to check a "remember me" box rather than force them to be persistently logged in. There are a number of methods that allow you to do this based on what behavior you want - SetAuthenticationCookie() and RedirectFromLoginPage() are the first ones that come to mind.
Check out FormsAuthentication.GetAuthenticationCookie(). This will generate an HTTP cookie with the authentication token, but not actually set it, which should allow you to change what you want - though if the FormsAuthentication module is looking for a specific value, messing with it might break authentication. You'll then have to add the cookie to the cookies collection in the response manually.
回答5:
My preferred method is to use session like cache. that is, try to read from session, if the value is there then return it, if it's not, read it from persistence store (database, etc), put it in session and return it.
To simplify this, create a class, add a property for each variable in session, and store an instance of the class in session. Then create a static method to get an instance of the class. I'll add an example later if I could.