当在多服务之间使用 Spring Session Redis 进行 Session 共享要很是当心,由于它很不安全,颇有可能致使整个服务实例不可用,没法处理任何请求。其中比较危险的地方就是在进行序列化,反序列化的时候(这种类型的错误尤为容易在没有开发规范的团队内发生,就是什么样的数据能够往共享存储里面存,什么样的不能存。存的时候要以什么样的格式去存,这些都要有规定才比较安全。由于共享存储是会影响到别人的不单单是为了本身的服务用起来方便)。RedisSerializer 接口的实现都是在序列化和反序列化出错的时候直接抛出异常从而致使整个请求错误。java
public interface RedisSerializer<T> { /** * Serialize the given object to binary data. * * @param t object to serialize * @return the equivalent binary data */ byte[] serialize(T t) throws SerializationException; /** * Deserialize an object from the given binary data. * * @param bytes object binary representation * @return the equivalent object instance */ T deserialize(byte[] bytes) throws SerializationException; }
下面用一张图来讲明我遇到的问题。Spring Session 的诞生老实说并非为了分布式系统,而是为集群系统提供了一种 Session 解决方案。可是咱们把 Spring Session 用在了分布式系统上用以解决 Session 共享的问题老实说自己就是有点难为人家 Spring Session 了 。git
Spring Session 实现 Session 共享的大体原理以下图所示 , 使用一个 Filter 来拦截全部请求,在拦截到请求以后对 HttpServletRequest 和 HttpServletResponse 进行包装 (HttpServletRequestWrapper , HttpServletResponseWrapper)。在包装中对 session 进行控制 ,将 session 数据都存储在第三方存储当中。redis
在了解了 Spring Session 的工做原理后再去考虑这个问题就有头绪了 。仍是经过图形的方式来作大概的说明。spring
学习 Spring Session 的 SessionRepositoryFilter 的实现方式 , 添加一个 Filter 顺序在 SessionRepositoryFilter 以后 , 在拦截过程当中包装 HttpServletRequest , 重写 getSession(boolean create) 和 getSession() 方法, 自定义一个 SafetyHttpSessionWrapper 包装 Session ,重写 setAttribute(String name , Object value) 函数 , 在保存属性成功后利用 redis 的发布订阅机制发送消息到 redis , 消息的内容为所保存对象的 .class 文件数据。在消息订阅端 , 接收到消息后利用 javassist 和 net.bytebuddy.dynamic.loading.ByteArrayClassLoader 将 .class 文件数据加载转换成 Class 的实例对象,可是这个 Class 实例的范围被限定在 ByteArrayClassLoader 中 , 而这个 ByteArrayClassLoader 是提供给 RedisSerializer 内部使用的 , 好比 JdkSerializationRedisSerializer , GenericJackson2JsonRedisSerializer 都须要使用到 ClassLoader 。这样当 服务A 在存储任何自定义的对象在 Session 中时, 访问服务 B 也不会出现读取 Session 反序列化 ClassNotFoundException 的错误了。安全
以上思路的代码实如今 WORKX 项目中,感兴趣的同窗能够参考 org.hepeng.workx.spring.session.redis.serializer 这个包下的代码。从 org.hepeng.workx.spring.session.redis.serializer.SafetyRedisSerializerConfiguration 这个类入手看源码。目前 RedisSerializer 组件只支持了 JdkSerializationRedisSerializer , GenericJackson2JsonRedisSerializer 以及它们的子类。session