Spring Session基于Redis存储的序列化问题
Spring redis SESSION 是如何进行反序列化?
Spring session针对Web的Request请求有一个org.springframework.session.web.http.SessionRepositoryFilter过滤器,根据SESSION ID获取相应的SESSION对象。
@Order(SessionRepositoryFilter.DEFAULT_ORDER) public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFilter{ ... private final SessionRepository<S> sessionRepository; ... }
SessionRepositoryFilter会调用sessionRepository.findById(sessionId)来查找SESSION对象,对于Redis,sessionRepository实现类为org.springframework.session.data.redis.RedisOperationsSessionRepository,该类默认的序列化类为org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.
public class RedisOperationsSessionRepository implements FindByIndexNameSessionRepository<RedisOperationsSessionRepository.RedisSession>, MessageListener { ... private RedisSerializer<Object> defaultSerializer = new JdkSerializationRedisSerializer(); ... }
查询JdkSerializationRedisSerializer源码,发现该类在反序列化时如果异常会抛出SerializationException异常,而SessionRepositoryFilter又没有处理异常,故如果序列化异常时就会导致请求异常。
public class JdkSerializationRedisSerializer implements RedisSerializer<Object> { private final Converter<Object, byte[]> serializer; private final Converter<byte[], Object> deserializer; /** * Creates a new {@link JdkSerializationRedisSerializer} using the default class loader. */ public JdkSerializationRedisSerializer() { this(new SerializingConverter(), new DeserializingConverter()); } /** * Creates a new {@link JdkSerializationRedisSerializer} using a {@link ClassLoader}. * * @param classLoader * @since 1.7 */ public JdkSerializationRedisSerializer(ClassLoader classLoader) { this(new SerializingConverter(), new DeserializingConverter(classLoader)); } /** * Creates a new {@link JdkSerializationRedisSerializer} using a {@link Converter converters} to serialize and * deserialize objects. * * @param serializer must not be {@literal null} * @param deserializer must not be {@literal null} * @since 1.7 */ public JdkSerializationRedisSerializer(Converter<Object, byte[]> serializer, Converter<byte[], Object> deserializer) { Assert.notNull(serializer, "Serializer must not be null!"); Assert.notNull(deserializer, "Deserializer must not be null!"); this.serializer = serializer; this.deserializer = deserializer; } public Object deserialize(@Nullable byte[] bytes) { if (SerializationUtils.isEmpty(bytes)) { return null; } try { return deserializer.convert(bytes); } catch (Exception ex) { throw new SerializationException("Cannot deserialize", ex); } } @Override public byte[] serialize(@Nullable Object object) { if (object == null) { return SerializationUtils.EMPTY_ARRAY; } try { return serializer.convert(object); } catch (Exception ex) { throw new SerializationException("Cannot serialize", ex); } } }
如何解决这个异常呢?定制Spring redis session的序列化类,替代原有的默认的JdkSerializationRedisSerializer。
@Configuration @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400) public class RedisConfig { @Bean public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>(); FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class); redisTemplate.setValueSerializer(fastJsonRedisSerializer); redisTemplate.setHashValueSerializer(fastJsonRedisSerializer); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setDefaultSerializer(new StringRedisSerializer()); redisTemplate.afterPropertiesSet(); return redisTemplate; } @Bean RedisSerializer<Object> springSessionDefaultRedisSerializer() { return new GenericFastJsonRedisSerializer(); } }
在原有序列化JdkSerializationRedisSerializer对象的基础上,在反序列化异常时捕获这个异常,仅记录相关日志即可
@Component("springSessionDefaultRedisSerializer") public class CustomSessionDefaultRedisSerializer extends JdkSerializationRedisSerializer { private static final Logger LOG = LoggerFactory.getLogger(CustomSessionDefaultRedisSerializer.class); public Object deserialize(@Nullable byte[] bytes) { Object deserialObj = null; try { deserialObj = super.deserialize(bytes); } catch(Exception e) { LOG.warn("deserialize session Object error!", e); } return deserialObj; } }
参考:
https://blog.yl-online.top/posts/74b23c9e.html