Spring boot Redis session does not survive the browser restart - spring

I am using Redis as session storage (running in docker container) for my Spring Boot application (2.1.7).
In application.properties are these records:
spring.session.store-type=redis
server.servlet.session.timeout=7d
spring.session.redis.flush-mode=on_save
spring.session.redis.namespace=spring:session
spring.redis.host=localhost
spring.redis.port=6379
server.servlet.session.cookie.secure=true
server.servlet.session.cookie.http-only=true
The Redis is "working". So there is functional session storage.
But the problem is that session expiry time on cookie is like this:
And If I close the browser and reopen the tab, I am not logged (and I have a new session identifier).
And it does not take effeck if I am logged as user, logged as admin (there is some listener listed below handling expiry time differently) or as guest (not logged user). Every time I restart browser, I have a new session identifier.
The usual "TTL" for session is 7 days (as describer in property file), but for admin users there is time redured to 1 hour
if (authentication.getPrincipal() instanceof LoggedInPersonDetails) {
LoggedInPersonDetails loggedInPersonDetails = (LoggedInPersonDetails) authentication.getPrincipal();
loggedInPersonDetails.getAuthorities().stream()
.filter(authority -> !Authority.WEB_ENTER.getName().equals(authority.getAuthority()))
.findAny()
.ifPresent(authority ->
session.setMaxInactiveInterval((int) nonStandardUserSessionTimeout.getSeconds()));
}
Dependencies in pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
None of this seems to be working. I switched from "classic" (non redis) spring boot session handling where behav was as expected. So there must be some error in spring boot to redis configuration.
EDIT: I tried to add the
server.servlet.session.cookie.max-age=7d
property but it does not seem to work. Expiry on cookie is still set to Session
EDIT2: I´ve figured out how to set expiration of cookie, I´ve registerd DefaultCookieSerializer bean in my Mvc config and set maxAge property to some value.
#Bean
public CookieSerializer defaultCookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieMaxAge(60 * 60 * 24 * 7);
return serializer;
}
But a new problem occures. The cookie expiry date is not updated as I am active on web. For example when expiry date is set to 12:00 and I reload the page at 11:58, the cookie expiry date should be renewed to now() + default maxAge.
I found some solution about implementing custom interceptor but that does not seem like a right solution to me. The spring-session has really not some functionality to do this?

Related

Spring Boot 3 Redis Indexed Session - Failure to save login

I am upgrading my team's Spring Boot project from 2.7.5 to 3.0.0, which includes a migration of the now deprecated WebSecurityConfigurerAdapter. I am working in Kotlin, but I do not believe the language impacts the performance of the component.
In both the old and new versions of the security configuration class, I have declared this Autowired field and corresponding bean:
#Autowired
private lateinit var sessionRepository: FindByIndexNameSessionRepository<out Session>
#Bean
fun sessionRegistry(): SessionRegistry {
return SpringSessionBackedSessionRegistry(sessionRepository)
}
At first, my application would not start, because Spring could not wire the session repository into my configuration, but after some digging, it was because I had to change the #EnableRedisHttpSession annotation to
#EnableRedisIndexedHttpSession
class SecurityConfiguration(
Now, my current issue with the application is that I cannot log into my application, and I think it has to do with the Redis session management.
I am able to load the login page, and submit a login request. When I submit the request, I am able to see all of the normal debug messages my application prints associated with logging in, taking me through a successful authentication. When all of that finishes, my application redirects the user to the main page, which checks if a user's authentication != null, and redirects them to the main page if so. However, the authentication at this point is null, and there is not an entry in redis for this user's authentication.
In case it helps, this is what the security configuration had defined in the SecurityFilterChain bean for the session parts before the migration:
.sessionManagement().sessionFixation().migrateSession().and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED).maximumSessions(1).sessionRegistry(sessionRegistry()).expiredUrl("/api/v1/logout").and()
and after:
.sessionManagement { ssm ->
ssm.sessionFixation { it.migrateSession() }
ssm.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.maximumSessions(1).sessionRegistry(sessionRegistry()).expiredUrl("/api/v1/logout")
}
Is there something I am missing to allow the user authentication to persist? I have not touched any part of the code for this migration besides the pom.xml file and the rewriting of the security configuration.
UPDATE: I have altered the tags of the security configuration, in a desperate attempt at getting something to save.
#Configuration
#EnableWebSecurity
#EnableSpringHttpSession
#EnableRedisIndexedHttpSession(redisNamespace = "admindev:session", flushMode = FlushMode.IMMEDIATE, saveMode = SaveMode.ALWAYS)
class SecurityConfiguration(
I have increased the logging level for spring security, and everything looks fine, until the session token is not found.
Update 2:
I have tweaked my application as I have described in my answer to this post, and I am now able to access the frontend of my application. However, when I attempt to login through the backend, I get caught up in an infinite redirection loop. The app tries to send me to the error/4xx page, which is the correct behavior, since we don't have a "/" link on the backend, but then the app tries to redirect me according to the "your session has expired" page, and since I'm logged in, it tries to redirect me back to "/"...
Looking through the logs, it seems to be related to the exception handling I set up on my filter chain:
http.exceptionHandling { e -> e.authenticationEntryPoint(LoginRedirectHandler()) }
//...
class LoginRedirectHandler : LoginUrlAuthenticationEntryPoint("/login") {
override fun determineUrlToUseForThisRequest(
request: HttpServletRequest,
response: HttpServletResponse,
exception: AuthenticationException?
): String {
return "${this.loginFormUrl}?error=timeout&requestedUrl=${request.requestURL}"
}
}
Now, the question becomes: the backend webpages were not looping before, what could have changed about this behavior?

Redisson doesn't set TTL or cache name correctly

I'm creating a Spring application that uses Redis cache via redisson client.
#Bean
public CacheManager cacheManager(RedissonClient redissonClient) throws IOException {
Map<String, CacheConfig> config = new HashMap<String,CacheConfig>();
config.put("employeesCache", new CacheConfig(24*60*1000, 12*60*1000));
RedissonSpringCacheManager manager= new RedissonSpringCacheManager(redissonClient, config);
return manager;
}
However when running this application the cache name created in Redis is {employeesCache}:redisson_options instead of just employeesCache.
Also, when I check for the TTL in the Redis CLI it returns (integer) -1 ,meaning it has not been set.
So the RedissonSpringCacheManager is partially functioning, it creates the cache but without any configuration, can you help me fix it.
I'm using the following Maven dependencies
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.13.1</version>
</dependency>
Redisson use a map to save your data, but keys in the map are not support TTL, so Redisson maintain a {employeesCache}:redisson_options to save the configurations, your employeesCache keys is maintained and deleted by redisson, NOT redis
So, your data will be saved in a map called employeesCache, not in {employeesCache}:redisson_options, just leave it alone.

Start spring-boot project without datasource configuration on application.properties

I have a spring-boot project where the database configuration should be saved in the first time the user execute the application, after ask for the connection data (server url, username and password). the application.properties file embbed in the WAR file of the application should be something like that:
security.basic.enabled=false
# THYMELEAF (ThymeleafAutoConfiguration)
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html
spring.thymeleaf.cache=false
# Multipart Configuration
spring.servlet.multipart.maxFileSize = 150MB
spring.servlet.multipart.maxRequestSize = 150MB
spring.servlet.multipart.fileSizeThreshold = 5MB
# Setting max size of post requests to 6MB (default: 2MB)
server.tomcat.max-http-post-size=157286400
After the first run, a second application.properties should be created with the database configuration. But right now, when I try run the project with the configuration above, it do not start, showing the following error message:
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
Action:
Consider the following:
If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).
without give the application the change to generate the second configuration file, with the database properties.
How I could force the application to run this first time, even without the database stuff?
update
Ok, then I get rid of this error message by keeping this 2 dependencies on my pom.xml:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.4.1</version>
</dependency>
and now the applications runs without error, and I am able to open the installation page to create the database on the postgresql server and import the initial data (user data included).
But I notice that this initial data is not being stored on the postgresql server (despite the datababse and all the tables are being created normally). I suspect they are being created on the hyperSql database on memory, what do not help me at all.
My guess is that's happen because I autowire some Dao classes for this entities on my InstallService class, and the code only create the data on the postgresql where I directly make the connection with this specific server.
Considering the method responsible by populate de initial data on my database is something like that:
public void criarUsuario(String user, String pass, String nome, String sobrenome, String email) {
Usuario novo = new Usuario();
novo.setUsername(user);
novo.setPassword(pass);
novo.setFirstName(nome);
novo.setLastName(sobrenome);
novo.setEmail(email);
novo.setEnabled(true);
novo.setLocked(false);
novo.setCredenciais(new ArrayList<Credencial>());
novo.getCredenciais().add( credencialDao.findBy("nome", "admin") );
usuarioDao.insert(novo);
...
}
What I could change here to persist this data on the postgresql database, instead of the hyperSql database?
update 2
Now I managed to run the application even with only the postgresql dependency om my pom.xml. I did that by adding a initial configuration on my first application.properties (locatd on /src/main/resources):
spring.datasource.driverClassName=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/appdata
spring.datasource.username=...
spring.datasource.password=..
spring.datasource.continue-on-error=true
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.show-sql=false
spring.jpa.hibernate.ddl-auto=none
spring.jpa.generate-ddl=false
But when I try access the application on the browser, I got an error saying the database not exist (of course not, when I run the application the first time I have a specific flow to create database, tables and populate initial data).
is there any way to supress this "database not exist" error when I run the project?

Spring basic security confiugred userid/password not working

Our application is based on Spring-boot 2.0. I've enabled basic security by adding the following dependency to pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
I also have added properties so that I can define my own userid and password for basic security, instead of the generated ones. I defined them like this in /resources/applicaiton.properties file:
security.user.name=user1
security.user.password=pass1
When I startup my application, I can see that is still generates the password for me in the log. Also, I am unable to login using user1/pass1 combination. I can only successfully login with the user=user and password=generated-password-from-log file.
Why won't spring security allow me to login with user1/pass1? What could be the problem?
Those properties need the spring prefix.
spring.security.user.name=user # Default user name.
spring.security.user.password= # Password for the default user name.
If I want to configure something I often take a look at this List
I hope this helps.

Spring session with redis - lost Principal

guys.
I migrated to Spring session with Redis implementation. I'm using spring boot and the only thing that i've made to start with this implementation is to add this
<!-- Spring Session with Redis -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- -->
in pom.xml and this in application.properties.
#Redis for session persistent
spring.session.store-type=REDIS
spring.redis.host=localhost
spring.data.redis.repositories.enabled=false
Everything is working fine, except that from time to time, the Principal (authneticated user is lost). I still have access to restricted pages, but Principal obejct is null. This leads to corrupted data, because I track the entries created from the speceific user.
Whan of the methods that have problem is:
public void addSamples(Integer distributorId, String articleNumber, Integer quantity, Principal user) {
Distributor distributor = distributorRepository.getOne(distributorId);
Tile tile = tileRepository.findByArticleNumber(articleNumber);
Merchandiser merchandiser = merchandiserRepository.findByUsername(user.getName());
Samples samples = new Samples();
samples.setMerchandiser(merchandiser);
samples.getSamplesPK().setDistributor(distributor);
samples.getSamplesPK().setTile(tile);
samples.setQuantity(quantity);
distributor.getSamples().add(samples);
distributorRepository.save(distributor);
}
I'm still logged and have access, but the merchandiser object is null..
What can be the reason for this ? Any help will be usefull.
Best regards.

Resources