Is there a way to do a Session.Abandon() when the user just kills the session by X'ing out with the little red X box in the upper right corner? I am seeing situations where after an "X out", the user comes back in and it seems like session variables are still present? I guess explorer keeps them intact? (I am unsure of this).
You can try doing an AJAX type callback in the OnUnload event - however, as someone else mentioned you'd have to be aware of other tabs being open (not easy), and it still won't guarantee you get that event.
There's a combination of things to do to get a similar type of effect.
Session Cookie should have a null/empty expiry time. This ensures the browser deletes the session from it's end after the browser is closed.
The ASP Session can be set with a short SessionState timeout value. This means if there's no client activity within that period, the Session will expire.
The side effect of this is that if a user is just looking at the site, and not performing activity (regardless of whether the browser is still open) - the session can expire.
This can be worked-around by having a Javascript timer periodically ping back to the server with an AJAX call or similar. The side effect of THIS is that it generates more site traffic.
The server is typically not aware of such events on the client. The only way the server can be notified about anything is if there is a request sent back to it. I guess you could create such a notification in a JavaScript, but then you should also keep in mind that the session in mind that the session is not per-page but (usually) per user, which means that you would need to keep track of how many tabs/windows the user has opened so that you don't kill the session when you should not.
Personally, I usually try to design the web apps so that they live well with the default handling of sessions, either providing a specific "Logout" command that will kill the session, or simply let it hit the timeout and die.
Related
I am trying to "log" forcefully when a user has been inactive and or the session has ended (either by inactivity or more importantly, when the browser has closed).
I dont want to use any silly AJAX solution to perform a post every few minutes for "im alive" or call when the browser is closed.
I was under the impression that if you store an object in Session, and you reach the Session_End event, then you will not be able to gain access to anything stored in Session as its ended.
But from some testing I have done, it appears that this is probably the last chance you can obtain access to the object.
Could this be true? is it reliable?
using ASP.NET 4.0 here.
Typically there are two things done.
The first is that a javascript timer is added to the client, not as a heartbeat, but rather as a reminder. If they are close to the session ending, then it simply says "session is about to end. Are you still there?" If so, then it does the "silly" post to ensure the server keeps the session going. This is purely to be nice to your users.
The second (and point of your question) is that you put something in session_end in order to clean up the session. Reliable? well.. most of the time.
Session_End won't run if the app pool is recycled. However, assuming the app pool is ok then yes it will execute when the session expires. The app pool can be recycled for a LOT of reasons ranging from the app crashing to exceeded memory usage to simply because it's been a while since the last reset. This is configurable in IIS.
Would I trust session_end? No. Not 100%. Of course, I wouldn't put anything inside of a session object that would require me to trust it 100% anyway.
For logging the timeout You can use the Global events to log at timeout. See this link for order of events http://www.techrepublic.com/article/working-with-the-aspnet-globalasax-file/5771721
There might be a few questions here, but one major question... what should be implemented if we make a modal authentication work? Let me try to explain..
Current environment:
ASP.NET w/ .NET 4.0 w/ forms authentication
Our customers that use our lab software have to be extra cautious of another user taking control of their computer, so we can't implement persistent timeouts (I think the last time I read, you can keep extending the timeout as long as there's something happening in ASP.NET, right?). Even though we have password authentication throughout our laboratory rich client application, we still don't want a random person walking by some employees desk to see what they're working on and have something get compromised. So I've been thinking about this for quite some time and tonight I had an epiphany. What if we were to have the Login page pop up in a modal dialog within an iframe (or object tag) in a modal div that's inside of our masterpage? How can we keep their session from ever expiring, but require them to login after the session has timed out? Is there anything else you can think of that will be required if we were to implement something like this for it to work? Note, we have session variables within the software that cannot be reset if this occurs. How can we keep them persistent but still make this work? The main thing is I want to avoid having them be redirected to the Login page. This is rather annoying for end-users. By law, they need to have the timeout set to 2 minutes, so I thought this would be really cool if I can make it work. Any other things we need to watch out for??
I can't but think that it's scary to use asp.net session, especially with forms-auth - because, the user gets 2 cookies: session and auth. Imagine what would happen if, somehow, authenticated user A would steal session cookie from authenticated user B: it would result in user A having access to all data that user B owns (unless your code checks whether user-id from auth-cookie owns the session object. In other words, I would suggest to get rid of the session, or at least add user-id value to session object and make sure you check that user id from the auth-cookie matches that within application_authorize event, maybe. You didn't ask for this info, but I think it's appropriate, regardless.
Since the session and the auth cookies have little to do with each other, as far as browser is concerned, and your goal is to keep the session alive, while auth-cookie should expire, then, you can maybe solve that by writing a piece of JS (hint: window.setInterval) that regularly pings some ANONYMOUS url (aspx page) at your server (make sure you add a random query to those requests; e.g. new Date().getTime()). The anon aspx page would need to read (do not write!) some value from the session (or simply retrieve the session object) - just to keep it alive (maybe this isn't really necessary; do experiment), but the browser WILL be sending asp.net session cookie with these requests, so you can keep the session object alive forever this way.
On the other side, your auth-cookie will expire. However you MUST set web.config settings (authentication > forms) to NOT use sliding expiration (as that mode essentially extends the validity/expiration of the auth cookie for another whatever-the-timeout-is minutes). Then, you can be sure that, after the cookie expires (e.g. after 20 min), when the user clicks on a protected link (well, a link that links to protected page; non-anon page), then they will land on login page. I know that you don't want this. So, to solve that, add another (independent) piece of javascript (hint: window.setTimeout([code], 2 * 60 * 1000) // to fire after 2 min since the page-load) to launch the login dialog. The login dialog would extend the auth-cookie by posting the uid/pwd and letting asp.net validate it.
Another thing: if you have ajax going on on that page, you must think of resetting these js timeouts back to 0 (or cancelling then reinitializing interval and timeout events). In other words, you can't start measuring inactivity since the page load - you have to reset the inactivity counter on every user's action (click; or at least on every ajax callback).
What I'm suggesting here may be an overkill. I would probably try to solve this differently. I would try to eliminate in-process session from the picture, and reload it based on auth-cookie's user-id from whereever user data is, every time it's needed (or once per request). I don't know why it's so important to keep the session object hanging in memory, even when the user is logged out (how do you know they won't leave for a week; keeping sessions alive would be killing your server if you had a large number of users). Maybe store the session data in database or some other caching mechanism on the network (e.g. memcached) and retrieve it once per request (e.g. in application_authorize), store it in request.context (to eliminate retrieving it multiple times from multiple places). Then, your auth-cookie will expire, and use JS to popup the login dialog a few min before the auth cookie expired (to avoid the gap where the user will land on login page if they click on a link, if you care about that even).
I hope these ideas help.
I am putting an "online" counter on a website, and I have run into these two contradicting sources.
This one (I am using this example code):
http://aspdotnetfaq.com/Faq/How-to-show-number-of-online-users-visitors-for-ASP-NET-website.aspx
...says that:
Also when user closes his browser or does not click on any links in
our website, session expires, and our "OnlineUsers" global variable
is decreased.
However, this one:
http://www.velocityreviews.com/forums/t383710-session-end-guarantee.html
...says that:
Closing the browser window, or browsing to another site will NOT cause
Session_End to fire, at least, not straightaway - the server has
absolutely no way of knowing what happens on the client machine until
it gets another HttpRequest from it. In this instance, Session_End
will fire when the session times out naturally.
I have tested and it seems that Session_End DOES NOT fire.
I basically want you guys to confirm or comment on this.
Is it possible to update the online counter on browser-close?
The second is true
Closing the browser window, or browsing to another site will NOT cause
Session_End to fire, at least, not straightaway - the server has
absolutely no way of knowing what happens on the client machine until
it gets another HttpRequest from it. In this instance, Session_End
will fire when the session times out naturally.
Session time out is 20 minutes by default. You can confirm this by not doing any activity on your website for 20 min.
Your two links are not contradictory. The first link is poorly worded, but it basically says what the second link says. It would be easier to understand if it were written like this:
Also when user closes his browser or does not click on any links in our website (after a period of time) the session will expire, and our "OnlineUsers" global variable is decreased.
Also note that Session_End may not always fire, especially if the session is killed or bounced (for example, if you update the web.config, the worker process recycles, or in some cases an uncaught exception occurs).
As is pointed out later in the first link posted the Session_End event fires after the session timeout expires. If the user who is associated with a session sends no requests to the server before the session timeout value expires the session_end event will be fired and the session will be removed from memory.
MSDN for asp.net session state: http://msdn.microsoft.com/en-us/library/ms178581.aspx
I am not able to make more than one request at a time in asp.net while the session is active. Why does this limitation exist? Is there a way to work around it?
This issue can be demonstrated with a WebForms app with just 3 simple aspx pages (although the limitation still applies in asp.net mvc).
Create an asp.net 3.5 web application.
There should be just three pages:
NoWait.aspx, Wait.aspx, and SessionStart.aspx
NoWait.aspx has this single nugget added between the default div tags: <%=DateTime.Now.Ticks %>. The code-behind for this page is the default (empty).
Wait.aspx looks just like NoWait.aspx, but it has one line added to Page_Load in the code-behind: Thread.Sleep(3000); //wait 3 seconds
SessionStart.aspx also looks just like NoWait.aspx, but it has this single line in its code-behind: Session["Whatever"] = "Anything";
Open a browser and go to NoWait.aspx. It properly shows a number in the response, such as: "633937963004391610". Keep refreshing and it keeps changing the number. Great so far! Create a new tab in the same browser and go to Wait.aspx. It sits for 3 seconds, then writes the number to the response. Great so far! No, try this: Go to Wait.aspx and while it's spinning, quickly tab over to NoWait.aspx and refresh. Even while Wait.aspx is sleeping, NoWait.aspx WILL provide a response. Great so far. You can continue to refresh NoWait.aspx while Wait.aspx is spinning, and the server happily sends a response each time. This is the behavior I expect.
Now is where it gets weird.
In a 3rd tab, in the same browser, visit SessionStart.aspx. Next, tab over to Wait.aspx and refresh. While it's spinning, tab over to NoWait.aspx and refresh. NoWait.aspx will NOT send a response until Wait.aspx is done running!
This proves that while a session is active, you can't make concurrent requests with the same user. Requests are all queued up and served synchronously. I do not expect or understand this behavior. I have tested this on Visual Studio 2008's built in web server, and also IIS 7 and IIS 7.5.
So I have a few questions:
1) Am I correct that there is indeed a limitation here, or is my test above invalid because I am doing something wrong?
2) Is there a way to work around this limitation? In my web app, certain things take a long time to execute, and I would like users to be able to do things in other tabs while they wait of a big request to complete. Can I somehow configure the session to allow "dirty reads"? This could prevent it from being locked during the request?
3) Why does this limitation exist? I would like to gain a good understanding of why this limitation is necessary. I think I'd be a better developer if I knew!
Here is a link talking about session state and locking. It does perform and exclusive lock.
The easiest way around this is to make the long running tasks asynchronous. You can make the long running tasks run on a separate thread, or use and asynchronous delegate and return a response to the browser immediately. The client side page can send requests to the server to check and see if it is done (through ajax most likely), and when the server tells the client it's finished, notify the user. That way although the server requests have to be handled one at a time by the server, it doesn't look like that to the user.
This does have it's own set of problems, and you'll have to make sure that account for the HTTP context closing as that will dispose certain functionality in the asp.net session. One example you'll probably have to account for is probably releasing a lock on the session, if that is actually occurring.
This isn't too surprising that this could be a limitation. Each browser would have it's own session, before the advent of ajax, post back requests were synchronous. Making the same session handle concurrent could get really ugly, and I can see how that wouldn't be a priority for the IIS and ASP.NET teams to add in.
For reasons Kevin described, users can't access two pages that might write to their session state at the same time - the framework itself can't exert fine-grained control over the locking of the session store, so it has to lock it for entire requests.
To work around this, pages that only read session data can declare that they do so. ASP.NET won't obtain a session state write lock for them:
// Or false if it doesn't need access to session state at all
EnableSessionState="ReadOnly"
By default the session expiry seems to be 20 minutes.
Update: I do not want the session to expire until the browser is closed.
Update2: This is my scenario. User logs into site. Plays around the site. Leaves computer to go for a shower (>20 mins ;)). Comes back to computer and should be able to play around. He closes browser, which deletes session cookie. The next time he comes to the site from a new browser instance, he would need to login again.
In PHP I can set session.cookie_lifetime in php.ini to zero to achieve this.
If you want to extend the session beyond 20 minutes, you change the default using the IIS admin or you can set it in the web.config file. For example, to set the timeout to 60 minutes in web.config:
<configuration>
<system.web>
<sessionState timeout="60" />
... other elements omitted ...
</system.web>
... other elements omitted ....
</configuration>
You can do the same for a particular user in code with:
Session.Timeout = 60
Whichever method you choose, you can change the timeout to whatever value you think is reasonable to allow your users to do other things and still maintain their session.
There are downsides of course: for the user, there is the possible security issue of leaving their browser unattended and having it still logged in when someone else starts to use it. For you there is the issue of memory usage on the server - the longer sessions last, the more memory you'll be using at any one time. Whether or not that matters depends on the load on your server.
If you don't want to guesstimate a reasonable extended timeout, you'll need to use one of the other techniques already suggested, requiring some JavaScript running in the browser to ping the server periodically and/or abandon the session when a page is unloaded (provided the user isn't going to another page on your site, of course).
You could set a short session timeout (eg 5 mins) and then get the page to poll the server periodically, either by using Javascript to fire an XmlHttpRequest every 2 minutes, or by having a hidden iframe which points to a page which refreshes itself every 2 minutes.
Once the browser closes, the session would timeout pretty quickly afterwards as there would be nothing to keep it alive.
This is not a new problem, there are several scenarios that must be handled if you want to catch all the ways a session can end, here are general examples of some of them:
The browser instance or tab is closed.
User navigates away from your website using the same browser instance or tab.
The users loses their connection to the internet (this could include power loss to user's computer or any other means).
User walks away from the computer (or in some other way stops interacting with your site).
The server loses power/reboots.
The first two items must be handled by the client sending information to the server, generally you would use javascript to navigate to a logout page that quickly expires the session.
The third and fourth items are normally handled by setting the session state timeout (it can be any amount of time). The amount of time you use is based on finding a value that allows the users to use your site without overwhelming the server. A very rough rule of thumb could be 30 minutes plus or minus 10 minutes. However the appropriate value would probably have to be the subject of another post.
The fifth item is handled based on how you are storing your sessions. Sessions stored in-state will not survive a reboot since they are in the computer's ram. Sessions stored in a db or cookie would survive the reboot. You could handle this as you see fit.
In my limited experience when this issue has come up before, it's been determined that just setting the session timeout to an acceptable value is all that's needed. However it can be done.
This is default. When you have a session, it stores the session in a "Session Cookie", which is automatically deleted when the browser is closed.
If you want to have the session between 2 browser session, you have to set the Cookie.Expired to a date in the feature.
Because the session you talk about is stored by the server, and not the client you can't do what you want.
But consider not using ASP.NET server side session, and instead only rely on cookies.
Unfortunately due to the explicit nature of the web and the fact there is no permanent link between a website server and a users browser it is impossible to tell when a user has closed their browser. There are events and JavaScript which you can implement (e.g. onunload) which you can use to place calls back to the server which in turn could 'kill' a session - Session.Abandon();
You can set the timeout length of a session within the web.config, remember this timeout is based on the time since the last call to the server was placed by the users browser.
Browser timedout did not added.
There's no way to explicitly clear the session if you don't communicate in some way between the client and the server at the point of window closing, so I would expect sending a special URI request to clear the session at the point of receiving a window close message.
My Javascript is not good enough to give you the actual instructions to do that; sorry :(
You cant, as you can't control how the html client response.
Actually why you need to do so? As long as no one can pick up the session to use again, it would expire after that 20 minutes. If resources does matter, set a more aggressive session expiry (most hosting companies did that, which is horribly annoying) or use less objects in session. Try to avoid any kind of object, instead just store the keys for retrieving them, that is a very important design as it helps you to scale your session to a state server when you get big.
Correct me if I am misreading the intent of your question, but the underlying question seems to be less about how to force the session to end when a user closes the browser and more about how to prevent a session from ending until the browser is closed.
I think the real answer to this is to re-evaluate what you are using sessions to do. If you are using them to maintain state, I agree with the other responses that you may be out of luck.
However, a preferred approach is to use a persistent state mechanism with the same scope as the browser session such as a cookie that expires when the browser is closed. That cookie could contain just enough information to re-initiate the session on the server if it has expired since the last request. Combined with a relatively short (5-10 min) session timeout, I think this gives you the best balance between server resource usage and not making the user continually "re-boot" the site.
Oh you have rewritten the question.
That one is absolutely feasible, as long as javascript is alive. Use any timed ajax will do. Check with prototype library http://www.prototypejs.org PeriodicalExecutor or jQuery with the ajax + timer plugin. Setup a dummy page which your executor will call from time to time, so your session is always alive unless if he logouts (kill the ajax timer in the same time) or close browser (which means the executor is killed anyway)