Spring session with redis - lost Principal - spring-boot

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.

Related

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.

Spring boot Redis session does not survive the browser restart

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?

Should oauth2 Resource Server Interrogate Userinfo Endpoint on Authentication Server

When creating a resource server to protect my api endpoints in spring boot I am using spring-boot-starter-oauth2-resource-server and it does not try to pull back the claims from the userinfo endpoint on the authentication server. I am wondering if this is expected behavior and if so should I be using another library to setup spring security for my resource server? It appears debugging that this module pulls in the info from the well-known and should be able to easily know the userinfo endpoint.
This is the current dependencies that I am using maybe I am just missing some module that I am not aware of.
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>openid-resource</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>openid-resource</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
NatFar's answer is right on the money, but I thought I'd add some color that I couldn't fit into a comment.
Indeed, Resource Server is about authorization, but the API provides hooks for you to be able to customize this, calling a userinfo endpoint being among them.
As of Spring Security 5.1:
#Override
protected void configure(HttpSecurity http) {
http
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(new MyConverter());
}
private static class MyConverter
implements Converter<Jwt, AbstractAuthenticationToken> {
#Override
public AbstractAuthenticationToken convert(Jwt jwt) {
// invoke the userinfo endpoint
// construct an Authentication statement from the response
}
}
Spring Security 5.1 only supports JWT, however in Spring Security 5.2 (which GAs in a couple of weeks) it supports opaque tokens as well. It also generalizes the representation a bit:
#Override
protected void configure(HttpSecurity http) {
http
.oauth2ResourceServer()
.opaqueToken()
.introspector(new MyIntrospector());
}
private static class MyIntrospector implements OpaqueTokenIntrospector {
#Override
public OAuth2AuthenticatedPrincipal introspect(String token) {
// invoke the userinfo endpoint
// construct an OAuth2AuthenticatedPrincipal from the response
}
}
I've added a ticket to get documentation added around your usecase; however, the JWT-introspection example that's already there is fairly close.
Check out what the Spring reference says about the resource server:
It’s atypical for a resource server to need to call a user info endpoint. This is because, fundamentally, a resource server is about authorizing a request, not authenticating it
Usually, it's the client application that queries the user info endpoint for more info about the user.
But the reference proceeds to show how to configure the resource server to call the user info endpoint if you're using the old Spring Security OAuth.
However, in Spring Security 5, it appears that you're only able to use the user info endpoint via .oauth2Client() or .oauth2Login().
The reference states that it's the client the makes a request for the user info.

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.

How to block Cassandra from trying to connect automatically

I have this project where I'm trying to connect to different DB types based on configuration.
I have it working for Mongo and MySQL and switch by loading Beans by using #ConditionalOnProperty(name = "settings.data.source", havingValue = "mongodb")
Now I want to add Cassandra, but once I added the following dependency to my pom, it starts trying to connect to Cassandra nodes on localhost.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra</artifactId>
</dependency>
I want to have more control over when the Cassandra resources are loaded.
It doesn't try to connect automatically when I added Mongo dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
Anyone familiar with this behavior? How can I control this? I don't always need a Cassandra connection...
You may disable Cassandra auto configuration,
#SpringBootApplication
#EnableAutoConfiguration(exclude={CassandraDataAutoConfiguration.class})
Yep, That's it, thanks!
#EnableAutoConfiguration(exclude={
CassandraDataAutoConfiguration.class,
MongoDataAutoConfiguration.class,
MongoRepositoriesAutoConfiguration.class,
MongoAutoConfiguration.class})
For both my #SpringBootApplication and #Configuration classes.

Resources