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