mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-11-04 04:08:43 +08:00 
			
		
		
		
	Merge remote-tracking branch 'origin/dev' into feature/springdoc
# Conflicts: # yudao-dependencies/pom.xml # yudao-framework/yudao-spring-boot-starter-web/pom.xml # yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java # yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/core/SpringFoxHandlerProviderBeanPostProcessor.java # yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/captcha/CaptchaController.java
This commit is contained in:
		@@ -2,15 +2,22 @@ package cn.iocoder.yudao.framework.web.config;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
 | 
			
		||||
import cn.iocoder.yudao.framework.web.core.clean.JsoupXssCleaner;
 | 
			
		||||
import cn.iocoder.yudao.framework.web.core.clean.XssCleaner;
 | 
			
		||||
import cn.iocoder.yudao.framework.web.core.filter.CacheRequestBodyFilter;
 | 
			
		||||
import cn.iocoder.yudao.framework.web.core.filter.DemoFilter;
 | 
			
		||||
import cn.iocoder.yudao.framework.web.core.filter.XssFilter;
 | 
			
		||||
import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
 | 
			
		||||
import cn.iocoder.yudao.framework.web.core.handler.GlobalResponseBodyHandler;
 | 
			
		||||
import cn.iocoder.yudao.framework.web.core.json.XssStringJsonDeserializer;
 | 
			
		||||
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
 | 
			
		||||
import com.fasterxml.jackson.databind.ObjectMapper;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Value;
 | 
			
		||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
 | 
			
		||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
 | 
			
		||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 | 
			
		||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 | 
			
		||||
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
 | 
			
		||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
 | 
			
		||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
 | 
			
		||||
import org.springframework.context.annotation.Bean;
 | 
			
		||||
@@ -48,7 +55,7 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
 | 
			
		||||
     * 设置 API 前缀,仅仅匹配 controller 包下的
 | 
			
		||||
     *
 | 
			
		||||
     * @param configurer 配置
 | 
			
		||||
     * @param api API 配置
 | 
			
		||||
     * @param api        API 配置
 | 
			
		||||
     */
 | 
			
		||||
    private void configurePathMatch(PathMatchConfigurer configurer, WebProperties.Api api) {
 | 
			
		||||
        AntPathMatcher antPathMatcher = new AntPathMatcher(".");
 | 
			
		||||
@@ -104,8 +111,9 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
 | 
			
		||||
     * 创建 XssFilter Bean,解决 Xss 安全问题
 | 
			
		||||
     */
 | 
			
		||||
    @Bean
 | 
			
		||||
    public FilterRegistrationBean<XssFilter> xssFilter(XssProperties properties, PathMatcher pathMatcher) {
 | 
			
		||||
        return createFilterBean(new XssFilter(properties, pathMatcher), WebFilterOrderEnum.XSS_FILTER);
 | 
			
		||||
    @ConditionalOnBean(XssCleaner.class)
 | 
			
		||||
    public FilterRegistrationBean<XssFilter> xssFilter(XssProperties properties, PathMatcher pathMatcher, XssCleaner xssCleaner) {
 | 
			
		||||
        return createFilterBean(new XssFilter(properties, pathMatcher, xssCleaner), WebFilterOrderEnum.XSS_FILTER);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -117,6 +125,32 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
 | 
			
		||||
        return createFilterBean(new DemoFilter(), WebFilterOrderEnum.DEMO_FILTER);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Xss 清理者
 | 
			
		||||
     *
 | 
			
		||||
     * @return XssCleaner
 | 
			
		||||
     */
 | 
			
		||||
    @Bean
 | 
			
		||||
    @ConditionalOnMissingBean(XssCleaner.class)
 | 
			
		||||
    public XssCleaner xssCleaner() {
 | 
			
		||||
        return new JsoupXssCleaner();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 注册 Jackson 的序列化器,用于处理 json 类型参数的 xss 过滤
 | 
			
		||||
     *
 | 
			
		||||
     * @return Jackson2ObjectMapperBuilderCustomizer
 | 
			
		||||
     */
 | 
			
		||||
    @Bean
 | 
			
		||||
    @ConditionalOnMissingBean(name = "xssJacksonCustomizer")
 | 
			
		||||
    @ConditionalOnBean(ObjectMapper.class)
 | 
			
		||||
    @ConditionalOnProperty(value = "yudao.xss.enable", havingValue = "true")
 | 
			
		||||
    public Jackson2ObjectMapperBuilderCustomizer xssJacksonCustomizer(XssCleaner xssCleaner) {
 | 
			
		||||
        // 在反序列化时进行 xss 过滤,可以替换使用 XssStringJsonSerializer,在序列化时进行处理
 | 
			
		||||
        return builder -> builder.deserializerByType(String.class, new XssStringJsonDeserializer(xssCleaner));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) {
 | 
			
		||||
        FilterRegistrationBean<T> bean = new FilterRegistrationBean<>(filter);
 | 
			
		||||
        bean.setOrder(order);
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,80 @@
 | 
			
		||||
package cn.iocoder.yudao.framework.web.core.clean;
 | 
			
		||||
 | 
			
		||||
import org.jsoup.Jsoup;
 | 
			
		||||
import org.jsoup.nodes.Document;
 | 
			
		||||
import org.jsoup.safety.Safelist;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * jsonp 过滤字符串
 | 
			
		||||
 */
 | 
			
		||||
public class JsoupXssCleaner implements XssCleaner {
 | 
			
		||||
 | 
			
		||||
    private final Safelist safelist;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用于在 src 属性使用相对路径时,强制转换为绝对路径。 为空时不处理,值应为绝对路径的前缀(包含协议部分)
 | 
			
		||||
     */
 | 
			
		||||
    private final String baseUri;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 无参构造,默认使用 {@link JsoupXssCleaner#buildSafelist} 方法构建一个安全列表
 | 
			
		||||
     */
 | 
			
		||||
    public JsoupXssCleaner() {
 | 
			
		||||
        this.safelist = buildSafelist();
 | 
			
		||||
        this.baseUri = "";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public JsoupXssCleaner(Safelist safelist) {
 | 
			
		||||
        this.safelist = safelist;
 | 
			
		||||
        this.baseUri = "";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public JsoupXssCleaner(String baseUri) {
 | 
			
		||||
        this.safelist = buildSafelist();
 | 
			
		||||
        this.baseUri = baseUri;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public JsoupXssCleaner(Safelist safelist, String baseUri) {
 | 
			
		||||
        this.safelist = safelist;
 | 
			
		||||
        this.baseUri = baseUri;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 构建一个 Xss 清理的 Safelist 规则。
 | 
			
		||||
     * 基于 Safelist#relaxed() 的基础上:
 | 
			
		||||
     * 1. 扩展支持了 style 和 class 属性
 | 
			
		||||
     * 2. a 标签额外支持了 target 属性
 | 
			
		||||
     * 3. img 标签额外支持了 data 协议,便于支持 base64
 | 
			
		||||
     *
 | 
			
		||||
     * @return Safelist
 | 
			
		||||
     */
 | 
			
		||||
    private Safelist buildSafelist() {
 | 
			
		||||
        // 使用 jsoup 提供的默认的
 | 
			
		||||
        Safelist relaxedSafelist = Safelist.relaxed();
 | 
			
		||||
        // 富文本编辑时一些样式是使用 style 来进行实现的
 | 
			
		||||
        // 比如红色字体 style="color:red;", 所以需要给所有标签添加 style 属性
 | 
			
		||||
        // 注意:style 属性会有注入风险 <img STYLE="background-image:url(javascript:alert('XSS'))">
 | 
			
		||||
        relaxedSafelist.addAttributes(":all", "style", "class");
 | 
			
		||||
        // 保留 a 标签的 target 属性
 | 
			
		||||
        relaxedSafelist.addAttributes("a", "target");
 | 
			
		||||
        // 支持img 为base64
 | 
			
		||||
        relaxedSafelist.addProtocols("img", "src", "data");
 | 
			
		||||
 | 
			
		||||
        // 保留相对路径, 保留相对路径时,必须提供对应的 baseUri 属性,否则依然会被删除
 | 
			
		||||
        // WHITELIST.preserveRelativeLinks(false);
 | 
			
		||||
 | 
			
		||||
        // 移除 a 标签和 img 标签的一些协议限制,这会导致 xss 防注入失效,如 <img src=javascript:alert("xss")>
 | 
			
		||||
        // 虽然可以重写 WhiteList#isSafeAttribute 来处理,但是有隐患,所以暂时不支持相对路径
 | 
			
		||||
        // WHITELIST.removeProtocols("a", "href", "ftp", "http", "https", "mailto");
 | 
			
		||||
        // WHITELIST.removeProtocols("img", "src", "http", "https");
 | 
			
		||||
 | 
			
		||||
        return relaxedSafelist;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String clean(String html) {
 | 
			
		||||
        return Jsoup.clean(html, baseUri, safelist, new Document.OutputSettings().prettyPrint(false));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,15 @@
 | 
			
		||||
package cn.iocoder.yudao.framework.web.core.clean;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 对 html 文本中的有 Xss 风险的数据进行清理
 | 
			
		||||
 */
 | 
			
		||||
public interface XssCleaner {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 清理有 Xss 风险的文本
 | 
			
		||||
     *
 | 
			
		||||
     * @param html 原 html
 | 
			
		||||
     * @return 清理后的 html
 | 
			
		||||
     */
 | 
			
		||||
    String clean(String html);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
package cn.iocoder.yudao.framework.web.core.filter;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.web.config.XssProperties;
 | 
			
		||||
import cn.iocoder.yudao.framework.web.core.clean.XssCleaner;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import org.springframework.util.PathMatcher;
 | 
			
		||||
import org.springframework.web.filter.OncePerRequestFilter;
 | 
			
		||||
@@ -13,7 +14,7 @@ import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Xss 过滤器
 | 
			
		||||
 *
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 对 Xss 不了解的胖友,可以看看 http://www.iocoder.cn/Fight/The-new-girl-asked-me-why-AJAX-requests-are-not-secure-I-did-not-answer/
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
@@ -30,10 +31,12 @@ public class XssFilter extends OncePerRequestFilter {
 | 
			
		||||
     */
 | 
			
		||||
    private final PathMatcher pathMatcher;
 | 
			
		||||
 | 
			
		||||
    private final XssCleaner xssCleaner;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
 | 
			
		||||
            throws IOException, ServletException {
 | 
			
		||||
        filterChain.doFilter(new XssRequestWrapper(request), response);
 | 
			
		||||
        filterChain.doFilter(new XssRequestWrapper(request, xssCleaner), response);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,10 @@
 | 
			
		||||
package cn.iocoder.yudao.framework.web.core.filter;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.collection.CollUtil;
 | 
			
		||||
import cn.hutool.core.io.IoUtil;
 | 
			
		||||
import cn.hutool.core.util.ArrayUtil;
 | 
			
		||||
import cn.hutool.core.util.ReflectUtil;
 | 
			
		||||
import cn.hutool.core.util.StrUtil;
 | 
			
		||||
import cn.hutool.http.HTMLFilter;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
 | 
			
		||||
import cn.iocoder.yudao.framework.web.core.clean.XssCleaner;
 | 
			
		||||
 | 
			
		||||
import javax.servlet.ReadListener;
 | 
			
		||||
import javax.servlet.ServletInputStream;
 | 
			
		||||
import javax.servlet.http.HttpServletRequest;
 | 
			
		||||
import javax.servlet.http.HttpServletRequestWrapper;
 | 
			
		||||
import java.io.BufferedReader;
 | 
			
		||||
import java.io.ByteArrayInputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStreamReader;
 | 
			
		||||
import java.util.LinkedHashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -24,113 +13,79 @@ import java.util.Map;
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
public class XssRequestWrapper extends HttpServletRequestWrapper {
 | 
			
		||||
    private final XssCleaner xssCleaner;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 基于线程级别的 HTMLFilter 对象,因为它线程非安全
 | 
			
		||||
     */
 | 
			
		||||
    private static final ThreadLocal<HTMLFilter> HTML_FILTER = ThreadLocal.withInitial(() -> {
 | 
			
		||||
        HTMLFilter htmlFilter = new HTMLFilter();
 | 
			
		||||
        // 反射修改 encodeQuotes 属性为 false,避免 " 被转移成 " 字符
 | 
			
		||||
        ReflectUtil.setFieldValue(htmlFilter, "encodeQuotes", false);
 | 
			
		||||
        return htmlFilter;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    public XssRequestWrapper(HttpServletRequest request) {
 | 
			
		||||
    public XssRequestWrapper(HttpServletRequest request, XssCleaner xssCleaner) {
 | 
			
		||||
        super(request);
 | 
			
		||||
        this.xssCleaner = xssCleaner;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static String filterXss(String content) {
 | 
			
		||||
        if (StrUtil.isEmpty(content)) {
 | 
			
		||||
            return content;
 | 
			
		||||
    // ============================ parameter ============================
 | 
			
		||||
    @Override
 | 
			
		||||
    public Map<String, String[]> getParameterMap() {
 | 
			
		||||
        Map<String, String[]> map = new LinkedHashMap<>();
 | 
			
		||||
        Map<String, String[]> parameters = super.getParameterMap();
 | 
			
		||||
        for (Map.Entry<String, String[]> entry : parameters.entrySet()) {
 | 
			
		||||
            String[] values = entry.getValue();
 | 
			
		||||
            for (int i = 0; i < values.length; i++) {
 | 
			
		||||
                values[i] = xssCleaner.clean(values[i]);
 | 
			
		||||
            }
 | 
			
		||||
            map.put(entry.getKey(), values);
 | 
			
		||||
        }
 | 
			
		||||
        return HTML_FILTER.get().filter(content);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ========== IO 流相关 ==========
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public BufferedReader getReader() throws IOException {
 | 
			
		||||
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public ServletInputStream getInputStream() throws IOException {
 | 
			
		||||
        // 如果非 json 请求,不进行 Xss 处理
 | 
			
		||||
        if (!ServletUtils.isJsonRequest(this)) {
 | 
			
		||||
            return super.getInputStream();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 读取内容,并过滤
 | 
			
		||||
        String content = IoUtil.readUtf8(super.getInputStream());
 | 
			
		||||
        content = filterXss(content);
 | 
			
		||||
        final ByteArrayInputStream newInputStream = new ByteArrayInputStream(content.getBytes());
 | 
			
		||||
        // 返回 ServletInputStream
 | 
			
		||||
        return new ServletInputStream() {
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public int read() {
 | 
			
		||||
                return newInputStream.read();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public boolean isFinished() {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public boolean isReady() {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public void setReadListener(ReadListener readListener) {}
 | 
			
		||||
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ========== Param 相关 ==========
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getParameter(String name) {
 | 
			
		||||
        String value = super.getParameter(name);
 | 
			
		||||
        return filterXss(value);
 | 
			
		||||
        return map;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String[] getParameterValues(String name) {
 | 
			
		||||
        String[] values = super.getParameterValues(name);
 | 
			
		||||
        if (ArrayUtil.isEmpty(values)) {
 | 
			
		||||
            return values;
 | 
			
		||||
        if (values == null) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        // 过滤处理
 | 
			
		||||
        for (int i = 0; i < values.length; i++) {
 | 
			
		||||
            values[i] = filterXss(values[i]);
 | 
			
		||||
        int count = values.length;
 | 
			
		||||
        String[] encodedValues = new String[count];
 | 
			
		||||
        for (int i = 0; i < count; i++) {
 | 
			
		||||
            encodedValues[i] = xssCleaner.clean(values[i]);
 | 
			
		||||
        }
 | 
			
		||||
        return values;
 | 
			
		||||
        return encodedValues;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Map<String, String[]> getParameterMap() {
 | 
			
		||||
        Map<String, String[]> valueMap = super.getParameterMap();
 | 
			
		||||
        if (CollUtil.isEmpty(valueMap)) {
 | 
			
		||||
            return valueMap;
 | 
			
		||||
    public String getParameter(String name) {
 | 
			
		||||
        String value = super.getParameter(name);
 | 
			
		||||
        if (value == null) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        // 过滤处理
 | 
			
		||||
        for (Map.Entry<String, String[]> entry : valueMap.entrySet()) {
 | 
			
		||||
            String[] values = entry.getValue();
 | 
			
		||||
            for (int i = 0; i < values.length; i++) {
 | 
			
		||||
                values[i] = filterXss(values[i]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return valueMap;
 | 
			
		||||
        return xssCleaner.clean(value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ========== Header 相关 ==========
 | 
			
		||||
    // ============================ attribute ============================
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object getAttribute(String name) {
 | 
			
		||||
        Object value = super.getAttribute(name);
 | 
			
		||||
        if (value instanceof String) {
 | 
			
		||||
            xssCleaner.clean((String) value);
 | 
			
		||||
        }
 | 
			
		||||
        return value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ============================ header ============================
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getHeader(String name) {
 | 
			
		||||
        String value = super.getHeader(name);
 | 
			
		||||
        return filterXss(value);
 | 
			
		||||
        if (value == null) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        return xssCleaner.clean(value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ============================ queryString ============================
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getQueryString() {
 | 
			
		||||
        String value = super.getQueryString();
 | 
			
		||||
        if (value == null) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        return xssCleaner.clean(value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,59 @@
 | 
			
		||||
package cn.iocoder.yudao.framework.web.core.json;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.web.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 java.io.IOException;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * XSS 过滤 jackson 反序列化器。
 | 
			
		||||
 * 在反序列化的过程中,会对字符串进行 XSS 过滤。
 | 
			
		||||
 *
 | 
			
		||||
 * @author Hccake
 | 
			
		||||
 */
 | 
			
		||||
@Slf4j
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public class XssStringJsonDeserializer extends StringDeserializer {
 | 
			
		||||
 | 
			
		||||
    private final XssCleaner xssCleaner;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
 | 
			
		||||
        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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user