Spring Boot with Session/Redis Serialization Error with Bad Active Directory Ldap Credentials
The Java object to be cached must implement the serializable interface, because spring will serialize the object and store it in redis.
e.g. public class Store implement Serializable
The short story here is ensure you implement the serializable interface on your class.
I hope this help. Good luck.
I figured out a solution to this problem. I am open to any suggestions to improve the answer.
The solution is not complete as I need to look specifically for the
com.sun.jndi.ldap.LdapCtx
type when serialization fails so I can deal with that specific case and throw the SerializationException
in all others. But I thought the general idea might be useful to anyone who is blocked on this.
Now when invalid credentials are used (eg Bad Username or Incorrect Password) the application returns to the log in page rather than blowing up :)
I added some RedisConfiguration
to replace the RedisTemplate
Spring Session is using.
import com.gateway.utils.LdapFailAwareRedisObjectSerializer;
@Configuration
public class RedisConfiguration {
@Primary
@Bean
public RedisTemplate<String,ExpiringSession> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, ExpiringSession> template = new RedisTemplate<String, ExpiringSession>();
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(new LdapFailAwareRedisObjectSerializer());
template.setConnectionFactory(connectionFactory);
return template;
}
}
Here is my implementation of RedisSerializer<Object>
(LdapFailAwareRedisObjectSerializer
which is got from here)
public class LdapFailAwareRedisObjectSerializer implements RedisSerializer<Object> {
private Converter<Object, byte[]> serializer = new SerializingConverter();
private Converter<byte[], Object> deserializer = new DeserializingConverter();
static final byte[] EMPTY_ARRAY = new byte[0];
public Object deserialize(byte[] bytes) {
if (isEmpty(bytes)) {
return null;
}
try {
return deserializer.convert(bytes);
} catch (Exception ex) {
throw new SerializationException("Cannot deserialize", ex);
}
}
public byte[] serialize(Object object) {
if (object == null) {
return EMPTY_ARRAY;
}
try {
return serializer.convert(object);
} catch (Exception ex) {
return EMPTY_ARRAY;
//TODO add logic here to only return EMPTY_ARRAY for known conditions
// else throw the SerializationException
// throw new SerializationException("Cannot serialize", ex);
}
}
private boolean isEmpty(byte[] data) {
return (data == null || data.length == 0);
}
}
This just worked fine for me after using classes of org.springframework.core.serializer.support.DeserializingConverter and org.springframework.core.serializer.support.SerializingConverter
/**
* @author Meron Abraha 12/18/17
*/
public class CustomRedisSerializer implements RedisSerializer<Object> {
private Converter<Object, byte[]> serializer = new SerializingConverter();
private Converter<byte[], Object> deserializer = new DeserializingConverter();
static final byte[] EMPTY_ARRAY = new byte[0];
public Object deserialize(byte[] bytes) {
if (isEmpty(bytes)) {
return null;
}
try {
return deserializer.convert(bytes);
} catch (Exception ex) {
throw new SerializationException("Cannot deserialize", ex);
}
}
public byte[] serialize(Object object) {
if (object == null) {
return EMPTY_ARRAY;
}
try {
return serializer.convert(object);
} catch (Exception ex) {
return EMPTY_ARRAY;
}
}
private boolean isEmpty(byte[] data) {
return (data == null || data.length == 0);
}
}