Change session ID with cluster redis - spring-boot

I have a spring boot project which uses webflux and spring session with redis (cluster mode) for session management.
I am trying to change the session ID when user state changes using
webSession.changeSessionId()
This results in a crossslot keys in request don't hash to the same slot error from redis.
How to overcome this issue?
Everything obviously works fine with local standalone redis.
Code to change session ID:
return Mono.justOrEmpty(sessionObj).flatMap(n -> {
sessionObj.set... // some change in values
setSessionAttribute(webSession, servicesSession);
return webSession.changeSessionId();
});

Related

Redis spring session bean not updated

I am trying to switch my http session to redis in my spring boot application.
When the first request comes to backend it's being filtered by authentication filter.
One duty of this filter is to populate user session bean with data. The session is succesfully saved to the redis instance at this step, but the delta of changes ( which should include the session bean) is not invoked. I want to point out that with storing session on tomcat the session beans work correctly.
So why session bean populated on OnePerRequest filter is not updated as the delta of session ?
Have you tried the below configuration?
#Configuration
#EnableRedisHttpSession(saveMode = SaveMode.ALWAYS)
public class RedisSessionConfig {
}
Try changing the flush mode to IMMEDIATE, by default it's ON_SAVE which means you explicitly have to save the session or in a managed environment, it happens before the response is serialized (I think).
In src/main/resources/application.properties you could do:
spring.session.redis.flush-mode=immediate
Or using #EnableRedisHttpSession do:
#EnableRedisHttpSession(redisFlushMode = RedisFlushMode.IMMEDIATE)

Spring boot + spring security + hazelcast session replication could not make it work

I am trying to introduce spring security to angularjs app. Back end uses spring framework . I used the methodology explained here for back end security.
https://samerabdelkafi.wordpress.com/2016/01/25/secure-angularjs-application-with-spring-security/
This is working fine with single app instance.
The problem is application is clustered and therefore sessions must be replicated .
I tried to use hazelcast for session replication as explained here:
https://dzone.com/articles/spring-boot-hazelcast-for-session-replication
When I introduce hazelcast , first authenticate is successful. After that the first request is also successfull. But after that it seems that
org.springframework.security.web.context.HttpSessionSecurityContextRepository can not find the session...
As I said this start to occur after I configure com.hazelcast.web.WebFilter for sesion replication as below:
#Bean
public WebFilter webFilter(HazelcastInstance hazelcastInstance) {
Properties properties = new Properties();
properties.put("instance-name", hazelcastInstance.getName());
properties.put("sticky-session", "true");
return new WebFilter(properties);
}
Here are the related logs:
2017-08-22 15:17:31,593 : [DEBUG] [http-nio-7023-exec-2][HttpSessionSecurityContextRepository] No HttpSession currently exists
2017-08-22 15:17:31,593 : [DEBUG] [http-nio-7023-exec-2][HttpSessionSecurityContextRepository] No SecurityContext was available from the HttpSession: null. A new one will be created.
I am sure that client sends the same cookie after successfull login
I could not figure out the reason. Any guidance is appreciated.
I could make this work by using spring session..
https://docs.spring.io/spring-session/docs/current/reference/html5/guides/java-hazelcast.html
The sessions are successfully replicated between two instances behind a round robin load balancer. Many thanks to spring session developers...

Spring Cache is not cleared on production

I am using spring cache mechanism with SimpleCacheManager/ConcurrentMapCache.
And I am using a web service to clear the cache and the following is the code .
for(String cacheName : cacheManager.getCacheNames()){
Cache cache =cacheManager.getCache(cacheName);
if(cache!=null){
cache.clear();
}
}
When I called this code from a Rest webservice on local vm , I can see its clearing the cache and can see the changes that we done in the database with other service , However on the production environment , the webservice returning 200 status in the logs. but it still shows the old data.
On production we have 2 servers
We have to restart our application to refresh the cache and get the latest data from the database.
I used to do this creating a void method annotated with #CacheEvict(allEntries=true), this annotation is similar to #CacheRemoveAll from JSR-107.
Something like that:
#CacheEvict(allEntries=true)
public void evictAll() {
// Do nothing
}
I know, it's ugly, but works to me.
My two cents, avoid to use the default spring cache manager in production, use a cache manager more sophisticated instead like Guava or EhCache.
Cheers.

Configure Cookie Domain in spring session

So I already success implement SSO using spring session and redis on development localhost domain.
But when I deploy to server using two sub domain.
login.example.com
apps.example.com
They always create new session Id on each sub domain.
I already try to configure using Context in tomcat configuration.
<Context sessionCookieDomain=".example.com" sessionCookiePath="/">
But no luck.
Spring session moves the session management on application level, so no surprise that trying to configure the container (in your case tomcat) has no effect. Currently there is a TODO in spring-session code to allow setting the domain, but is not implemented.
Maybe it is best to open an issue to allow setting the domain or comment/vote on https://github.com/spring-projects/spring-session/issues/112.
Meanwhile a workaround would be to go with your own implementation of MultiHttpSessionStrategy based on CookieHttpSessionStrategy.
Finally I succeeded to setdomain on application level.
You're right, I hope in the future they implement the feature to set domain.
For now I create CustomCookieHttpSessionStrategy for my own implmentation.
private Cookie createSessionCookie(HttpServletRequest request,
Map<String, String> sessionIds) {
...
sessionCookie.setDomain(".example.com");
// TODO set domain?
...
}
And then register bean as HttpSessionStrategy.

How to update multiple spring config instance clients

Spring cloud config client helps to change the properties in run time. Below are 2 ways to do that
Update GIT repository and hit /refresh in the client application to get the latest values
Update the client directly by posting the update to /env and then /refresh
Problem here in both the approaches is that there could be multiple instances of client application running in cloud foundry and above rest calls will reach any one of the instances leaving application in inconsistent state
Eg. POST to /env could hit instance 1 and leaves instance 2 with old data.
One solution I could think of is to continuously hit these end points "n" times using for loop just to make sure all instance will be updated but it is a crude solution. Do any body have better solution for this?
Note: We are deploying our application in private PCF environment.
The canonical solution for that problem is the Spring Cloud Bus. If your apps are bound to a RabbitMQ service and they have the bus on the classpath there will be additional endpoints /bus/env and /bus/refresh that broadcast the messages to all instances. See docs for more details.
Spring Cloud Config Server Not Refreshing
see org.springframework.cloud.bootstrap.config.RefreshEndpoint code hereļ¼š
public synchronized String[] refresh() {
Map<String, Object> before = extract(context.getEnvironment()
.getPropertySources());
addConfigFilesToEnvironment();
Set<String> keys = changes(before,
extract(context.getEnvironment().getPropertySources())).keySet();
scope.refreshAll();
if (keys.isEmpty()) {
return new String[0];
}
context.publishEvent(new EnvironmentChangeEvent(keys));
return keys.toArray(new String[keys.size()]);
}
that means /refresh endpoint pull git first and then refresh catch,and public a environmentChangeEvent,so we can customer the code like this.

Resources