Spring session lazy deserialization - java

I have the following situation, i have a microservice architecture with an api gateway and multiple downstream services, some of these have an independent session and this causes my system to throw expired session exception on random service calls.
Since we cannot rewrite these services from scratch we started by introducing hazelcast so that all services can share the same session.
the problem now is that when a service writes an object of a class that other services don't have in their classpath a deserialization exception is thrown.
to solve this i was thinking that if only the attributes that get accessed from a service get deserialized i could probably avoid the exception and everything should work fine.
Do you know if this is at all possible with spring session, or maybe can suggest another solution that would allow me solve the initial problem?
here is a sample project to reproduce my problem: https://github.com/deathcoder/hazelcast-shared-session

I believe I got what's happening: Spring-Session-Hazelcast by default store session updates locally until request completed & when request completed, before returning the response, send everything to the cluster using EntryProcessor. EntryProcessor requires object classes available on the member who stores that session record and since data is distributed, it's possible some other member stores a session created in another instance. According to what you're saying, not all nodes are identical, don't contain all classes & this causes serialization exception.
What you can do, you can use User Code Deployment feature to deploy those missing classes to other members: https://docs.hazelcast.org/docs/3.11/manual/html-single/index.html#member-user-code-deployment-beta
If you're changing object that you're storing in the session, you can set class-cache-mode to OFF to prevent not caching them but sending with each operation.
Please try & let me know if this solves your problem.

I would try to avoid sessions in the API layer in the first place. They scale poorly. And synchronizing sessions is even less scalable.
Try using access tokens instead, for example a JWT token. A token should contain enough user identity information to load the resources necessary to process the transaction (the resources can be cached).
As for the other state in the session - microservices are self-contained from the process perspective, so all intermediate results should be persisted to the database. But of course I don't know the details of your particular application, so this is just a general thought.

Related

Sharing session data between two war files

I have two war files such as war1 and war2
If am login the application, the session will be created in war1 and from that if am navigate to war2, there i need the same session data.
I tried crossContext=true in context.xml of server from that i can access the data by storing it in servletContext.
But the issue is once i logined the screen in chrome the session data will be stored in servletContext and the data will maintain till the application is running.
If am giving the same URL in another browser like IE here also, i can get the servletContext data so instead of navigate to login page the corresponding screen will be opened
Kindly suggest me how can i overcome this issue in java?
Is there any way to findout browser switching or incognito window mode of the browser in java?
Note: am using tomcat server
I have never dealt with your exact configuration problem, but even if you can make this work on a single Tomcat instance, you might have problems should your two web applications ever be distributed across multiple Tomcat instances.
So, I am going to suggest that you actually use a database to store state which needs to be passed between the two applications in a safe and reliable way. Note that the database approach also scales nicely in a distributed environment, so long as you have a single logical database.
While session replication indeed can be done in Tomcat (see here) I really suggest you to avoid this type of issues by eliminating the session altogether.
This session replication is an approach that was somewhat common before ~15-10 years, but nowadays when we have a lot of servers running in parallel to serve user requests and have elastic clusters, this approach is not good enough because basically it doesn't scale well.
There are many ways to achieve what you want, though:
Use a shared database to store the session information. Add some session Id to the response and require the client to pass this id back into all subsequent request along the session. Then execute a query to the Database by this Id and retrieve all the session information.
This solution also doesnt really scale well, but then you can shard the session information if the db permits to do so...
Use Redis/Aerospike to save the session information of the currently connected user. somewhat like DB approach, but since redis run in-memory it will be much faster. In general, this approach can be used in conjunction with 1 where redis is an in-memory cache.
Encrypt the session information or even just sign cryptographically and send back to client. Client will have to supply this information along with the request without knowing which server will actually serve this request.
Without delving into cryptography I'll just state that encryption can be done if you don't want client to see the session information (despite the fact that this is the user whose information is supplied) and signature is used to prevent tempering the data (while sending it back to server).
The data can be supplied to server from client via Header or cookie for instance.

Implement multi tenancy in a stateless environment using Spring and Hibernate and Tomcat

I’m trying to understand how we build a RESTful API for a SaaS product where it involves multi tenancy. The technology stack is Java using Spring and Hibernate and deploying a WAR to Tomcat.
My main issue is how do we maintain the tenant_id within the REST call in order for the application to use the correct database connection when performing CRUD. Seeing that Tomcat uses a thread pool and reuses threads we should not use ThreadLocal.
I have read that slf4j supports this with the MDC implementation for logging. A servelet filter does maintain the tenant_id upfront and clears it upon exit of the filter. Therefore, the logger uses the correct tenant_id in the messages.
At the same time using a ThreadLocal goes against the principle of stateless as this implicitly adds a state.
Furthermore, the idea of creating some kind of ContextSession object holding the tenant_id and passing it around does not seem to solve my issue. As this object would be passed down the layers of the DAL and DAO to load the objects. I want to avoid this high coupling on this ContextSession class, as well having to include it on many method signatures.
How do I implement multi tenancy in a stateless environment?
When a user logs onto your application, they would be somehow associated with a tenant. So the tenant information should be accessible from the spring SecurityContext.
As an example, if i have an application with 2 tenants; tenantA and tenantB. When logging on, the user needs to indicate somehow which tenant they belong to. This can be done in various ways, such as using different host names (eg. tenantA.myapp.com, tenantB.myapp.com) or url parameters, or entering the tenant information on a logon form. When authenticating the user, you then need to use the authentication realm associated with the particular tenant. As part of the authentication, the spring SecurityContext should be setup to contain information that lets you determine which tenant the user belongs to on subsequent service calls from the user. The SecurityContext should be accessible from different service layers of your application without you needing to explicitly program anything for that to happen.
There are a variety of ways to associate your current HttpSession to a specific tenant.
Provide some type of client_id parameter on the URL that is used to reverse lookup the specified backend tenant through a centralize common client database.
Associate an authenticated user to a specific tenant. When users authenticate against a centralized user database, the tenant is looked up based on the user account.
How that tenant_id is passed to the lower application tiers is really a matter of taste.
My first option would be to use a ThreadLocal. If you're already using Spring Security, you're already using ThreadLocal variables and may not even be aware of it.
The use of ThreadLocal variables do not break the stateless design of your application. You just have pieces of code that expect a variable to contain a specific value that it must use. It's just a fancy way to pass state across your application tiers without actually explicitly passing it as arguments on a method signature.
Obviously the other two options are to use some Context object or merely pass the value directly downstream.
In a web application, I would typically do this via some interceptor or filter that would per request lookup the request parameter client_id and get the associated tenantId and set that in a special ThreadLocal variable.
Once the call to chain.doFilter( request, response ); has returned or throws an exception, you simply then clear the ThreadLocal variable.

Avoid session replication in Spring rest application using Cookies

I am working on Spring 4 mvc application with mysql database, tomcat server.
Basically, I am creating a Spring rest which will be used by Angular JS.
Note: I am not using Spring security
In order to avoid session replication in case of clustered environment, I am using cookie approach.On login, I am generating one unique session id (using java UUID) and use it to create a cookie and then set the cookie in the response.Also, storing that session id in database along with any user data.
In order to authenticate every rest API, I have written a Spring interceptor which will intercept every rest API call which in turn check if there is a cookie in the request.If it is present, I am fetching session id value and using it, making a database call to check if it is valid or not. On logout, I am deleting the cookie.
Base on what i am doing as explained above, I have few questions:
1) Is my approach correct? or do you see any flaw in it.
2) Let me know if there is any other better method to achieve the same i.e. to avoid session replication.
3) Since, I am not using any HTTP session, how do I achieve something like session-timeout or do i even need it?
1) Is my approach correct? or do you see any flaw in it.
Its a good approach. Just a couple of points though in order of precedence:-
If you are using your API to service alot of requests then think about using in-memory cache rather then DB. Going to DB is relatively much more expensive. Alot is subjective I know and it depends on your setup, but just consider the DB for data you want to live beyond sessions. Better to use a more temporary/faster store such as an in-memory cache for things like API tokens. If across a cluster then explore a distributed cache solution.
Using cookies is not necessarily a security risk but have a bit of a read about CSRF. More secure to pass the token in a HTTP Header rather than in the cookie itself - that is if you are concerned about CSRF (I do use the Header approach in my own apps but I think CSRF is relatively rare and it depends on how sensitive your data is)
2) Let me know if there is any other better method to achieve the same
i.e. to avoid session replication.
Nothing to add over response to 1.)
3) Since, I am not using any HTTP session, how do I achieve something like session-timeout or do i even need it?
Store (preferably in cache) a timestamp with the token, and refresh it in the cache for each transaction that uses the token. Then, when checking if you consider if the token is valid, check the timestamp and you can decide (based on time elapsed) if you wish to remove the token and request the client to re-authenticate.

Storing progress information in session bean

We are running 4 instances of tomcat server in 2 geo-locations with DNS-based load balancer. We are providing some export services which tasks require a lot of time to complete. So the user typically submits a request to one of our servers and that is responsible for whole processing.
The problem shows up when the user is later requesting for progress information on a randomly chosen instance.
I was thinking about sharing progress information across all instances such as a spring authentication is shared using Redis. I've tried to use spring session bean with aop proxy for sharing progress information, but it seems to be a bad idea. I did some research and debugging that shows the bean is stored in Redis and can be accessed by all instances, but the state is no longer updated. I believe that's because the thread is not able update session information after the original request is return to caller.
Another solution I could think of, is to use our MySQL database for storing such information, but i'm afraid of huge overload caused by continual updating of progress information.
Any ideas to solve my issue?
Thanks in advance,
Michal
OK, I've finally resolved my issue. I was thinking hard about persisting a progress information in session, that was not available when processing an asynchronous task, and I've completely overlooked the fact it is not a good idea:-)
My new solution is as simple as it could be. The unique task id is generated when user requests for task processing and returned to client. Progress information is continuously updated in redis under defined task key, so that the task id could be later used when client is requesting a task state. There is no need for using session because the redis instances are synchronized by replication itself.
Thanks everyone for comments!
Regards,
Michal

Application Session Stores on CloudBees

I'm trying to fully understand when/how to use CloudBees' notion of application session stores.
After reading that article, my understanding of them is that they provide a way to make multiple instances of your appserver share persistent session variables. That way, you can load balance between each server, and it doesn't matter which instance your user gets redirected to; each server instance has the same shared session states (for all users).
So first, if my understanding is incorrect or slightly misinformed, please begin by correcting me!
Assuming I am more or less correct, then I have the following questions:
How does this hurt performance or memory utilization? I would imagine that if each server has to share the same cached session data for all users, it would get kind of bulky and perhaps even pricy...
Is there a way to programmatically CRUD session states (i.e. flush the cache, inspect it with JMX, etc.)?
Can someone provide a concrete use case for this feature, just so I'm positive I "get it"?
Thanks in advance!
Session store let you replicate the HttpSession around the cluster, so that you can scale-out application load on multiple instances, without having to stick a user to a specific server (this is the other option you can use : configure sticky session)
By nature, serializing HttpSession data has some impacts on performance, but as it uses a memcache backend this is minimal. This also mostly depends on the amount and size of objects you store in user session. Also, please note this require you explicitly put object in HttpSession after any attribute change, even the object was retrieved from session - this is a general API issue with JavaEE clustering.
You can't programmatically query the session store (you could with the adequate memcache client code, but this doesn't make much sense)
Two concrete use cases
application distributed on multiple nodes, with session distributed around the cluster, and without sticky session so that load is actually distributed for ALL user
Application te be redeployed can't suffer user session to be lost, to ensure a continuous 0 downtime service. version N+1 application will then retrieve httpsession data from version N, need to be compatible from serialization point of view, but connected user won't notice the redeployment and version upgrade

Categories

Resources