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

   

  

posted on 2020-05-04 15:59  溪水静幽  阅读(3713)  评论(0编辑  收藏  举报