package cn.iocoder.yudao.framework.xss.core.json; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.xss.config.XssProperties; import cn.iocoder.yudao.framework.xss.core.clean.XssCleaner; import jakarta.servlet.http.HttpServletRequest; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.util.PathMatcher; import tools.jackson.core.JacksonException; import tools.jackson.core.JsonParser; import tools.jackson.core.JsonToken; import tools.jackson.databind.DeserializationContext; import tools.jackson.databind.deser.jdk.StringDeserializer; /** * XSS 过滤 jackson 反序列化器。 * 在反序列化的过程中,会对字符串进行 XSS 过滤。 * * @author Hccake */ @Slf4j @AllArgsConstructor public class XssStringJsonDeserializer extends StringDeserializer { /** * 属性 */ private final XssProperties properties; /** * 路径匹配器 */ private final PathMatcher pathMatcher; private final XssCleaner xssCleaner; @Override public String deserialize(JsonParser p, DeserializationContext ctxt) throws JacksonException { // 1. 白名单 URL 的处理 HttpServletRequest request = ServletUtils.getRequest(); if (request != null) { String uri = ServletUtils.getRequest().getRequestURI(); if (properties.getExcludeUrls().stream().anyMatch(excludeUrl -> pathMatcher.match(excludeUrl, uri))) { return p.getString(); } } // 2. 真正使用 xssCleaner 进行过滤 if (p.hasToken(JsonToken.VALUE_STRING)) { return xssCleaner.clean(p.getString()); } JsonToken t = p.currentToken(); // [databind#381] if (t == JsonToken.START_ARRAY) { return _deserializeFromArray(p, ctxt); } // need to gracefully handle byte[] data, as base64 if (t == JsonToken.VALUE_EMBEDDED_OBJECT) { Object ob = p.getEmbeddedObject(); if (ob == null) { return null; } if (ob instanceof byte[]) { return ctxt.getBase64Variant().encode((byte[]) ob, false); } // otherwise, try conversion using toString()... return ob.toString(); } // 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML) if (t == JsonToken.START_OBJECT) { return ctxt.extractScalarFromObject(p, this, _valueClass); } if (t.isScalarValue()) { String text = p.getValueAsString(); return xssCleaner.clean(text); } return (String) ctxt.handleUnexpectedToken(_valueClass, p); } }