shiro的session信息放redis反序列化异常解决
2021-06-26 本文已影响0人
little多米
背景
在本地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里面存的值
果然这个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),不然配置不生效。