@Controller接收Map<String,Object>时自定义类反序列化问题如何解决
反序列化问题解决方案
问题根因
你在实体类上添加的@JsonTypeInfo仅在Jackson反序列化时明确知道目标类型为该实体类/其父类/其实现的接口时才会生效。当前控制器入参定义为Map<String, Object>,值的目标类型是通用Object,Jackson默认不会扫描匹配带@JsonTypeInfo注解的实体类,遇到JSON对象会直接反序列化为LinkedHashMap,因此注解不会触发生效。
可落地解决方案
方案1:入参保持Map结构,手动转换复杂类型
无需调整入参定义,直接在业务逻辑中对Map中的复杂类型做转换:
@Autowired private ObjectMapper objectMapper; @PostMapping("/complete") public ResponseEntity<?> complete( @RequestBody(required = false) Map<String, Object> variables ) { if (variables != null) { variables.replaceAll((k, v) -> { // 仅处理反序列化生成的LinkedHashMap类型 if (v instanceof Map) { Map<String, Object> valueMap = (Map<String, Object>) v; String className = (String) valueMap.get("class"); if (className == null) { return v; } try { // 白名单校验,避免恶意类实例化风险 if (!List.of("com.yourpackage.PurchaseOrder", "com.yourpackage.PurchaseOrderEntry") .contains(className)) { return v; } Class<?> targetClazz = Class.forName(className); return objectMapper.convertValue(valueMap, targetClazz); } catch (ClassNotFoundException e) { // 自定义异常处理逻辑 return v; } } // Integer、String等基础类型直接返回 return v; }); } // 后续业务逻辑 return ResponseEntity.ok().build(); }
方案2:改用JsonNode作为入参,自行处理不同类型
JsonNode是Jackson提供的JSON树结构,可直接读取节点类型和属性,转换逻辑更清晰:
@Autowired private ObjectMapper objectMapper; @PostMapping("/complete") public ResponseEntity<?> complete( @RequestBody(required = false) JsonNode variables ) { if (variables != null && variables.isObject()) { Iterator<Map.Entry<String, JsonNode>> fieldIter = variables.fields(); while (fieldIter.hasNext()) { Map.Entry<String, JsonNode> field = fieldIter.next(); JsonNode node = field.getValue(); if (node.isTextual()) { String strVal = node.asText(); // 处理字符串类型 } else if (node.isInt() || node.isLong()) { long numVal = node.asLong(); // 处理数字类型 } else if (node.isObject()) { String className = node.get("class").asText(); // 同样先做白名单校验 Class<?> targetClazz = Class.forName(className); Object entity = objectMapper.treeToValue(node, targetClazz); // 处理自定义实体类 } } } return ResponseEntity.ok().build(); }
方案3:全局配置ObjectMapper启用默认多态处理(仅限内网可信场景)
如果接口仅在内网提供、调用方完全可信,可以全局配置Jackson对所有非基础类型启用多态反序列化,无需手动转换:
@Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); // 仅对非最终类型启用多态类型信息,和你实体类的配置规则匹配 mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); return mapper; } }
注意:该配置存在远程代码执行(RCE)安全风险,公网暴露的接口禁止使用。
安全提示
无论使用哪种方案,都建议对请求中传入的class属性做白名单校验,仅允许你预设的自定义类通过校验,避免恶意构造的请求传入危险类触发漏洞。
内容的提问来源于stack exchange,提问作者aeowlian
相关产品推荐
相关产品推荐

