美文网首页
shiro的session信息放redis反序列化异常解决

shiro的session信息放redis反序列化异常解决

作者: little多米 | 来源:发表于2021-06-26 16:46 被阅读0次

    背景
    在本地session移至redis存储时,原本以为引入spring-session-data-redis依赖,配置RedisHttpSessionConfiguration,在web.xml中引入springSessionRepositoryFilter就结束了,没想到遇到序列化相关的问题,具体就是只有getter,没有setter。日志如下:

    26-Jun-2021 15:53:16.432 涓ラ噸 [http-nio-8081-exec-6] org.apache.catalina.core.StandardWrapperValve.invoke 鍦ㄨ矾寰勪负/traffic-web鐨勪笂涓嬫枃涓紝Servlet[spring2]鐨凷ervlet.service锛堬級寮曞彂浜嗗叿鏈夋牴鏈師鍥犵殑寮傚父Filtered request failed.
        com.fasterxml.jackson.databind.JsonMappingException: Problem deserializing 'setterless' property ("realmNames"): no way to handle typed deser with setterless yet (through reference chain: org.apache.shiro.subject.SimplePrincipalCollection["realmNames"])
            at com.fasterxml.jackson.databind.deser.impl.SetterlessProperty.deserializeAndSet(SetterlessProperty.java:102)
            at com.fasterxml.jackson.databind.deser.impl.BeanPropertyMap.findDeserializeAndSet(BeanPropertyMap.java:285)
            at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:248)
            at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:169)
            at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:144)
            at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedForId(AsPropertyTypeDeserializer.java:124)
            at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:95)
            at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromAny(AsPropertyTypeDeserializer.java:167)
            at com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer$Vanilla.deserializeWithType(UntypedObjectDeserializer.java:494)
            at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:42)
            at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3560)
            at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2709)
    

    分析
    提示很清楚,就是shiro的SimplePrincipalCollection类中realmNames字段没有setter方法,没法反序列化。
    来看看realmNames是什么鬼,作为成熟的框架也偷懒不写setter?仔细一看,发现并不简单。类里面没有realmNames,只有个getRealmNames方法。
    原来是个假getter,是由其他字段动态生成的,如下:

    public Set<String> getRealmNames() {
        if (realmPrincipals == null) {
            return null;
        } else {
            return realmPrincipals.keySet();
        }
    }
    

    看下redis里面存的值

    image.pngimage.png
    果然这个get方法被序列化了,并存到redis了,然后在反序列化的时候,又没有setter,就报了异常
    尝试在反序列化时忽略
    本以为objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)就能解决,但是跟踪了一圈源码之后,发现因为有getter,这个字段已经不算未知字段了。。。
    其实这样的字段序列化和反序列化也没啥意义,仔细研究这个类之后,发现整个类值得存储的字段就只有realmPrincipals。
    序列化时去掉无关字段
    既然不能反序列化的时候解决,那就在序列化的时候控制需要序列化的字段就可以了。
    常规的方式是在不需要的属性上面加注解@JsonIgnore就ok的,就是那么简单我办不到~,因为这个类是框架jar包的类无法修改。那就只有寻找不常规的方法了。
    终于一番搜索之后找到了,ObjectMapper配置如下。
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    //objectMapper.configure(MapperFeature.USE_ANNOTATIONS, false);
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
    
    //只序列化必要shiro字段
    String [] needSerialize = {"realmPrincipals"};
    objectMapper.addMixIn(SimplePrincipalCollection.class, IncludShiroFields.class);
    objectMapper.setFilters(new SimpleFilterProvider().addFilter("shiroFilter", SimpleBeanPropertyFilter.filterOutAllExcept(needSerialize)));
    // 此项必须配置,否则会报java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX
    objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
    objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    

    核心就是objectMapper.addMixIn()和objectMapper.setFilters()两个方法
    SimplePrincipalCollection是需要处理的类,IncludShiroFields就是一个简单的接口,如下:

    @JsonFilter("shiroFilter")
    public interface IncludShiroFields {
    }
    

    通过上面的配置间接控制SimplePrincipalCollection类中必要字段的序列化,从而解决了问题。
    ps:因为使用了注解,一定要去掉objectMapper.configure(MapperFeature.USE_ANNOTATIONS, false),不然配置不生效。

    相关文章

      网友评论

          本文标题:shiro的session信息放redis反序列化异常解决

          本文链接:https://www.haomeiwen.com/subject/imyzyltx.html