Redis session repository with Jackson cannot serialize BadCredentialException
See original GitHub issueI just migrate my application to spring-boot 2 / Spring 5 and I have difficulties to configure the session cache. I use spring-security, spring-session-data-redis and jackson2. When I use a good login/password, everything works fine. The session is stored in Redis with a Json format. But if I enter a wrong password, the application try to store the BadCredentialException and ActiveDirectoryAuthenticationException in Redis (I have 2 AuthenticationProvider : PreAuthenticatedAuthenticationProvider and ActiveDirectoryLdapAuthenticationProvider).
The problem is that Jackson cannot serialize the BadCredentialException.
org.springframework.data.redis.serializer.SerializationException: Could not read JSON: The class with org.springframework.security.authentication.BadCredentialsException and name of org.springframework.security.authentication.BadCredentialsException is not whitelisted. If you believe this class is safe to deserialize, please provide an explicit mapping using Jackson annotations or by providing a Mixin. If the serialization is only done by a trusted source, you can also enable default typing. See https://github.com/spring-projects/spring-security/issues/4370 for details; nested exception is java.lang.IllegalArgumentException: The class with org.springframework.security.authentication.BadCredentialsException and name of org.springframework.security.authentication.BadCredentialsException is not whitelisted. If you believe this class is safe to deserialize, please provide an explicit mapping using Jackson annotations or by providing a Mixin. If the serialization is only done by a trusted source, you can also enable default typing. See https://github.com/spring-projects/spring-security/issues/4370 for details
at org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer.deserialize(GenericJackson2JsonRedisSerializer.java:132)
at org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer.deserialize(GenericJackson2JsonRedisSerializer.java:110)
at org.springframework.data.redis.core.AbstractOperations.deserializeHashValue(AbstractOperations.java:354)
at org.springframework.data.redis.core.AbstractOperations.deserializeHashMap(AbstractOperations.java:298)
at org.springframework.data.redis.core.DefaultHashOperations.entries(DefaultHashOperations.java:233)
at org.springframework.data.redis.core.DefaultBoundHashOperations.entries(DefaultBoundHashOperations.java:172)
at org.springframework.session.data.redis.RedisOperationsSessionRepository.getSession(RedisOperationsSessionRepository.java:429)
at org.springframework.session.data.redis.RedisOperationsSessionRepository.findById(RedisOperationsSessionRepository.java:398)
at org.springframework.session.data.redis.RedisOperationsSessionRepository.findById(RedisOperationsSessionRepository.java:245)
at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.lambda$getRequestedSessionId$0(SessionRepositoryFilter.java:359)
at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:174)
at java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1351)
at java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:126)
at java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:498)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:485)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:152)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:464)
at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.getRequestedSessionId(SessionRepositoryFilter.java:360)
at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.isRequestedSessionIdValid(SessionRepositoryFilter.java:268)
at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.commitSession(SessionRepositoryFilter.java:230)
at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.access$100(SessionRepositoryFilter.java:196)
at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:149)
at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:81)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:728)
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:472)
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:395)
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:316)
at org.apache.catalina.core.StandardHostValve.custom(StandardHostValve.java:395)
at org.apache.catalina.core.StandardHostValve.status(StandardHostValve.java:254)
at org.apache.catalina.core.StandardHostValve.throwable(StandardHostValve.java:349)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:175)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
Here is my configuration :
@Configuration
@EnableRedisHttpSession
public class SessionRepositoryConfig implements BeanClassLoaderAware {
@Value("${spring.redis.host}")
private String redisHostName;
@Value("${spring.redis.port}")
private Integer redisPort;
@Value("${session.max-inactive-time}")
private Integer sessionMaxInactiveTime;
private ClassLoader loader;
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.loader = classLoader;
}
@Bean
JedisConnectionFactory jedisConnectionFactory() {
JedisClientConfiguration jedisConfig = JedisClientConfiguration.builder().usePooling().build();
RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration(redisHostName, redisPort);
return new JedisConnectionFactory(redisConfig, jedisConfig);
}
private ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModules(SecurityJackson2Modules.getModules(this.loader));
return mapper;
}
@Bean
public RedisTemplate<Object, Object> redisTemplate(JedisConnectionFactory jedisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer(objectMapper()));
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(jedisConnectionFactory);
return template;
}
@Bean
public RedisOperationsSessionRepository sessionRepository(RedisTemplate<Object, Object> redisTemplate) {
RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(redisTemplate);
sessionRepository.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());
sessionRepository.setDefaultMaxInactiveInterval(sessionMaxInactiveTime);
return sessionRepository;
}
}
Is it possible to configure Spring to store the session only if the login is a success ?
Issue Analytics
- State:
- Created 6 years ago
- Comments:5 (4 by maintainers)
Top Results From Across the Web
Spring Boot with Session/Redis Serialization Error with Bad ...
The Java object to be cached must implement the serializable interface, because spring will serialize the object and store it in redis.
Read more >Handling Deserialization errors in Spring Redis Sessions
This approach ensures that every time a de-serialization error is thrown while trying to read an object from the session, that object is...
Read more >Storing Json Formatted Spring Session in Redis - Rustam's Blog
Anything that is related to session attribute should be JSON serializable, otherwise, it will be keep throwing and errors. Lets assume we have ......
Read more >JacksonJsonRedisSerializer (Spring Data Redis 1.1.1 ...
RedisSerializer that can read and write JSON using Jackson's ObjectMapper . ... Note:Null objects are serialized as empty arrays and vice versa.
Read more >spring-projects/spring-session - Gitter
Does Hazelcast support using Jackson instead of native serialization? ... I have a java application and I use spring-session backed by redis for...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Thanks for the report @YLombardi.
I’ve reproduced the issue using our Redis with JSON serialization Boot sample app and this diff:
This should perhaps be handled by Spring Security, WDYT @rwinch? Same error was reported in spring-projects/spring-security#4370 (comment).
Closing since this has been addressed on Spring Security side by spring-projects/spring-security#5087.