SpringBoot整合XssFilter,Jsoup等实现请求参数的过滤,处理Xss攻击及sql注入

前言

SpringBoot整合XssFilter,Jsoup等实现请求参数的过滤,处理Xss攻击及sql注入,以下是涉及的主要类:

原理过程

Springboot中会使用FilterRegistrationBean来注册Filter,Filter是Servlet规范里面的,属于容器范围,Springboot中没有web.xml,那Springboot中,不用管Filter是如何交给Servlet容器,只见FilterRegistrationBean的如下继承图,即可知道。(现在框架很多,各个都要穷究底层,可能时间不允许?可能项目不允许?……只要熟悉了,慢慢就会知道Filter是如何交给Servlet容器”的潜规则
​​​​

实现原理及执行顺序

大致原理:有的方法可能同时需要多个filter依次对其进行过滤,这时候便需要对filter的执行进行优先级的排序。如下:

1、新建一个配置文件WEBConfig.java,
2、先生成一个过滤器的bean,
3、再把过滤器的bean注入到FilterRegistrationBean中,并设置一些属性,过滤的url,执行的顺序之类的(order的数值越小,优先级越高)即可。

具体实现:

1、在springboot 中,依赖FilterRegistrationBean 这个类,实现Filter【javax.servlet.Filter】接口,实现Filter方法
2、添加 @Configuration 注解,将自定义Filter加入过滤链

WebConfig.java

package com.AAAAAAAAAAAA.common.config;

import com.AAAAAAAAAAAA.common.interceptor.XSRFHandlerInterceptor;
import com.AAAAAAAAAAAA.common.xss.XssFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

@Configuration
@SuppressWarnings("unchecked")
public class WebConfig {

    /**
     * XssFilter Bean
     */
    @Bean
    public FilterRegistrationBean xssFilterRegistrationBean() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new XssFilter());
        filterRegistrationBean.setOrder(1);
        filterRegistrationBean.setEnabled(true);
        filterRegistrationBean.addUrlPatterns("/*");
        Map<String, String> initParameters = new HashMap<>();
        initParameters.put("excludes", "/favicon.ico,/img/*,/js/*,/css/*");
        initParameters.put("isIncludeRichText", "true");
        filterRegistrationBean.setInitParameters(initParameters);
        return filterRegistrationBean;
    }

    @Bean
    public ObjectMapper getObjectMapper(FebsProperties febsProperties) {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setDateFormat(new SimpleDateFormat(febsProperties.getTimeFormat()));
        return mapper;
    }

    @Bean //将组件注册在容器
    public WebMvcConfigurerAdapter webMvcConfigurerAdapter() {
        WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {

            //注册拦截器
            @Override
            public void addInterceptors(InterceptorRegistry registry) {
                //super.addInterceptors(registry);
                //静态资源; *.css , *.js
                //SpringBoot已经做好了静态资源映射
                registry.addInterceptor(new XSRFHandlerInterceptor()).addPathPatterns("/**")
                        .excludePathPatterns("/static/**","/css/**", "/js/**", "/fonts/**","/index","/error",
                                "/img/**", "/druid/**", "/user/regist", "/gifCode", "/*.gif", "/", "/actuator/**", "/test/**");

//                .excludePathPatterns("/addSuggest","/traffic/login","/traffic/logout","/logout","/css/**","/js/**","/fonts/**",
//                        "/img/**","/druid/**","/user/regist","/gifCode","/*.gif","/","/actuator/**","/test/**");
            }
        };
        return adapter;
    }
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("forward:/index");
        registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
    }


}

WebConfig.java的解析

设置XssFilter

filterRegistrationBean.setFilter(new XssFilter());

过滤资源

//过滤应用程序中所有资源,当前应用程序根下的所有文件包括多级子目录下的所有文件,注意这里*前有“/”
registration.addUrlPatterns("/*");
//过滤指定的类型文件资源, 当前应用程序根目录下的所有html文件,注意:*.html前没有“/”,否则错误
registration.addUrlPatterns(".html");
//过滤指定的目录下的所有文件,当前应用程序根目录下的folder_name子目录(可以是多级子目录)下所有文件
registration.addUrlPatterns("/folder_name/*");
//过滤指定文件
registration.addUrlPatterns("/index.html");
webMvcConfigurerAdapter被不被推荐使用,推荐使用WebMvcConfigurer 

友情链接:https://www.cnblogs.com/telwanggs/p/10802011.html  https://blog.csdn.net/weixin_42184538/article/details/85266822

XssFilter.java

package com.AAAAAAAAAAAA.common.xss;

import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Xss攻击拦截器
 *
 */
public class XssFilter implements Filter {

	private static Logger logger = LoggerFactory.getLogger(XssFilter.class);
	// 是否过滤富文本内容
	private boolean flag = false;

	private List<String> excludes = new ArrayList<>();

	@Override
	public void init(FilterConfig filterConfig) {
		logger.info("------------ xss filter init ------------");
		String isIncludeRichText = filterConfig.getInitParameter("isIncludeRichText");
		if (StringUtils.isNotBlank(isIncludeRichText)) {
			flag = BooleanUtils.toBoolean(isIncludeRichText);
		}
		String temp = filterConfig.getInitParameter("excludes");
		if (temp != null) {
			String[] url = temp.split(",");
			excludes.addAll(Arrays.asList(url));
		}
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest req = (HttpServletRequest) request;
		if (handleExcludeURL(req)) {
			chain.doFilter(request, response);
			return;
		}
		XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request,
				flag);
		chain.doFilter(xssRequest, response);
	}

	@Override
	public void destroy() {
		// do nothing
	}

	private boolean handleExcludeURL(HttpServletRequest request) {
		if (excludes == null || excludes.isEmpty()) {
			return false;
		}
		String url = request.getServletPath();
		return excludes.stream().map(pattern -> Pattern.compile("^" + pattern)).map(p -> p.matcher(url)).anyMatch(Matcher::find);
	}

}

XssFilter根据WebConfig中的isIncludeRichText的布尔值,进行是否拦截富文本内容,及excludes内容的过滤和其他请求的传递!,该类引入了XssHttpServletRequestWrapper包装类。

XssHttpServletRequestWrapper.java

package com.AAAAAAAAAAAA.common.xss;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.commons.lang3.StringUtils;

import com.AAAAAAAAAAAA.common.util.JsoupUtil;

import java.util.stream.IntStream;

/**
 * Jsoup过滤http请求,防止Xss攻击
 *
 */
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {

	private HttpServletRequest orgRequest;

	private boolean isIncludeRichText;

	XssHttpServletRequestWrapper(HttpServletRequest request, boolean isIncludeRichText) {
		super(request);
		orgRequest = request;
		this.isIncludeRichText = isIncludeRichText;
	}

	/**
	 * 覆盖getParameter方法,将参数名和参数值都做xss过滤
	 * 如果需要获得原始的值,则通过super.getParameterValues(name)来获取
	 * getParameterNames,getParameterValues和getParameterMap也可能需要覆盖
	 */
	@Override
	public String getParameter(String name) {
		if (("content".equals(name) || name.endsWith("WithHtml")) && !isIncludeRichText) {
			return super.getParameter(name);
		}
		name = JsoupUtil.clean(name);
		String value = super.getParameter(name);
		if (StringUtils.isNotBlank(value)) {
			value = JsoupUtil.clean(value);
		}
		return value;
	}

	/**
	 * 覆盖getHeader方法,将参数名和参数值都做xss过滤
	 * 如果需要获得原始的值,则通过super.getHeaders(name)来获取
	 * getHeaderNames 也可能需要覆盖
	 */
	@Override
	public String getHeader(String name) {
		name = JsoupUtil.clean(name);
		String value = super.getHeader(name);
		if (StringUtils.isNotBlank(value)) {
			value = JsoupUtil.clean(value);
		}
		return value;
	}
	
	@Override
	public String[] getParameterValues(String name) {
		String[] arr = super.getParameterValues(name);
		if (arr != null) {
			IntStream.range(0, arr.length).forEach(i -> arr[i] = JsoupUtil.clean(arr[i]));
		}
		return arr;
	}

	/**
	 * 获取原始的request
	 */
	private HttpServletRequest getOrgRequest() {
		return orgRequest;
	}

	/**
	 * 获取原始的request的静态方法
	 */
	public static HttpServletRequest getOrgRequest(HttpServletRequest req) {
		if (req instanceof XssHttpServletRequestWrapper) {
			return ((XssHttpServletRequestWrapper) req).getOrgRequest();
		}
		return req;
	}

}

XssHttpServletRequestWrapper为包装类,将参数名和参数值都做xss过滤。采用Jsoup过滤http请求,防止Xss攻击,将容易引起xss漏洞的半角字符直接替换成全角字符等。

JsoupUtil.java

package com.AAAAAAAAAAAA.common.util;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.safety.Whitelist;

/**
 * Xss过滤工具
 *
 */
public class JsoupUtil {

	protected JsoupUtil(){

	}

	/**
	 * 使用自带的basicWithImages 白名单
	 * 允许的便签有a,b,blockquote,br,cite,code,dd,dl,dt,em,i,li,ol,p,pre,q,small,span,
	 * strike,strong,sub,sup,u,ul,img
	 * 以及a标签的href,img标签的src,align,alt,height,width,title属性
	 */
	private static final Whitelist whitelist = Whitelist.basicWithImages();
	/*
	 * 配置过滤化参数,不对代码进行格式化
	 */
	private static final Document.OutputSettings outputSettings = new Document.OutputSettings().prettyPrint(false);
	static {
		/*
		 * 富文本编辑时一些样式是使用style来进行实现的 比如红色字体 style="color:red;" 所以需要给所有标签添加style属性
		 */
		whitelist.addAttributes(":all", "style");
	}

	public static String clean(String content) {
		return Jsoup.clean(content, "", whitelist, outputSettings);
	}

}

JsoupUtil.java使用自带的basicWithImages 白名单等,过滤掉所有的html标签,拦截XSS

XSRFHandlerInterceptor.JAVA

在webconfig.java中,引入此类。进行项目的请求信息进行拦截传递,及参数列表中的sha摘要和后台生成的sha摘要进行比较,sha摘要 匹配完成 没有被篡改,进行放行

具体代码

package com.AAAAAAAAAAAA.common.interceptor;

import com.AAAAAAAAAAAA.common.domain.ResponseBo;
import com.AAAAAAAAAAAA.traffic.util.SignUtil;
import com.alibaba.fastjson.JSON;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * @Auther: 阿啄debugIT
 * @Date: 2019/12/26
 * @Description:
 */
public class XSRFHandlerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("============进入xsrf拦截器==================");
        System.out.println(request.getMethod() + "请求地址: " + request.getRequestURL());
        //获取request中的请求参数
        Map<String, String[]> map = request.getParameterMap();
        Map<String, Object> paramMap = new HashMap<>();
        Iterator iter = map.entrySet().iterator();
        // 前台参数的sha摘要
        String shakey = "";
        // 获取所有的参数
        while (iter.hasNext()) {
            Map.Entry entry = (Map.Entry) iter.next();
            String key = (String) entry.getKey();
            String[] val = (String[]) entry.getValue();
            if (val.length > 0) {
                if ("token".equals(key)) {
                    shakey = val[0];
                } else {
                    if ("/traffic/login".equals(request.getServletPath())) {
                        val[0] = val[0].replaceAll(" ", "+");
                    }
                    paramMap.put(key, val[0]);
                }
            }
            System.out.println("key: " + key + "; value: " + val[0]);
        }
        System.out.println("前端传过来token: " + shakey);
        // 后台通过除了 key之后所有的参数 生成sha摘要
        String xsfrShaKey = SignUtil.SHA1(paramMap);
        System.out.println("后台生成的token: " + xsfrShaKey);
        // 参数列表中的sha摘要和后台生成的sha摘要进行比较
        if (!shakey.equals(xsfrShaKey)) {
            // sha摘要 匹配完成 没有被篡改,进行放行
            return true;
        } else {
            render(response);
            return false;
        }
//        return true;
    }

    private void render(HttpServletResponse response) throws Exception {
        response.setContentType("application/json;charset=UTF-8");
        OutputStream out = response.getOutputStream();
//        Result result = new Result(500, "", "请求异常");
        ResponseBo result=ResponseBo.error("请检查您输入的信息是否存在敏感信息,或者操作是否非法");
        String str = JSON.toJSONString(result);
        out.write(str.getBytes("UTF-8"));
        out.flush();
        out.close();
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object Objecthandler, ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}

能力与精力有限,难免有误,敬请指正,谢谢!
以上所有涉及代码https://download.csdn.net/download/as4589sd/12144926

发布了111 篇原创文章 · 获赞 6 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/as4589sd/article/details/104230551