Let's say I have app with several account types and each type has its own allowed maximum of concurrent sessions. For example, if the maximum is 1 then when user tries to log in from his laptop while being logged in on his desktop PC he should get an error.
How can I achive that with Spring? I know about concurrency-control but as far as I know it allows to "statically" set the limit for entire context.
I was thinking about SpEL but didn't came up with anything.
P.S.
Also I would like to know is there a specific exception being thrown when user exceeds the limit so I could handle it and show page with explanation why he can't log in.
UPD
Thanks to #Nándor for clever thoughts: when user tries to log in on another device he should be asked to either logout on the current device or on the other.
UPD2
I found that there is a bean of class SessionRegistry and it holds all the neccessery information, also it allows to manually expire sessions. Also I can control amount of sessions with it.
But there's still problem remaining: how do I notify another device that session has been shut down? When I manually expire the session with SessionInformation.expireNow() it give me a page with following text on it:
This session has been expired (possibly due to multiple concurrent logins being attempted as the same user).
The text means exactly what I want but I'd like to handle this as I want. I tried #ExceptionHandler on java.lang.Exception but it hasn't catched anything.
Thanks in advance!
Thanks #Nándor for thoughts, I finally found a solution.
As to managing number of active sessions:
I use SessionRegistry bean, it can manage active sessions on all principals. Also, I think it's worth mentioning that SessionRegistry appears to be empty until I took off the security configuration from servlet config to applicationContext.xml. Spring logs that it successfully put session id to the registry but its still empty unless it's declared in the applicationContext.xml.
As to handle session expiring:
concurrency-control tag has expire-url tag that holds url to which user will be redirected in case of expired session. I can map controller to it and do what I want.
Related
I am interested in asking users to re-authenticate after 15 mins of inactivity without killing their session (session life is longer).
Is there a way to specify an authentication timeout within Spring Security that would accomplish this automatically or some cleaver way I can accomplish this without too much effort?
Adding a request filter seems to be the best way to achieve this goal. In order to prevent the loss of data, I chose to have a blocking popup that displays if the session has been inactive. This way I can refresh the page and the user is right where they left off instead of needing to store the initial request and having to find it and redirect them.
I'm writing the login modules for a JBoss 7.11 web application that needs to limit the number of active sessions a user may be logged into at a time (the actual number will be configurable).
My current plan is to write a custom Login-Module that accepts a user Principal, references the database to see how many sessions are actually allowed per user, and reject or allow depending on whether or not the user sessions associated with that Principal exceeds the limit.
My question is: what the best way is to find out how to reference the number of active sessions that are associated with a Principal?
I do have the obvious option of tracking active sessions in my database and get my count from there, but I'd prefer not to have to use the database for that.
I also noticed an answer (Get HttpSession/Request in a JAAS Login Module) that indicated that HTTPServletRequests can be grabbed, in which case I can get the Session and even the ServletContexts, but I am unsure if there is anything I can do with these to accomplish what I want to do in a Login-Module once I have them.
I am designing a web application which should keep the user's profile that has lots of information,
obviously it reads the data from database.
Currently, I have the username in my session and everytime I need the user's info should read the session then create an object of profile class (that read the data from database again) to retrieve user's info, is it the best practice for such an issue?
This is a typical trade-of between performance and memory consumption. If you want a fast application, keep the whole user profile in HTTP session but make sure you have enough memory in your server.
If you want to keep resource consumption low, store only user ID in session and load it from a database every time you need it. This also makes clustering simpler as there is less data to migrate.
Reasonable compromise is to use the latter approach with some caching. This way hot users (currently using the system) are kept in memory, while idle users or infrequently accessing new pages are swept out from cache (assuming cache is smaller then the number of HTTP sessions).
Agreed with Obe6 response,
Best practice is to ensure if the profile is not in session then to retreive from a datasource and then attach it to a session.
When session is invalidated then all information is removed from session.
There is a good article on this from IBM.
http://www.ibm.com/developerworks/websphere/library/bestpractices/store_objects_in_httpsession.html
Generally a 'best practice' is to maintain the User profile data in session and load all needed information only the first time from the database.
In other words mantain an instance of Profileclass in your http session (must implement Serializable). Profile must hold all the informations used more frequently.
Note that 'reading the session' is like reading an HashMap (so has a minimum cost in term of performances).
When the HttpSession will expire, Profile will be garbage collected.
UPDATE (based on your comments) :
to count (or find) active users (inactive are all the others), a typical solution is make Profile implements the HttpSessionBindingListener interface. When Profile is bound to a session, is notified, so you can increment a static counter (a static attribute of Profile class for example),and you can decrement it when the Profile is unbound (programmatically unbound or because its session has expired)
Session is generally a good enough place to keep the user profile data. You need to quantify how much of data you are talking here. Let's say its 5KB per session, then, you could store up to 20000 user profile in memory using 100 MB of RAM. You can allocate heap to JVM accordingly based on the max. number of active sessions you expect on your site.
This is just one aspect. When you plan to scale the app by adding more app servers, then, you can even think of moving the sessions out to a out-of-process cache/memory stores such as memcached.
If all the user profile data you keep in session does not get rendered on each page, then, it may be a good idea only to keep bare minimum in session and fetch other data as per need.
I'm working on my first website with the Play! framework, and at one point I'm calling this method when the user logs in:
static void connect(User user){
session.put("userid", user.id);
}
Simply storing the userid in a session, and I can check if it's set on each request, works fine. Problem is, once the browser is closed the cookie is lost, and the user needs to login again. I want to create a "remember me" option, and it seems that the only way to do that is create a cookie and send it with the respons, like this:
response.setCookie("user", userdata, "14d");
So I'm wondering, what's the point in creating a session, when it does the exact same thing? (But does not give me any control over the cookie time). And another thing I havn't found yet, is how to read the cookie from the request?
(And I'm aware of the fact that cookies created with setCookie are not encrypted and I need to call Crypto.sign())
1) A Session in Play! is always maintained via cookie (i.e in client side), this is attributed to 'Share nothing' approach.
2) If you use Secure module (or you can take a look at the code and follow if you are writing your own), the 'authenticate()' method takes the parameter 'remember' and set the session for 30 days (response.setCookie("rememberme", Crypto.sign(username) + "-" + username, "30d");)
ie. if user doesn't choose to be 'remembered', their session last only until the browser is closed.
3) The real difference is, as you mentioned, session.put() doesn't allow to set session time out. If you want to extend the session then set it on the cookie.
4) If you want additional authentication while user performing CRUD, (even if user choose to be 'remembered' or their session got extended explicitly by you) its better to set the username/id to cache (rather than setting another identifier to session again) and clear it off when user logout. This will scale well if you choose to use a distributed cache like memcache.
5) To read from cookie, request.cookies.get("name") comes handy.
There are two ways to store state in web apps - client side and server side.
On Server-side either you can use Session or Application objects.
On Client-side you can use View State, Cookies, hidden fields, etc.
Session has a timeout duration after which it expires. When ever you access a web application a session is created for you which lasts for a duration. Hence it is per user thing. Even if you increase the timeout duration, it still expires if you close the browser. Application object is shared between all users.
Cookies are a better way to store such information which needs to be remembered for a longer duration e.g. a day or more. You would have noticed that google allows you to stay logged in for days. That is because they use cookies for state management and not sessions.
You should store the user id in cookie in exactly the same point where you did with session attribute. Use HttpServletRequest.getCookies() for reading cookie. This method returns array of cookies, so you have to iterate over the array to identify relevant cookie.
To change cookie, just override it.
The session lets you tie server-side data to the specific browser session: under the hood a cookie is automatically created that the server uses to look up the server-side data associated with a specific browser.
Control over the session cookie expiry is typically done somewhere in your framework's configuration (or sometimes in the web.xml file used by the app server). You can read the cookie from the HttpServletRequest's getCookies method.
EDIT: this is the getCookies documentation, and for the Play! framework see http://groups.google.com/group/play-framework/msg/6e40b07ff9b49a8a for an example of persistent login and cookie retrieval.
Basically a session is only viable for the period of time in which a user is interacting with your application + the session timeout that you specify. The usability of cookies is to store relevant information to the user so that, when they come back to the website again, you may identify them once more.
For instance, if you have both sensitive and insensitive information regarding a user, you could make your application more friendly by determining who they are via a cookie and loading all of the insensitive information. Once they authenticate themselves then you can load the sensitive information as well.
MSDN has some great reference material as to how to work with cookies at http://msdn.microsoft.com/en-us/library/ms178194.aspx
I'm using OpenID. How do I make it so that the user stays logged in for a long time even after closing the browser window?
How do I store and get access to the user's User object?
Basically, I guess I just don't really understand how sessions work in Java.
So you actually want like a "Remember me on this computer" option? This is actually unrelated to OpenID part. Here's a language-agnostic way how you can do it:
First create a DB table with at least cookie_id and user_id columns. If necessary also add a cookie_ttl and ip_lock. The column names speaks for itself I guess.
On first-time login (if necessary only with the "Remember me" option checked), generate a long, unique, hard-to-guess key (which is in no way related to the user) which represents the cookie_id and store this in the DB along with the user_id. Store the cookie_id as cookie value of a cookie with known cookie name, e.g. remember. Give the cookie a long lifetime, e.g. one year.
On every request, check if the user is logged in. If not, then check the cookie value cookie_id associated with the cookie name remember. If it is there and it is valid according the DB, then automagically login the user associated with the user_id and postpone the cookie age again and if any, also the cookie_ttl in DB.
In Java/JSP/Servlet terms, make use of HttpServletResponse#addCookie() to add a cookie and HttpServletRequest#getCookies() to get cookies. You can do all the first-time checking in a Filter which listens on the desired recources, e.g. /* or maybe a bit more restricted.
With regard to sessions, you don't need it here. It has a shorter lifetime than you need. Only use it to put the logged-in user or the "found" user when it has a valid remember cookie. This way the Filter can just check its presence in the session and then don't need to check the cookies everytime.
It's after all fairly straight forward. Good luck.
See also:
How to implement "Stay Logged In" when user login in to the web application
How do servlets work? Instantiation, sessions, shared variables and multithreading
Well, the original reason I chose OpenID was so someone else could handle as much of the implementation and security of authentication for me.
After looking into OpenID more, it appears there is something called an "Immediate Request" (http://openid.net/specs/openid-authentication-2_0.html#anchor28).
When requesting authentication, the Relying Party MAY request that the OP not interact with the end user. In this case the OP MUST respond immediately with either an assertion that authentication is successful, or a response indicating that the request cannot be completed without further user interaction.
Because of this I think I could just store the user's openID url in the cookie, and use an immediate request to see if the user is authenticated or not. This way I don't have to do anything with my database, or implement any logic for preventing session hijacking of the long-lived cookie.
This method of doing it seems to be the way OpenID suggests to do it with their Relying Party Best Practices document.