Session timeout and multiple webapps - session

I'm faced with a curious problem in my current project:
I've got multiple Spring MVC based web apps deployed on a Glassfish 3.1 server - and I need to be able to "timeout" the user based on the "sesion timeout" parameter in their respective web.xml - no matter in which application the user is on. Please don't ask why the applications are in separate WARs - the architecture is so. The user is logged in via WebApp A and is redirected to a WebApp B - and then the user can keep jumping to different web apps - I guess you get the idea. The WebAppB etc. have numerous Ajax calls (I'm not even going there) as well. The question, I guess, boils down to the fact that I'm not able to share session data between WebApp A and WebApp B (I may be wrong here - and this is where I require help) and so I don't have any way to know by checking
httpServletRequest.getSession(false)
in WebAppB since it returns null in both cases when the first request hits the WebAppB and the first request "after" a session timeout. I have to keep "something" in the WebAppA's session and check for its existence in WebAppB's session - which brings me back to the issue of sharing session data within web applications. I cannot use DB storage, since that would mean a DB call on every request. I got a direction by googling that "crossContext" thing in Tomcat helps in such scenarios - but will something like this be helpful in Glassfish ( there's a "crossContextAllowed" property for sun-web-app.xml which I recently found).
I've been stuck with this for quite some time now and I'm not even sure this is a question worth your time - so thanks in advance for trying to help.
Trishul

I cannot help you with the Glassfish implementation, but what you need is a form of Single Sign On between webapps.
To implement this form of SSO you usually need to do two things:
Make sure all your webapps share a common root context i.e webapp A is on /commonroot/webappA and webapp B is on /commonroot/webappB. The reason for this is that the same session Id must be delivered to the two webapps when the user switches between them. Session Ids are usually stored in cookies and browsers deliver cookies based on path. There must be a setting on Glassfish (as there is on Tomcat and Jetty) which can "force" webapp A to deliver a cookies on path "/commonroot" (rather than /commonroot/webappA) and webappB to do the same. Any access to webapp A or webapp B will then pull and provide the unique session id from the cookie associated with the /commonRoot path.
Once you have all your webapps within the same 'SSO context' share a common session for an user, you need to have these webapps access the session from a common, unique store. A DB is a usual way to do it but if you are looking for speed, something like memcached or hazelcast may be more appropriate. The advantage of using a DB is that it additionally provides session persistence: if your session store is bounced, an user making a call with a session which is not expired will be transparently reconnected without having to login again.
Servlet/JavaEE containers usually provide samples of SSO Realms/SessionManagers or equivalent that will directly implement what you require or that you can hack to fine tune to your needs.

Related

Getting started with sessions

I am using Spring-MVC to build a website prototype. We have a MySQL database that we address using Hibernate.
Now, I want to add basic user session handling (user can login, do stuff on the site and logout). I thought it should be easy to do, but after a bit of research I feel a bit lost.
I have seen the spring-session project. It seems it depends on a redis server for storing session data. Is there a way I could use my existing Mysql/Hibernate setup for storing the session data?
Also, what is the most easiest way to do sesions in spring-mvc ?
Sessions will be maintained within the web/app servers like Tomcat, Weblogic etc., you need not to store session related stuff in any database.

Access postgres DB from two applications

I am performing load balancing for my application. I have made two application servers, say A and B. That access the same database (Postgres) using Hibernate.
The problem arises when the system moves from A to B. Most of the things are working fine, but at some points (While reading data from db) the system automatically logs the user out.
Is this because of synchronization with db?
Is there some kind of lock on the database when one application is accessing the database?
What do i need to do in-order to get it working?
It'll be a great help.
While the Postgres server can terminate connections for various reasons (i.e. if an admin does so), generally that would be a client-side thing, which in your case is likely Hiberate as a Spring DAO. If your app-level login is being closed, it's likely to be an issue with how your app is logging back in on the other server.
Most likely, you have entirely separate DAO instances on each server, and so when the switch from app-server A to B occurs mid DB read, it has to start over from the beginning on the new server, including authentication, et al.
I'm assuming each is deployed in its own WAR and has the DAO injected at runtime, at which point the connection to Postgres is made. (You could add logging in your dependency-injection code to determine more.)
In addition to the separate database connections, it appears it doesn't handle failover to the other server without requiring re-logging in. That shouldn't be related directly to Postgres, at least not the connection. It would depend on how you're persisting the login. i.e. if it's cached locally, that would also have two separate instances in each app, and the authentication would need to reoccur. If the credentials are stored in Postgres, the connection would first need to be obtained before re-authenticating.
Edit in response to comment from OP:
Since they are two separate app instances, a load balancer alone would not work, since that would most likely depend on the request being (mostly) stateless. If you authenticated via a token which was either in the URL or the header, a load balancer would work, since the the auth token would be redirected also, and while reauthentication would need to happen in the backend, authentication should be granted if the token is valid, and the app should essentially stay logged in. It doesn't sound like you're using auth tokens, though.
In short, which two separate instances, the authentication really always needs to happen on every request, but how that's managed can vary. You could look into an OAuth solution, for example, but that may be overkill for your needs.
In any case, you should avoid taking state straight from the client, since that can be tampered with. If the server were to failover cleanly (say, for load purposes), you could control that security handover in the app logic by propagating the security context to the other instance, although that could get a bit unwieldy.
However, during an unclean failover (say, if the JVM heap limit is reached), that wouldn't be possible -- you would need an external authentication system that the other app could query using the supplied credentials, determine the request is authenticated, and allow the request to proceed.

How to manage web sessions for different web apps with single sign on?

I have 2 osgi distributed web apps A(A1, A2) and B(B1, B2), i am using shiro to share the http sessions for single sign on so that logginng in one system logs in the other too. But I want the session data to be separated between system A and B apart from the authentication/authorization data,so I need a way to share the authentication data and separate system specific session data of A and B. I am thinking to use shiro for single sign on and use a different distributed session for each system to store system specific data, the main workflow would be :
log in system A with shiro, create an another session(or some data holder object to store the session data) using the shiro session id,stores it in backend(db or cache).
do stuff in system A, put session data into the session created above, writes to backend.
When another request comes in system A, look for a session with the id matching the shiro session id, if found, use it, else create new one.
Does it make sense? Is it the right way to solve my problem? What is the best way? Please share your thoughts.
Any input is appreciated.
Thank you.
I've done something like that with a Servlet Filter. My HtppSession contains the authorization id which I then lookup and provide as security context for the request. If no security id is present, the client is forced to login. Obviously the security context can be held in a distribution mechanism so it is available from all systems in a cluster.
Make sure you use https between the client and the systems or otherwise someone can eavesdrop, read the cookie with the id, and login to one of the systems with the authorization of the victim. I.e. session hijacking.

how to share data stored in session of different webapps under same tomcat

I have several webapps deployed under one tomcat, each with it's own context, but I need to share the login user information between those webapps, such as, when user login from webapp A, it's id, name and other information are stored in session, when it tries to access other webapp, I hope we can get those information from session, according servlet spce, session can't be shared between different webapps, is there a way to share those data in seesion between different webapps deployed in same tomcat?
Not easily. The Servlet API doesn't support such usage. There are several ways to do this:
SSO may get you most of what you need.
Store information in a database and look it up in the other web applications when you need it.
Create/find some shared storage accessible from both web applications (i.e. some form of cache). You need to be very careful with this approach that you don't create any memory leaks.
Use cross context requests to pass information between web applications (a little ugly and may create security issues).
Probably a bunch of ways I haven't thought of.
1.use this attribute
crossContext="true"
2.use webservice to manage user privileges
Pretty old topic, but I got similar issue. I haven't solved it yet, but some notes, which might be helpful for others on seeking solution.
There are several reasons why you might want to share sessions between several Tomcat Web apps. One of issues is dependency hell, so one web app uses newer libraries and you just cannot incorporate functionality into another web app.
For example on Apache/PHP case, you can just put your apps in different folders and run them within same session.
Another issues is also persistence of sessions, which comes important when you want to share session data on Tomcat.
What is easy to achieve, is to use same session cookie. You just have to add sessionCookiePath="/" into context.xml.
<Context reloadable="true" crossContext="true" sessionCookiePath="/">
To make sessions persistent you might want to store data into database. Which means, you have to configure session storage on context.xml file. For example:
<Valve className="org.apache.catalina.valves.PersistentValve" />
<Manager className="org.apache.catalina.session.PersistentManager" processExpiresFrequency="3" maxIdleBackup="1" debug="99">
<Store className="org.apache.catalina.session.JDBCStore"
driverName="org.postgresql.Driver"
connectionURL="jdbc:postgresql://localhost:5432/tomcat"
connectionName="<username-here>"
connectionPassword="<password-here>"
sessionAppCol="app_name"
sessionDataCol="session_data"
sessionIdCol="session_id"
sessionLastAccessedCol="last_access"
sessionMaxInactiveCol="max_inactive"
sessionTable="public.tomcat_sessions"
sessionValidCol="valid_session"
debug="99"
/>
</Manager>
Session data will be written into database. But what becomes an issues is that apps have different names! So they cannot share session data.
sessionAppCol="app_name"
Field sessionAppCol is responsible for separating data for different apps. When I change app name on session data row, then data becomes immediately available for this app, which name I'm using.
So the only obstacle left is to make apps to share same name somehow.. But how?
Seems that on case of using JDBCStore, there is no way to share sessions. However, it is possible with Redisson.
Download appropriate versions of redisson-all and redisson-tomcat.
Put these on /lib folder
Install Redis
Start Redis
Modify Context.xml
Create redisson.yml on /lib folder and add appropriate settings
Reboot Tomcat
Context.xml
<Context reloadable="true" crossContext="true" sessionCookiePath="/">
<Manager
className="org.redisson.tomcat.RedissonSessionManager"
configPath="${catalina.base}/conf/redisson.yml"
readMode="REDIS"
updateMode="AFTER_REQUEST"
broadcastSessionEvents="true"
keyPrefix="bce"
/>
redisson.yml:
singleServerConfig:
address: "redis://127.0.0.1:${REDIS_PORT:-6379}"
database: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
You have now persistent shared sessions.
Setting crossContext="true" allows to access data from other webapps.

What does the crossContext attribute do in Tomcat? Does it enable session sharing?

All I can find in the Tomcat 5.5 docs is:
Set to true if you want calls within this application to ServletContext.getContext() to successfully return a request dispatcher for other web applications running on this virtual host. Set to false (the default) in security conscious environments, to make getContext() always return null.
I've found some forum posts that comment that setting crossContext=true also enables sharing the session object between different web applications, but I'm not able to find any official docs stating this.
Is there a relation between Servlet.getContext() and the ability to share session state between different web applications?
What does the crossContext attribute really do in Tomcat?
You can share sessions between web applications by using a Single Sign-On Valve.
You would set crossContext=true if you wanted to share some information between different Web Applications in the same Virtual Host.
For example app1 would call:
setAttribute("name", object);
and another app could call
getContext("/app1").getAttribute("name");
to read the information. If crossContext wasn't set to true, the getContext("/app1") would have returned null.
However, the use of crossContext is both rare and potentially insecure.
From the javadoc ServletContext.getContext():
This method allows servlets to gain access to the context for various parts of the server, and as needed obtain RequestDispatcher objects from the context. The given path must be begin with "/", is interpreted relative to the server's document root and is matched against the context roots of other web applications hosted on this container.
So for instance if you want to include a page from a different webapp you need to set crossContext to true.
I tried it myself and I can't find the magical session sharing side effect, so the crossContext attribute only does what the docs say.
I've posted another question to see if there is a way to share the session state.

Resources