RuoYi模块功能分析:第三章 过滤器

文章目录

  • 一、application.yaml
  • 二、FilterConfig配置类
  • 三、XssFilter过滤器
    • 2.1、XssHttpServletRequestWrapper
  • 四、RepeatableFilter过滤器
    • 2.1、RepeatedlyRequestWrapper


一、application.yaml

配置xss过滤器相关信息

# 防止XSS攻击
xss:
  # 过滤开关
  enabled: true
  # 排除链接(多个用逗号分隔)
  excludes: /system/notice
  # 匹配链接
  urlPatterns: /system/*,/monitor/*,/tool/*

二、FilterConfig配置类

注入全局过滤器位于package com.ruoyi.framework.config;包下

  • xss过滤器用于防止xss工具
  • RepeatableFilter过滤器用于解决InputStream只能读取一次的问题
/**
 * Filter配置
 *
 * @author ruoyi
 */
@Configuration
public class FilterConfig
{
    @Value("${xss.excludes}")
    private String excludes;

    @Value("${xss.urlPatterns}")
    private String urlPatterns;

    /**
     * 注册xss过滤器
     * @return
     */
    @SuppressWarnings({ "rawtypes", "unchecked" }) // 忽略警告类型
    @Bean
    // xss.enabled=true则注入spring
    @ConditionalOnProperty(value = "xss.enabled", havingValue = "true")
    public FilterRegistrationBean xssFilterRegistration()
    {
        // 创建过滤器注册对象
        FilterRegistrationBean registration = new FilterRegistrationBean();

        registration.setDispatcherTypes(DispatcherType.REQUEST);

        // 添加xss过滤器
        registration.setFilter(new XssFilter());

        // 设置过滤器过滤路径
        registration.addUrlPatterns(StringUtils.split(urlPatterns, ","));

        // 设置过滤器名字
        registration.setName("xssFilter");

        // 设置过滤器优先级,越小越优先
        registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);

        // 添加自定义参数,排除路径
        Map<String, String> initParameters = new HashMap<String, String>();
        initParameters.put("excludes", excludes);
        registration.setInitParameters(initParameters);

        return registration;
    }

    /**
     * 注入InputStream过滤器,解决InputStream只能读取一次的问题
     * @return
     */
    @SuppressWarnings({ "rawtypes", "unchecked" }) // 忽略警告类型
    @Bean
    public FilterRegistrationBean someFilterRegistration()
    {
        // 创建过滤器注册对象
        FilterRegistrationBean registration = new FilterRegistrationBean();

        // 添加registration过滤器
        registration.setFilter(new RepeatableFilter());

        // 设置过滤器过滤路径
        registration.addUrlPatterns("/*");

        // 设置过滤器名
        registration.setName("repeatableFilter");

        // 设置过滤器优先级,越小越优先
        registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);

        return registration;
    }
}

三、XssFilter过滤器

防止xss攻击位于package com.ruoyi.common.filter;包下

/**
 * 防止XSS攻击的过滤器
 * 
 * @author ruoyi
 */
public class XssFilter implements Filter
{
    /**
     * 排除链接
     */
    public List<String> excludes = new ArrayList<>();

    /**
     * 初始化,获取需要排除的链接
     * @param filterConfig
     * @throws ServletException
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException
    {
        // 获取自定义参数excludes
        String tempExcludes = filterConfig.getInitParameter("excludes");

        // 判断是否为空
        if (StringUtils.isNotEmpty(tempExcludes))
        {
            // 根据,分割为字符串数组
            String[] url = tempExcludes.split(",");

            // 遍历
            for (int i = 0; url != null && i < url.length; i++)
            {
                // 添加到排除列表
                excludes.add(url[i]);
            }
        }
    }

    /**
     * 业务处理,重写request
     * @param request
     * @param response
     * @param chain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException
    {
        // 获取req,res
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        // 判断是否需要过滤
        if (handleExcludeURL(req, resp))
        {
            chain.doFilter(request, response);
            return;
        }

        // 包装处理后的request并放行
        XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
        chain.doFilter(xssRequest, response);
    }

    /**
     * 判断当前请求否需要进行xss过滤
     * @param request
     * @param response
     * @return
     */
    private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response)
    {
        // 获取请求路径
        String url = request.getServletPath();

        // 获取请求方法
        String method = request.getMethod();
        // GET DELETE 不过滤
        if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method))
        {
            return true;
        }

        // 判断当前请求是否包含在排除列表中
        return StringUtils.matches(url, excludes);
    }

    @Override
    public void destroy()
    {

    }
}

2.1、XssHttpServletRequestWrapper

防止xss攻击具体实现

/**
 * XSS过滤处理
 * 
 * @author ruoyi
 */
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper
{
    /**
     * @param request
     */
    public XssHttpServletRequestWrapper(HttpServletRequest request)
    {
        super(request);
    }

    /**
     * 重写获取请求参数值
     * @param name
     * @return
     */
    @Override
    public String[] getParameterValues(String name)
    {
        // 获取所有请求参数值
        String[] values = super.getParameterValues(name);

        // 判断是否为空
        if (values != null)
        {
            // 创建相同长度的新数组
            int length = values.length;
            String[] escapesValues = new String[length];

            // 遍历
            for (int i = 0; i < length; i++)
            {
                // 防xss攻击和过滤前后空格,自定义工具类
                escapesValues[i] = EscapeUtil.clean(values[i]).trim();
            }
            return escapesValues;
        }
        return super.getParameterValues(name);
    }

    /**
     * 重写获取InputStream
     * @return
     * @throws IOException
     */
    @Override
    public ServletInputStream getInputStream() throws IOException
    {
        // 非json类型,直接返回
        if (!isJsonRequest())
        {
            return super.getInputStream();
        }

        // 为空,直接返回,自定义工具类
        String json = IOUtils.toString(super.getInputStream(), "utf-8");
        if (StringUtils.isEmpty(json))
        {
            return super.getInputStream();
        }

        // xss过滤,自定义工具类
        json = EscapeUtil.clean(json).trim();
        byte[] jsonBytes = json.getBytes("utf-8");
        final ByteArrayInputStream bis = new ByteArrayInputStream(jsonBytes);
        return new ServletInputStream()
        {
            @Override
            public boolean isFinished()
            {
                return true;
            }

            @Override
            public boolean isReady()
            {
                return true;
            }

            @Override
            public int available() throws IOException
            {
                return jsonBytes.length;
            }

            @Override
            public void setReadListener(ReadListener readListener)
            {
            }

            @Override
            public int read() throws IOException
            {
                return bis.read();
            }
        };
    }

    /**
     * 是否是Json请求
     * 
     * @param
     */
    public boolean isJsonRequest()
    {
        String header = super.getHeader(HttpHeaders.CONTENT_TYPE);
        return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);
    }
}

四、RepeatableFilter过滤器

用于解决InputStream只能读取一次的问题,位于package com.ruoyi.common.filter;包下

/**
 * Repeatable 过滤器
 * 
 * @author ruoyi
 */
public class RepeatableFilter implements Filter
{

    @Override
    public void init(FilterConfig filterConfig) throws ServletException
    {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException
    {
        ServletRequest requestWrapper = null;
        // 判断是否为json请求
        if (request instanceof HttpServletRequest
                && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE))
        {
            requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response);
        }
        if (null == requestWrapper)
        {
            chain.doFilter(request, response);
        }
        else
        {
            chain.doFilter(requestWrapper, response);
        }
    }

    @Override
    public void destroy()
    {

    }
}

2.1、RepeatedlyRequestWrapper

具体实现,讲读取的内容缓存到byte数组中。再次读取读取数组中的内容

你可能感兴趣的:(Java开发专栏,开发语言,java)