使用SecurityJackson2Modules序列化Spring Security会话时BigDecimal兼容问题如何解决
解决方案
你现有的方案对业务代码侵入性较强,每个用到BigDecimal的字段都需要手动加Jackson注解,维护成本高,更合理的方案是全局修改Spring Session的Redis序列化配置,有两种常用实现路径:
方案1:全局扩展ObjectMapper配置,业务代码无侵入
直接在Spring Session配置类中自定义Redis序列化用的ObjectMapper,显式支持BigDecimal序列化,不需要修改任何业务DTO代码,代码如下:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.security.jackson2.SecurityJackson2Modules; import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; @Configuration @EnableRedisHttpSession public class SpringSessionRedisConfig { @Bean public RedisSerializer<Object> springSessionDefaultRedisSerializer() { ObjectMapper mapper = new ObjectMapper(); // 注册Spring Security内置的Jackson序列化模块,保证Security相关对象正常序列化 mapper.registerModules(SecurityJackson2Modules.getModules(this.getClass().getClassLoader())); // 配置多态序列化规则,显式允许包括BigDecimal在内的非final类型的序列化 mapper.activateDefaultTyping( LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL ); return new GenericJackson2JsonRedisSerializer(mapper); } }
如果对反序列化安全要求较高,可以替换LaissezFaireSubTypeValidator为自定义的子类,只放行业务包下的DTO类和java.math.BigDecimal这类需要的JDK类型,避免反序列化漏洞。
方案2:自定义BigDecimal序列化规则,避免冗余类型信息
如果不想让序列化结果增加多余的多态类型字段,也可以单独注册BigDecimal的序列化/反序列化器,代码如下:
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.StdSerializer; import java.io.IOException; import java.math.BigDecimal; // 自定义序列化器 class CustomBigDecimalSerializer extends StdSerializer<BigDecimal> { public CustomBigDecimalSerializer() { super(BigDecimal.class); } @Override public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeString(value.toString()); } } // 自定义反序列化器 class CustomBigDecimalDeserializer extends StdDeserializer<BigDecimal> { public CustomBigDecimalDeserializer() { super(BigDecimal.class); } @Override public BigDecimal deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { return new BigDecimal(p.getValueAsString()); } }
然后在方案1的ObjectMapper配置中注册该模块即可:
SimpleModule bigDecimalModule = new SimpleModule(); bigDecimalModule.addSerializer(BigDecimal.class, new CustomBigDecimalSerializer()); bigDecimalModule.addDeserializer(BigDecimal.class, new CustomBigDecimalDeserializer()); mapper.registerModule(bigDecimalModule);
这种方案序列化后的结果不会包含多余的类型字段,存储空间更小,且不需要修改业务代码。
内容的提问来源于stack exchange,提问作者Alexey Usharovski
相关产品推荐
相关产品推荐

