I am using Infinispan caching as session scoped bean to cache user related data objects in a Spring MVC application.
Now we migrate to spring boot and we want to use #enableRedisHttpSession
but we face the problem that the Infinispan CacheManager attached to the session is not Serializable, producing the following exception:
java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [org.infinispan.spring.provider.SpringEmbeddedCacheManagerFactoryBean]
org.springframework.core.serializer.DefaultSerializer.serialize(DefaultSerializer.java:43)
org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:63)
org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:35)
org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.serialize(JdkSerializationRedisSerializer.java:50)
org.springframework.data.redis.core.AbstractOperations.rawHashValue(AbstractOperations.java:166)
org.springframework.data.redis.core.DefaultHashOperations.putAll(DefaultHashOperations.java:128)
org.springframework.data.redis.core.DefaultBoundHashOperations.putAll(DefaultBoundHashOperations.java:85)
org.springframework.session.data.redis.RedisOperationsSessionRepository$RedisSession.saveDelta(RedisOperationsSessionRepository.java:409)
org.springframework.session.data.redis.RedisOperationsSessionRepository$RedisSession.access$000(RedisOperationsSessionRepository.java:331)
org.springframework.session.data.redis.RedisOperationsSessionRepository.save(RedisOperationsSessionRepository.java:211)
org.springframework.session.data.redis.RedisOperationsSessionRepository.save(RedisOperationsSessionRepository.java:141)
org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.commitSession(SessionRepositoryFilter.java:193)
org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.access$100(SessionRepositoryFilter.java:169)
org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:127)
org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:65)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:103)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
Typically we will balance user requests to multiple nodes so we need to make the cache shared between the nodes (using Redis store).
can any one help.
Obviously the cache manager is not serializable as it should not be serialized - it's not data. You have to track the field that references the SpringEmbeddedCacheManagerFactoryBean and make that transient.
You should never store an Infinispan CacheManager (or a Cache) in the session. Make it a singleton.
Infinispan is able to handle replication without the need of Redis, just make sure you enable the right Cache Mode (for example 'replicated' or 'distributed') and your user data will be available from all other nodes without needing to store the cache in Redis.
Related
To my understanding,
when a session is initialized for some user,
the session gets a hash and session_id which identifies it.
ex. session[key]=value
session_id = 23f8fzsj2048j20j
Now, when logging out a user, I know you can simply set
session[:user_id] = nil
But what happens to the actual session hash?
If I'm not wrong, if there is User A, B, and C, they each get assigned a unique session, for example with User A with session_id = 12345abc, User B with session_id = 23456abc, and so on.
Does this not create infinite amount of session hashes then?
Are they garbage collected when they do not get used?
This depends entirely on the mechanism you're using to store hashes, but the short answer is: No, sessions are not garbage-collected, and they generally don't need to be.
Sessions are (by default) stored using CookieStore. There is no server-side data, all of the cookie data is encrypted and stored in a cookie. The user's browser is responsible for clean-up.
You can use alternative session storage engines, such as ActiveRecord sesssion store which stores session data in the database. Doing this requires you to manually choose when to consider a record "expired", and to implement your own clean-up code, again, they are not automatically "garbage collected": How does Rails know when to delete a record from the `sessions` table?
I am not sure what you mean by session_id, but by default sessions are using a cookie that is saved in your users' browser.
If you'd like more reference for the code involved I suggest you read this, https://github.com/rails/rails/blob/3ac3760c69e6e6914c5ddae138856b3c82ac0f20/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb, and then go on and see the Rack implementation for cookies.
As for nullifying a session in the context of logging a user out, most authentication frameworks check for a special cookie with a user id that is set once a user has logged in and when that value is not found in the cookie your are essentially logged out.
The hash itself which gets initialized in the request cycle lives in memory and will be garbage collected at the end of the request-response cycle, in a very naive way, your hash lives in the heap and gets garbage collected.
The data that it is referencing to lives in your browser's cookiejar and has it's own mechanisms for cleaning it.
Here's my question: I'm writing a platform which I will be giving to the customers to implement their projects with. So in my platform I have created a SessionService in which I have methods like getCurrentSession, getAttribute, setAttribute, etc. Before spring-session my getCurrentMethod looked like this:
#Override
public HttpSession getCurrentSession() {
if (this.session == null) {
final ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
return attr.getRequest().getSession(true); // true == allow create
}
return this.session;
}
which worked perfectly fine, although it looks ugly and have no backing like redis. Now I want to migrate to spring-session and I was hoping to use the SessionRepository to find the current session of the user, however I can only see a getSession(String id) in there. I believe the id is stored in the cookie, so to use it I will probably have to pass the HttpServletRequest object from my controllers, to my facades, to the service layer which is very near the db layer. This looks like a very bad idea to me, so my question would be: is there any way to get the currentSession near the db layer? One way I would think is to write an interceptor that will be invoked the controllers, which will set the current session in the repository, or the service maybe? I'm just not sure this is the right way to go.
Obtaining the Session Id from Service Layer
You can use the RequestContextHolder to retrieve the session id, set attributes, and remove attributes.
The RequestContextHolder is typically setup using RequestContextListener or RequestContextFilter. Spring Session does NOT work with RequestContextListener because there is no way for Spring Session to wrap the request before the RequestContextListener is invoked.
Unfortunately, this means for Spring Boot applications, RequestContextHolder does not work out of the box. To work around it you can create a RequestContextFilter Bean. See spring-boot/gh-2637 for updates on this issue.
Should I be putting this in session?
Just because it is easy to put a lot of objects in session and it is stored in Redis does not mean it is the right thing to do.
Keep in mind that the entire session is retrieved on every request. So while Redis is fast, this can have a significant impact if there are lots of objects in session. Obviously the implementation can be optimized for your situation, but I think the concept of session generally holds this property.
A general rule of thumb is, "Do I need this object for over 95% of my requests?" (read this as almost all of my requests). If so, it may be a candidate for session. In most cases, the object should be security related if it fits this criteria.
Should I access session id from ThreadLocal in the service layer?
This is certainly open for debate as code is as much of an art as it is a science.
However, I'd argue that you should not be obtaining the session id from thread locale variables throughout your architecture. Doing this feels a bit like obtaining a "Person id" and obtaining the current "Person id" from the HttpServletRequest in a ThreadLocale. Instead, values should be obtained from the controller and passed into your service layer.
Your code does not need changing. It will return the Spring Session session object.
Though it is generally better to inject the HttpSession from the controller, or use session-scoped beans and #SessionAttribute than to have such a session service in the first place.
it is famous to get the current user by calling :
springSecurityService.currentUser ;
Does Spring Ssecurity API save this object in HttpSession. if So, how to access to this object from session .
i.e: session['currentUser']
It doesn't.
As you showed in your answer the Principal is stored in the session, but that's the org.springframework.security.core.userdetails.UserDetails instance that was created by the org.springframework.security.core.userdetails.UserDetailsService. The default implementation of that in the plugin is grails.plugin.springsecurity.userdetails.GrailsUser but that's easily customized.
The UserDetails instance is typically a lightweight object, just containing the username and hashed password, a few locked/enabled booleans, and a collection of GrantedAuthority instances to store role names. I often recommend that users extend this to also contain data that's useful but unlikely to change during a login session, e.g. full name, to avoid going to the database to retrieve it. Since the UserDetails is stored in the session and easily accessible via springSecurityService.principal it's a great place to store data like this.
But it is not the same thing as what's returned from getCurrentUser()/currentUser - this is the GORM user/person domain class that was loaded by the UserDetailsService to create the UserDetails instance. It can have a lot more data associated with it, lazy-loaded hasMany collections, etc. It's often a rather large object that should not be stored in the session. Doing so does make its data conveniently available, but will affect scalability because you waste server memory and limit the number of concurrent sessions a server can have. And it's a disconnected Hibernate object, so to use it for most persistence-related actions requires that you reload the instance anyway, often with merge(). That loads the whole thing from the database, so it's a lot more efficient to store the extra data you need in the UserDetails, along with the id of the instance so you can easily retrieve the instance as needed. That's what getCurrentUser()/currentUser does - it uses the id if it's available for a get() call, or the username for the equivalent of a findByUsername() call (which should be around the same cost if the username has a unique index).
After HttpSession inspecting session.getAttributeNames(), i want to share my result :
session.getAttribute('SPRING_SECURITY_CONTEXT').authentication.principal
I am not able to understand what is the function of this line in web.xml
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>server</param-value>
</context-param>
I have read that the NetBeans default is client. I've just faced an issue that I have many beans in my application, and the <param-value> was set to client, so I was getting
java.io.NotSerializableException
error although my beans were Serializable (i.e. they implemented the Serializable interface.). My beans were in #ViewScope. But when I changed it to server, things are going to work. Why? What is the difference when I use client and server. Can anyone explain me with the help of an example.
Thanks
java.io.NotSerializableException
This kind of exception has usually a message in the root cause which shows the fully qualified class name of the class which doesn't implement Serializable. You should pay close attention to this message to learn about which class it is talking about and then let it implement Serializable accordingly.
Often, making only your managed bean classes serializable is not always sufficient. You also need to ensure that each of its properties is also serializable. Most standard types like String, Long, etc implement all already Serializable. But (custom) complex types such as nested beans, entities or EJBs should each also be serializable. If something is not really implementable as Serializable, such as InputStream, then you should either redesign the model or make it transient (and keep in mind that it will be null after deserialization).
What is the difference when i use client and server
First some background information: Why JSF saves the state of UI components on server?
The main technical difference is that the client setting stores the entire view state as the value of the javax.faces.ViewState hidden input field in the generated HTML output and that the server setting stores it in the session along with an unique ID which is in turn referenced as the value of the javax.faces.ViewState hidden input field.
So, setting to client increases the network bandwidth usage but decreases the server memory usage and setting to server does the other way round. Setting to client has however an additional functional advantage: it prevents ViewExpiredExceptions when the session has expired or when the client opens too many views.
javax.faces.STATE_SAVING_METHOD parameter is used to specify where the state should be saved.
If you want to save the state on the server (which is the default in the JavaServer Faces reference implementation), specify the param-value value as server.
Otherwise to save the state on the client side we can specify client in the param-value.
If the state is saved on the client, the state of the entire view is rendered to a hidden field on the page.
I'm developing an Asp.net (MVC but this doesn't really matter) application. I have a custom IHttpModule that's responsible for the PostAuthenticateRequest to change user principal & identity.
I'm storing UserID and UserName in authentication cookie when user logs-in. I have an IUser (implemented by DAO and Business Objects layer, each with their own additional members) that I need all over Business Service classes. When a user wants anything I have to provide IUser object instance (usually from Business Objects layer) so providing ID from the auth ticket isn't sufficient.
So I'm thinking of how and where would be best to persist logged in user's IUser data?
I don't want to fetch it every time from the DB (based on authentication ticket's UserID data)
I can't store it in Session since I have to work inside PostAuthenticateRequest, where Session isn't ready yet
I want all the functionality to be encapsulated within my custom IHttpModule
Choices that I see:
Cache
Cookie
(Session) - by moving from PostAuthenticateRequest to PostAcquireRequestState event and change principal/identity there, but I'd like to avoid this
Processes that seem to complicate things are:
User logs-in, user data is fetched from the DB and persisted somehow for later requests
User logs-out, user data has to be removed from persisted medium automagically
User changes own profile, user data has to be discarded and reread on next request from the DB
I wan't all these to be handled automatically by HttpModule (if possible) to eliminate developer's errors of forgetting to reset these things.
What I also don't want is to write/read some hardcoded variables/keys and manipulate them in other parts of the application. This would only present technical debt.
Questions
What would you suggest?
How does SO persist user data between requests?
Given your requirements, I suppose the best solution is to retrieve the ID from the cookie and use it to index into the Http Cache (HttpContext.Current.Cache).
If you want to maintain how users access it, wrap the Cache in a "UserCache" object. The object could be constructed by an HttpModule and stored as a (wait for it...) singleton within the cache itself or, better yet, just constructed when needed to pull from the http cache. This would depend on where you need to access it and whether HttpContext.Current.Cache is directly available. The lazy implementation is below.
Again, this is for clarity and is not how I'd actually implement it.
public class UserCache
{
public IUser GetUser(object userKey)
{
return HttpContext.Current.Cache[userKey];
}
public void AddUser(object userKey, IUser user)
{
/* this could pull the key from the user object as well. */
HttpContext.Current.Cache.Add(/* add the object with key and a sliding expiration that is slightly greater than session timeout */);
}
public void ExpireUser(object userKey)
{
HttpContext.Current.Cache.Remove(userKey);
}
/* If you don't want to do SQL cache dependency */
public void UpdateUser(object userKey, IUser user)
{
HttpContext.Current.Cache.Insert(/* ... */);
}
}
Using the default caching mechanisms (or better yet a caching mechanism supplied by DI so you're not tied to an implementation), you can set an expiration to automatically remove users from the cache as mentioned in the comment. You can setup the cache to be dependent on SQL server updates as well to handle the updates or manually update it as part of the service to save changes.
More information about the default cache is available here. More information about cache dependencies is available here.
In the HttpModule itself, I suppose you could do some magic in the EndRequest event to see if the request is authenticated and then log the user out based on the cookie, but I'm not sure if that would work as I've never tried it. You might want to have a look at this article on MSDN from WAY back in the 1.1 days and see if it answers some of the problems you are trying to solve.
As for the SO architecture and how they do it, I'd imagine they load it when needed because they keep most of the database in RAM at all times (http://highscalability.com/stack-overflow-architecture).