• (六)admin-boot项目之全局处理预防xss攻击


    (六)全局处理预防xss攻击

    项目地址:https://gitee.com/springzb/admin-boot
    如果觉得不错,给个 star

    简介:
    这是一个基础的企业级基础后端脚手架项目,主要由springboot为基础搭建,后期整合一些基础插件例如:redis、xxl-job、flowable、minioio、easyexcel、skyWalking、rabbitmq

    目录

    一、xss攻击简介

    XSS攻击方式很多,这里只介绍一些常用的XSS攻击手段以及其目的,为提出Springboot项目防止XSS攻击解决方案做说明。例如:

    盗用cookie,获取敏感信息。

    利用植入Flash,通过crossdomain权限设置进一步获取更高权限;或者利用Java等得到类似的操作。

    利用iframe、frame、XMLHttpRequest或上述Flash等方式,以(被攻击)用户的身份执行一些管理动作,或执行一些一般的如发微博、加好友、发私信等操作。

    利用可被攻击的域受到其他域信任的特点,以受信任来源的身份请求一些平时不允许的操作,如进行不当的投票活动。

    在访问量极大的一些页面上的XSS可以攻击一些小型网站,实现DDoS攻击的效果。

    许多初级Springboot开发人员经常中此招,最近在测试个人项目时,也出现了这个问题。那么在Springboot项目中,我们该如何防止XSS攻击呢?

    二、springboot预防xss攻击原理

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-msJDz630-1660384593710)(image/image_zyeSe6yrYl.png)]

    1、前端请求进入到XssFilter

    2、包装request

    3、过滤转义原来的body

    4、进入其他过滤器,后到达controller

    5、返回数据给前端

    三、相关代码

    引入maven pom.xml

      
       
            <dependency>
                <groupId>org.apache.commonsgroupId>
                <artifactId>commons-textartifactId>
                <version>1.9version>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    XssProperties.java配置

    package cn.mesmile.admin.common.filter.xss;
    
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    
    import java.util.HashSet;
    import java.util.Set;
    
    /**
     * 预防xss攻击设置
     * @author zb
     */
    @ConfigurationProperties("security.xss")
    @Data
    public class XssProperties {
    
        /**
         *  是否开启xss拦截
         */
        private Boolean enabled = true;
    
        /**
         *  放行拦截的路径
         */
        private Set<String> skipUrl = new HashSet<>();
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    XssHttpServletRequestWrapper.java 过滤包装器

    package cn.mesmile.admin.common.filter.xss;
    
    import cn.hutool.core.util.StrUtil;
    import cn.mesmile.admin.common.utils.WebUtil;import com.alibaba.fastjson.JSONObject;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.text.StringEscapeUtils;
    
    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.Iterator;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    /**
     * xss过滤包装器
     *
     * @author zb
     * @Description 
     */
    @Slf4j
    public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
        private final HttpServletRequest orgRequest;
        private byte[] body;
    //    private static final XssHtmlFilter HTML_FILTER = new XssHtmlFilter();
    
        public XssHttpServletRequestWrapper(HttpServletRequest request) {
            super(request);
            this.orgRequest = request;
        }
    
        @Override
        public BufferedReader getReader() throws IOException {
            return new BufferedReader(new InputStreamReader(this.getInputStream()));
        }
    
        @Override
        public ServletInputStream getInputStream() throws IOException {
            if (super.getHeader("Content-Type") == null) {
                return super.getInputStream();
            } else if (super.getHeader("Content-Type").startsWith("multipart/form-data")) {
                return super.getInputStream();
            } else {
                if (this.body == null) {
                    String requestBody = WebUtil.getRequestBody(super.getInputStream());
                    if (StrUtil.isNotEmpty(requestBody)) {
                        // 去除json字符串中所有类型为string两边的空格
                        Map<String, Object> stringObjectMap = StringJsonUtils.
                                jsonStringToMapAndTrim(requestBody, true, false);
                        requestBody = JSONObject.toJSONString(stringObjectMap);
                    } else {
                        // 为空则直接返回
                        return super.getInputStream();
                    }
                    this.body = requestBody.getBytes();
                }
                final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.body);
                return new ServletInputStream() {
                    @Override
                    public int read() {
                        return byteArrayInputStream.read();
                    }
    
                    @Override
                    public boolean isFinished() {
                        return false;
                    }
    
                    @Override
                    public boolean isReady() {
                        return false;
                    }
    
                    @Override
                    public void setReadListener(ReadListener readListener) {
                    }
                };
            }
        }
    
        @Override
        public String getParameter(String name) {
            String value = super.getParameter(this.xssEncode(name));
            if (StrUtil.isNotBlank(value)) {
                value = this.xssEncode(value);
            }
            return value;
        }
    
        @Override
        public String[] getParameterValues(String name) {
            String[] parameters = super.getParameterValues(name);
            if (parameters != null && parameters.length != 0) {
                for (int i = 0; i < parameters.length; ++i) {
                    parameters[i] = this.xssEncode(parameters[i]);
                }
                return parameters;
            } else {
                return null;
            }
        }
    
        @Override
        public Map<String, String[]> getParameterMap() {
            Map<String, String[]> map = new LinkedHashMap();
            Map<String, String[]> parameters = super.getParameterMap();
            Iterator iterator = parameters.keySet().iterator();
            while (iterator.hasNext()) {
                String key = (String) iterator.next();
                String[] values = (String[]) parameters.get(key);
                for (int i = 0; i < values.length; ++i) {
                    values[i] = this.xssEncode(values[i]);
                }
                map.put(key, values);
            }
            return map;
        }
    
        @Override
        public String getHeader(String name) {
            String value = super.getHeader(this.xssEncode(name));
            if (StrUtil.isNotBlank(value)) {
                value = this.xssEncode(value);
            }
            return value;
        }
    
    
        private String xssEncode(String input) {
            // 转义成 html 4
            return StringEscapeUtils.escapeHtml4(input);
    //        return HTML_FILTER.filter(input);
        }
    
        public HttpServletRequest getOrgRequest() {
            return this.orgRequest;
        }
    
        public static HttpServletRequest getOrgRequest(HttpServletRequest request) {
            return request instanceof XssHttpServletRequestWrapper ? ((XssHttpServletRequestWrapper) request).getOrgRequest() : request;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148

    XssRequestFilter.java xss 过滤器

    package cn.mesmile.admin.common.filter.xss;
    
    import org.springframework.util.AntPathMatcher;
    
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    
    /***
     * 自定义xss过滤器
     * @author zb
     * @Description
     */
    public class XssRequestFilter implements Filter {
    
        private final XssProperties xssProperties;
        private final AntPathMatcher antPathMatcher = new AntPathMatcher();
    
        @Override
        public void init(FilterConfig config) {
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            String path = ((HttpServletRequest) request).getServletPath();
            if (this.xssProperties.getEnabled() && !this.isXssSkip(path)) {
                XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
                chain.doFilter(xssRequest, response);
            } else {
                chain.doFilter(request, response);
            }
        }
    
        /**
         * 判断是否跳过路径
         * @param path
         * @return
         */
        private boolean isXssSkip(String path) {
            return this.xssProperties.getSkipUrl().stream().anyMatch((pattern) -> {
                return this.antPathMatcher.match(pattern, path);
            });
        }
    
        @Override
        public void destroy() {
        }
    
        public XssRequestFilter(final XssProperties xssProperties) {
            this.xssProperties = xssProperties;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    注册xss过滤器

    package cn.mesmile.admin.common.filter;
    
    import cn.mesmile.admin.common.filter.xss.XssProperties;
    import cn.mesmile.admin.common.filter.xss.XssRequestFilter;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import javax.servlet.DispatcherType;
    
    /**
     * @author zb
     * @Description 配置注册过滤器
     */
    @Configuration(
            proxyBeanMethods = false
    )
    @EnableConfigurationProperties({XssProperties.class})
    public class RequestConfiguration {
    
        private final XssProperties xssProperties;
    
        @Bean
        public FilterRegistrationBean<XssRequestFilter> xssFilterRegistration() {
            FilterRegistrationBean<XssRequestFilter> registration = new FilterRegistrationBean();
            registration.setDispatcherTypes(DispatcherType.REQUEST, new DispatcherType[0]);
            registration.setFilter(new XssRequestFilter(this.xssProperties));
            registration.addUrlPatterns(new String[]{"/*"});
            registration.setName("xssRequestFilter");
            registration.setOrder(Integer.MAX_VALUE);
            return registration;
        }
    
        public RequestConfiguration(final XssProperties xssProperties) {
            this.xssProperties = xssProperties;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    工具类:

    package cn.mesmile.admin.common.filter.xss;
    
    import cn.hutool.core.util.StrUtil;
    import com.alibaba.fastjson.JSONArray;
    import com.alibaba.fastjson.JSONObject;
    import org.apache.commons.text.StringEscapeUtils;
    
    import java.util.*;
    
    /**
     * @author zb
     * @Description
     */
    public class StringJsonUtils {
    
        /**
         * 去除json字符串中所有类型为string两边的空格
         * @param jsonString 需要处理的json字符串
         * @param escapeHtml 是否转义html符号
         * @param trim 是否去除两个空格
         */
        public static Map<String, Object> jsonStringToMapAndTrim(String jsonString,boolean escapeHtml ,boolean trim) {
            Map<String, Object> map = new HashMap<>();
            JSONObject jsonObject = JSONObject.parseObject(jsonString);
            for (Object k : jsonObject.keySet()) {
                Object o = jsonObject.get(k);
                if (o instanceof JSONArray) {
                    List<Map<String, Object>> list = new ArrayList<>();
                    Iterator<Object> it = ((JSONArray) o).iterator();
                    while (it.hasNext()) {
                        Object obj = it.next();
                        list.add(jsonStringToMapAndTrim(obj.toString(), escapeHtml , trim));
                    }
                    map.put(k.toString(), list);
                } else if (o instanceof JSONObject) {
                    // 如果内层是json对象的话,继续解析
                    map.put(k.toString(), jsonStringToMapAndTrim(o.toString(), escapeHtml, trim));
                } else {
                    // 如果内层是普通对象的话,直接放入map中
                    if (o instanceof String) {
                        String s = o.toString();
                        if (escapeHtml){
                            // 转义成 html 4
                            s = StringEscapeUtils.escapeHtml4(s);
                        }
                        if (trim){
                            // 去除字符串两边空格
                            s = StrUtil.trim(s);
                        }
                        map.put(k.toString(), s);
                    } else {
                        map.put(k.toString(), o);
                    }
                }
            }
            return map;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    package cn.mesmile.admin.common.filter.xss;
    
    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletResponse;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.nio.charset.StandardCharsets;
    
    /**
     * @author zb
     * @Description
     */
    public class WebUtil {
    
        /**
         * springmvc中的 @RequestBody 获取body中的数据
         * @param servletInputStream 输入流
         * @return body字符串
         */
        public static String getRequestBody(ServletInputStream servletInputStream) {
            StringBuilder sb = new StringBuilder();
            BufferedReader reader = null;
            try {
                reader = new BufferedReader(new InputStreamReader(servletInputStream, StandardCharsets.UTF_8));
                String line;
                while((line = reader.readLine()) != null) {
                    sb.append(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (servletInputStream != null) {
                    try {
                        servletInputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return sb.toString();
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    xss过滤器

    package cn.mesmile.admin.common.filter.xss;
    
    import java.util.*;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.ConcurrentMap;
    import java.util.logging.Logger;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    /**
     * @author zb
     * @Description
     */
    public final class XssHtmlFilter {
    
        private static final int REGEX_FLAGS_SI = 34;
        private static final Pattern P_COMMENTS = Pattern.compile("", 32);
        private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", 34);
        private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", 32);
        private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", 34);
        private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", 34);
        private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", 34);
        private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", 34);
        private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", 34);
        private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?");
        private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?");
        private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
        private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
        private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", 32);
        private static final Pattern P_END_ARROW = Pattern.compile("^>");
        private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
        private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)");
        private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)");
        private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)");
        private static final Pattern P_AMP = Pattern.compile("&");
        private static final Pattern P_QUOTE = Pattern.compile("<");
        private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
        private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
        private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
        private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap();
        private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap();
        private final Map<String, List<String>> vAllowed;
        private final Map<String, Integer> vTagCounts;
        private final String[] vSelfClosingTags;
        private final String[] vNeedClosingTags;
        private final String[] vDisallowed;
        private final String[] vProtocolAtts;
        private final String[] vAllowedProtocols;
        private final String[] vRemoveBlanks;
        private final String[] vAllowedEntities;
        private final boolean stripComment;
        private final boolean encodeQuotes;
        private boolean vDebug;
        private final boolean alwaysMakeTags;
    
        public XssHtmlFilter() {
            this.vTagCounts = new HashMap();
            this.vDebug = false;
            this.vAllowed = new HashMap();
            ArrayList<String> aAtts = new ArrayList();
            aAtts.add("href");
            aAtts.add("target");
            this.vAllowed.put("a", aAtts);
            ArrayList<String> imgAtts = new ArrayList();
            imgAtts.add("src");
            imgAtts.add("width");
            imgAtts.add("height");
            imgAtts.add("alt");
            this.vAllowed.put("img", imgAtts);
            ArrayList<String> noAtts = new ArrayList();
            this.vAllowed.put("b", noAtts);
            this.vAllowed.put("strong", noAtts);
            this.vAllowed.put("i", noAtts);
            this.vAllowed.put("em", noAtts);
            this.vSelfClosingTags = new String[]{"img"};
            this.vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"};
            this.vDisallowed = new String[0];
            this.vAllowedProtocols = new String[]{"http", "mailto", "https"};
            this.vProtocolAtts = new String[]{"src", "href"};
            this.vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"};
            this.vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"};
            this.stripComment = true;
            this.encodeQuotes = true;
            this.alwaysMakeTags = false;
        }
    
        public XssHtmlFilter(final boolean debug) {
            this();
            this.vDebug = debug;
        }
    
        public XssHtmlFilter(final Map<String, Object> conf) {
            this.vTagCounts = new HashMap();
            this.vDebug = false;
    
            assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
    
            assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
    
            assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
    
            assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
    
            assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
    
            assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
    
            assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
    
            assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";
    
            this.vAllowed = Collections.unmodifiableMap((HashMap)conf.get("vAllowed"));
            this.vSelfClosingTags = (String[])((String[])conf.get("vSelfClosingTags"));
            this.vNeedClosingTags = (String[])((String[])conf.get("vNeedClosingTags"));
            this.vDisallowed = (String[])((String[])conf.get("vDisallowed"));
            this.vAllowedProtocols = (String[])((String[])conf.get("vAllowedProtocols"));
            this.vProtocolAtts = (String[])((String[])conf.get("vProtocolAtts"));
            this.vRemoveBlanks = (String[])((String[])conf.get("vRemoveBlanks"));
            this.vAllowedEntities = (String[])((String[])conf.get("vAllowedEntities"));
            this.stripComment = conf.containsKey("stripComment") ? (Boolean)conf.get("stripComment") : true;
            this.encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean)conf.get("encodeQuotes") : true;
            this.alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean)conf.get("alwaysMakeTags") : true;
        }
    
        private void reset() {
            this.vTagCounts.clear();
        }
    
        private void debug(final String msg) {
            if (this.vDebug) {
                Logger.getAnonymousLogger().info(msg);
            }
    
        }
    
        public static String chr(final int decimal) {
            return String.valueOf((char)decimal);
        }
    
        public static String htmlSpecialChars(final String s) {
            String result = regexReplace(P_AMP, "&", s);
            result = regexReplace(P_QUOTE, """, result);
            result = regexReplace(P_LEFT_ARROW, "<", result);
            result = regexReplace(P_RIGHT_ARROW, ">", result);
            return result;
        }
    
        public String filter(final String input) {
            this.reset();
            this.debug("************************************************");
            this.debug("              INPUT: " + input);
            String s = this.escapeComments(input);
            this.debug("     escapeComments: " + s);
            s = this.balanceHtml(s);
            this.debug("        balanceHtml: " + s);
            s = this.checkTags(s);
            this.debug("          checkTags: " + s);
            s = this.processRemoveBlanks(s);
            this.debug("processRemoveBlanks: " + s);
            s = this.validateEntities(s);
            this.debug("    validateEntites: " + s);
            this.debug("************************************************\n\n");
            // 去除两边空格
    //        s = StringUtils.trimWhitespace(s);
    //        s = StrUtil.trim(s);
            return s;
        }
    
        public boolean isAlwaysMakeTags() {
            return this.alwaysMakeTags;
        }
    
        public boolean isStripComments() {
            return this.stripComment;
        }
    
        private String escapeComments(final String s) {
            Matcher m = P_COMMENTS.matcher(s);
            StringBuffer buf = new StringBuffer();
            if (m.find()) {
                String match = m.group(1);
                m.appendReplacement(buf, Matcher.quoteReplacement(""));
            }
    
            m.appendTail(buf);
            return buf.toString();
        }
    
        private String balanceHtml(String s) {
            if (this.alwaysMakeTags) {
                s = regexReplace(P_END_ARROW, "", s);
                s = regexReplace(P_BODY_TO_END, "<$1>", s);
                s = regexReplace(P_XML_CONTENT, "$1<$2", s);
            } else {
                s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s);
                s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s);
                s = regexReplace(P_BOTH_ARROWS, "", s);
            }
    
            return s;
        }
    
        private String checkTags(String s) {
            Matcher m = P_TAGS.matcher(s);
            StringBuffer buf = new StringBuffer();
    
            while(m.find()) {
                String replaceStr = m.group(1);
                replaceStr = this.processTag(replaceStr);
                m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
            }
    
            m.appendTail(buf);
            s = buf.toString();
            Iterator iterator = this.vTagCounts.keySet().iterator();
    
            while(iterator.hasNext()) {
                String key = (String)iterator.next();
                for(int ii = 0; ii < (Integer)this.vTagCounts.get(key); ++ii) {
                    s = s + " + key + ">";
                }
            }
            return s;
        }
    
        private String processRemoveBlanks(final String s) {
            String result = s;
            String[] blanks = this.vRemoveBlanks;
            int length = blanks.length;
            for(int i = 0; i < length; ++i) {
                String tag = blanks[i];
                if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) {
                    P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?> + tag + ">"));
                }
    
                result = regexReplace((Pattern)P_REMOVE_PAIR_BLANKS.get(tag), "", result);
                if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) {
                    P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>"));
                }
    
                result = regexReplace((Pattern)P_REMOVE_SELF_BLANKS.get(tag), "", result);
            }
    
            return result;
        }
    
        private static String regexReplace(final Pattern regexPattern, final String replacement, final String s) {
            Matcher m = regexPattern.matcher(s);
            return m.replaceAll(replacement);
        }
    
        private String processTag(final String s) {
            Matcher m = P_END_TAG.matcher(s);
            String name;
            if (m.find()) {
                name = m.group(1).toLowerCase();
                if (this.allowed(name) && !inArray(name, this.vSelfClosingTags) && this.vTagCounts.containsKey(name)) {
                    this.vTagCounts.put(name, (Integer)this.vTagCounts.get(name) - 1);
                    return " + name + ">";
                }
            }
    
            m = P_START_TAG.matcher(s);
            if (!m.find()) {
                m = P_COMMENT.matcher(s);
                return !this.stripComment && m.find() ? "<" + m.group() + ">" : "";
            } else {
                name = m.group(1).toLowerCase();
                String body = m.group(2);
                String ending = m.group(3);
                if (!this.allowed(name)) {
                    return "";
                } else {
                    String params = "";
                    Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
                    Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
                    List<String> paramNames = new ArrayList();
                    ArrayList paramValues = new ArrayList();
    
                    while(m2.find()) {
                        paramNames.add(m2.group(1));
                        paramValues.add(m2.group(3));
                    }
    
                    while(m3.find()) {
                        paramNames.add(m3.group(1));
                        paramValues.add(m3.group(3));
                    }
    
                    for(int ii = 0; ii < paramNames.size(); ++ii) {
                        String paramName = ((String)paramNames.get(ii)).toLowerCase();
                        String paramValue = (String)paramValues.get(ii);
                        if (this.allowedAttribute(name, paramName)) {
                            if (inArray(paramName, this.vProtocolAtts)) {
                                paramValue = this.processParamProtocol(paramValue);
                            }
    
                            params = params + " " + paramName + "=\"" + paramValue + "\"";
                        }
                    }
    
                    if (inArray(name, this.vSelfClosingTags)) {
                        ending = " /";
                    }
    
                    if (inArray(name, this.vNeedClosingTags)) {
                        ending = "";
                    }
    
                    if (ending != null && ending.length() >= 1) {
                        ending = " /";
                    } else if (this.vTagCounts.containsKey(name)) {
                        this.vTagCounts.put(name, (Integer)this.vTagCounts.get(name) + 1);
                    } else {
                        this.vTagCounts.put(name, 1);
                    }
    
                    return "<" + name + params + ending + ">";
                }
            }
        }
    
        private String processParamProtocol(String s) {
            s = this.decodeEntities(s);
            Matcher m = P_PROTOCOL.matcher(s);
            if (m.find()) {
                String protocol = m.group(1);
                if (!inArray(protocol, this.vAllowedProtocols)) {
                    s = "#" + s.substring(protocol.length() + 1);
                    if (s.startsWith("#//")) {
                        s = "#" + s.substring(3);
                    }
                }
            }
    
            return s;
        }
    
        private String decodeEntities(String s) {
            StringBuffer buf = new StringBuffer();
            Matcher m = P_ENTITY.matcher(s);
    
            String match;
            int decimal;
            while(m.find()) {
                match = m.group(1);
                decimal = Integer.decode(match);
                m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
            }
    
            m.appendTail(buf);
            s = buf.toString();
            buf = new StringBuffer();
            m = P_ENTITY_UNICODE.matcher(s);
    
            while(m.find()) {
                match = m.group(1);
                decimal = Integer.valueOf(match, 16);
                m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
            }
    
            m.appendTail(buf);
            s = buf.toString();
            buf = new StringBuffer();
            m = P_ENCODE.matcher(s);
    
            while(m.find()) {
                match = m.group(1);
                decimal = Integer.valueOf(match, 16);
                m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
            }
    
            m.appendTail(buf);
            s = buf.toString();
            s = this.validateEntities(s);
            return s;
        }
    
        private String validateEntities(final String s) {
            StringBuffer buf = new StringBuffer();
            Matcher m = P_VALID_ENTITIES.matcher(s);
    
            while(m.find()) {
                String one = m.group(1);
                String two = m.group(2);
                m.appendReplacement(buf, Matcher.quoteReplacement(this.checkEntity(one, two)));
            }
    
            m.appendTail(buf);
            return this.encodeQuotes(buf.toString());
        }
    
        private String encodeQuotes(final String s) {
            if (!this.encodeQuotes) {
                return s;
            } else {
                StringBuffer buf = new StringBuffer();
                Matcher m = P_VALID_QUOTES.matcher(s);
    
                while(m.find()) {
                    String one = m.group(1);
                    String two = m.group(2);
                    String three = m.group(3);
                    m.appendReplacement(buf, Matcher.quoteReplacement(one + regexReplace(P_QUOTE, """, two) + three));
                }
    
                m.appendTail(buf);
                return buf.toString();
            }
        }
    
        private String checkEntity(final String preamble, final String term) {
            return ";".equals(term) && this.isValidEntity(preamble) ? '&' + preamble : "&" + preamble;
        }
    
        private boolean isValidEntity(final String entity) {
            return inArray(entity, this.vAllowedEntities);
        }
    
        private static boolean inArray(final String s, final String[] array) {
            String[] newArray = array;
            int length = array.length;
            for(int i = 0; i < length; ++i) {
                String item = newArray[i];
                if (item != null && item.equals(s)) {
                    return true;
                }
            }
            return false;
        }
    
        private boolean allowed(final String name) {
            return (this.vAllowed.isEmpty() || this.vAllowed.containsKey(name)) && !inArray(name, this.vDisallowed);
        }
    
        private boolean allowedAttribute(final String name, final String paramName) {
            return this.allowed(name) && (this.vAllowed.isEmpty() || ((List)this.vAllowed.get(name)).contains(paramName));
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441

    四、测试

    package cn.mesmile.admin.modules.system.controller;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * @author zb
     * @Description
     */
    @Slf4j
    @RequestMapping("/api/v1/hello")
    @RestController
    public class HelloController {
    
        @GetMapping("/get")
        public String hello (@RequestParam("word") String word) {
            return word;
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    启动 cn.mesmile.admin.AdminApplication 应用启动类

    访问:">http://localhost:8080/api/v1/hello/get?word=

    application-dev.yml

    # xss相关配置
    security:
      xss:
        enabled: true
        # 跳过检查路径
        skip-url:
          - /api/v1/hello/**
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    1、先将要测试的路径跳过xss检查

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6V0jyy6U-1660384593712)(image/image_YTuCwryXPr.png)]

    2、修改配置文件中的跳过路径

    # xss相关配置
    security:
      xss:
        enabled: true
        # 跳过检查路径
        skip-url:
          - /api
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

  • 相关阅读:
    【Android】 Glide 官方库自带的图片裁切类
    JavaScript/jQuery功能函数集
    Dubbo admin 快速搭建
    华为云短信服务教你用C++实现Smgp协议
    C- 可变参数的简单版实现
    基于SD卡的嵌入式Linux系统镜像制作
    XiaoZaiMultiAutoAiDevices-多进程多设备自动化测试框架
    跳房子 I
    思腾云计算
    qt判断当前日期是不是当月的最后一天
  • 原文地址:https://blog.csdn.net/suprezheng/article/details/126322503