I have set max concurrent sessions to one.
Now there are 2 situations I want to handle if there is an active session (Someone is already logged in for that User Id):
If the user accesses login page again on the same browser:
In this case, he will be directly logged in.
If the user logs in from different browser/client machine: In this case, need to show the error "A session is already active for this user. Are you sure you want to overwrite the session?". If the user selects 'yes', the error will be shown at the previous session (this I know error page can be configured), and if he selects no, it will not login, the previous session will persist.
How can these usecases be achieved using spring security? So far, I have been able to found the basic management of concurrent sessions, by setting max session count and error page.
Please help.
Thanks.
I think you have almost done it. To ask user before overwriting a session follow the below steps.
The expiredUrl property of ConcurrentSessionFilter should got to a page where the UI is provided to ask user (e.g. askUser.html).
The askUser.html form submits to another url (e.g. /api/confirmSessionRewrite) with POST parameter yes or no.
In controller ConfirmSessionRewrite either remove the user from session registry or forward it to login page.
sessionRegistry.removeSessionInformation(info.getSessionId());
You have to modify your config so that the ConcurrentSessionFilter would not run on /api/confirmSessionRewrite request. Otherwise it will be a loop.
Related
When the user logs in with the same userid from another computer, without logging off from the first, the default behaviour is that the first session is invalidated. Now, when the user goes back to the first computer, I want to tell him that his session has been invalidated because he has logged on from some other computer. How can I do that ?
I have thought of two approaches :
Allow multiple logins, and when the user logs in from the second computer, add a boolean flag to all the sessions (whose ids are obtained from the sessionRegistry) and when the user goes back to login from the first computer, check whether the current session has the boolean flag as true. If yes, invalidate the session, and send the user a message. This will be done in a CustomSuccessHandler.
Flipside : Its probably not possible to obtain the session object via the session id (which is all what session registry provides)
Don't allow multiple logins, and when the user goes back to his first computer, somehow find out the reason for the session being invalidated. If its invalidated because of multiple logins, display the proper message.
Flipside : It doesn't seem possible to add invalidation reasons while invalidating a session, and I don't know how to access this information (if it exists) from the first computer
Don't allow multiple logins. You can easily achieve this using spring security.
Add concurrent-session-control to spring security.
<concurrent-session-control max-sessions="1" exception-if-maximum-exceeded="false" expired-url="/jsp/invalidate.do"/>
//you can also set expired-url to your custom invalidate page rather than create a mapping
Create custom requestMapping for invalidate user.
#RequestMapping( value = "/invalidate.do", method = RequestMethod.GET )
public String invalidate(HttpServletRequest request)
{
request.setAttribute("invalidate",true);
return "login";
}
Then your login page
<c:if test="${not empty invalidate}">
<script type="text/javascript">
alert("You have been Logged Out. Someone signed in using your account.");
</script>
</script>
Another way is to set your error-if-maximum-exceed to true,so it will flag error if user tries to login into another session.
<security:session-management>
<security:concurrency-control error-if-maximum-exceeded="true" max-sessions="1"/>
</security:session-management>
Then create a custom message in your message.properties
ConcurrentSessionControlStrategy.exceededAllowed=You have been Logged Out. Someone signed in using your account.
Hope it helps.
Links:
http://codehustler.org/blog/spring-security-tutorial-form-login/
Maximum concurrent users in Spring Security
Scenario:
User logs into website.com using firefox. Login credentials are valid, user is directed to member's page.
User tries to log in to website.com using chrome. Login credentials are valid, because use is already logged in using firefox, system will throw error, asks user to close other session to login through chrome.
How can I detect that? Right now I am able to detect it if user only use one browser, but seems to break when user uses two different browsers to log in at different times.
EDIT* I want to say it's more than just using different browsers, the website should not allow multiple people to log in with the same login credentials.
I am assuming your application is j2EE/servlet based. If it is the case, two browsers are independent of each other, hence they have their own sessionId and can function independently, as long as your application does not interfere.
To prevent this scenario, one way to implement is, keep a hashmap of SessionID and UserID in your servlet. You populate this on every successful login, for example via a filter or a valve. When you are populating the hashmap, make a check, to see if any other sessionID is already using this userID. If it is used, check if the corresponding sessionID is still active. If it is not active, allow the login, and delete the stale sessionID. If it is active, terminate the other session and allow the login.
If you're using Spring Security - it may be specified by parameter in the configuration file.
If just plain java - during log-in put user's session id to some storage; when he tries to log-in again you should prohibit it.
But you need to avoid situation when the user will be in storage very long time after closing the browser (one possible solution is short session timeout + keep-alive requests)
In your application, keep a timeout of the user that's updated after each call to the app. You can define the user as 'locked' into a session (for example your firefox session) until either the timeout expires, or the user requests a logout. When you log in on another browse (for example, chrome) it checks to see if there's an active session and, if there is, denies the login attempt.
I'm going to make up a quick example. This isn't even close to production ready and is for illustrative purposes only.
class User {
long lastCheckin;
int userId;
String username;
}
Now, when someone does anything in the app, like viewing a page, you do this
user.lastCheckin = System.currentTimeMillis();
Now, when someone specifically requests a logout, you do this
user.lastCheckin = 0L;
Now, when someone tries to log in, you do this
if(user.lastCheckin + PREDEFINED_TIMEOUT > System.currentTimeMillis()) {
return new LoginResponse(false,"User is active in another session.");
}
You can store a map of logged-in users on an application scope variable like a ServletContext. For example, on your auth servlet, you can probably do something like:
Map<String,String> activeUsers = request.getSession().getServletContext().getAttribute("__ONLINE_AUTHENTICATED_USERS");
//if null, context hasn't been prepared yet, create and attach a new one?2
You have to be careful though. Being an application scope variable, you need to ensure some thread safety and this is something which the servletContext.setAttribute/getAttribute is providing(e.g. those operations are not thread safe). You may be able to handle this by using some sort of application lifecycle listener to 'initialize' the servletContext to have the user map. This way you won't need to worry about the set/getAttribute. You still need to think about the map operations themselves(e.g. use j.u.c.ConcurrentHashMap maybe?).
You also have to take care of cleaning up(e.g. removing from the map) when a user logs out or session times out.
You also have to consider that a user might lock himself out for a long time by this approach(e.g. close browser but do not logout properly, session needs to timeout before the mapping is cleared).
Edit:
You also need to think about scalability and this depends on your application. Are you expecting a million online users? Or only a couple of thousands?
I have a web app in which I have set the maximum inactivity time to 10 min. This is just for testing purposes. Basically, if the session has timeout and I click on a link, the following window browser checks if the session is valid. This is also working fine. If this happens, I get a message saying "session has expired, please login again". But the orginal window stays open and if I click on the same link, then this time is letting me see the page, even though I have not logged in again. Why is this?
I am using the session.invalidate() if the session is expired, to make sure all attributes are removed, but this is not working somehow.
I using the following part of the code at the beginning of the page:
if(request.isRequestedSessionIdValid() == false)
{
response.sendRedirect("expired.jsp");
session.invalidate();
return;
}
This is working the first time this page is loaded, but if I click on the link again to load it once more, this condition is not met, despite the session being timeout.
Could you please give any advice?
Update: My webapp works the following way:
User gets to the index.jsp page and uses an ID and password to access the system, then there is a BRMspace.jsp page where there is a folder structure for the user to access depending on the documents they are after. By clicking on each folder, a table with a database populated is displayed for the user to download the documents they want.
The issue I am having is that after 10 min of inactivity, if the user clicks on one folder on the initial screen, the database is not displayed, instead I get a message saying that session has expired and I am redirected to the login page, which is ideal. However, if I click on the same folder again, this time I get the usual table with the data and all documents. It seems that after one click, the inactivity time is not longer valid.... so I am not sure how to do... I am using session.invalidate() to delete all data about the session, but obvioulsy is not working.
I need the user to be redirected to login page again after the inactivity time no matter where the user clicks on.
This is an update:
Hi there, I have to re-take this question, which has been very helpful to resolve 90% of my original issue, but I still have one left.... on my web application, when user logins, they have a list of options to click on, each click takes them to a new tab which are different .jsp files... if session has expired, these tabs show the expired.jsp file, which is perfect... however, the original tab, the one that is shown after the user logins, stays live, I mean, it does not show that the session has expired... what can I do in this case?...
A web session doesn't have anything to do with any login or access credentials. It simply means that the container has data saved for you that can be retrieved if you pass in the correct session token (either by cookie or request parameter). If you have been inactive on the site for a period of time (in your case 10 minutes), that data is discarded and if you check for a sessions validity, you will discover whether the data is still around or has been discarded. If the session has expired, the container will automatically create a new session for you to handle future requests. And if another request is sent to the server before the timeout expires, that requested session will not be invalid.
If you are trying to prevent people from access a page when they have not logged in, you actually need to put some value into the session that says they have authenticated, and check that value. Just checking whether their requested session is valid is not sufficient.
We have a spring security application with a pretty standard setup. Currently we only allow 1 session per principal, rejecting additional logins by the same principal until the first session is logged out or expired (maximumSessions=1, excpeptionIfMaximumExceeded=true).
I'd like to change this so that when a principal logs in a second time with a currently active login on another session the first session is invalidated/replaced. This is easily accomplished using the provided spring security concurrent session control strategy but I am having trouble figuring out how to alert the user. When a user's session is replaced the session is invalidated by the logout handler. The next request will get a redirect to the login page with a error code on the query string. However, if the request which gets this redirect is an image or other non-programatic call I'm unable to handle this.
It seems like I need to put the user into an inbetween state, where they have a session but it is expired and they need to log back in if they didn't mean to replace their original session. However I don't see a good way to do this.
Is there an example of a setup like this somewhere?
Have you thought of working out a polling mechanism in javascript to alert the user when their session is about to be invalidated? This way they will know that their session isnt valid and possibly have a chance to refresh it in case they have partially filled out forms or text areas.
This can also work from the login side. You can add a step after login if they have another session active and have them verify they want to invalidate it.
It seems that an in between step isnt necessary because, generally,authentication should be boolean. Either they are authenticated or they arent. The inbetween zone might be tougher to handle all cases for.
The website is having auto-refresh.When an user login with the same username that is logged in already somewhere,how to logout the previous login?How to give a relogin page in the first browser window?
Please provide some code snippets....
Thanks in advance....
This post is dealing with a similar problem.
Without you specifying more details, it's difficult to answer your question properly. First of all, if a user opens another tab or window within the same browser, they will be still logged in using the previous login. This is normal behaviour.
If a user logs in using a different browser, then one thing you can do:
register a HttpSessionListener
when a session is created, using void sessionCreated(HttpSessionEvent se), check if user's credentials and session id are in your database
if not, put them in a database table
if yes, then invalidate their previous session by deleting previous credentials in database
when a session is destroyed, using void sessionDestroyed(HttpSessionEvent se) delete user's credentials in database
One other thing. If you're going to use this approach, then you'll have to check with every browser request if your user's credentials are stored in a database. You can use a Servlet filter for this. This will, of course, be an overhead.
One more thing. If there's an exception in your session creation/destruction code, there's a danger of user's credentials aren't properly disposed of in database. You can handle this using database triggers to delete rows that are as old as your session timeout is.