package com.iailab.framework.xss.core.json; import com.iailab.framework.common.util.servlet.ServletUtils; import com.iailab.framework.xss.config.XssProperties; import com.iailab.framework.xss.core.clean.XssCleaner; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StringDeserializer; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.util.PathMatcher; import javax.servlet.http.HttpServletRequest; import java.io.IOException; /** * 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 IOException { // 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.getText(); } } // 2. 真正使用 xssCleaner 进行过滤 if (p.hasToken(JsonToken.VALUE_STRING)) { return xssCleaner.clean(p.getText()); } 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); } }