前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

1、过滤器和拦截器

1.1、过滤器、拦截器、aop区别

功能 过滤器 拦截器 AOP
概念 Java Servlet 规范中的一部分,是在请求进入 Servlet 之前或响应离开 Servlet 之后对请求和响应进行预处理和后处理的组件。它可以对请求的内容、字符编码、请求参数等进行过滤和转换,也可以对响应的内容、状态码等进行修改。 是在 Web 框架(如 Spring MVC)中用于对请求进行拦截和处理的组件。它可以在请求到达控制器之前或控制器处理完请求之后执行一些额外的操作,如权限验证、日志记录、性能监控等。 一种编程范式,它允许开发者在不修改原有业务逻辑的基础上,对程序进行增强。AOP 通过将横切关注点(如日志记录、事务管理、权限验证等)从业务逻辑中分离出来,形成独立的切面(Aspect),然后在运行时将这些切面织入到目标对象的方法中。
应用场景 常用于字符编码过滤、敏感词汇过滤、请求合法性检查等场景。 常用于权限验证、日志记录、性能监控、请求参数预处理等场景。 常用于事务管理、日志记录、权限验证、缓存管理等场景。
执行顺序 请求进入 Servlet 容器时最先执行,在响应离开 Servlet 容器时最后执行。 在过滤器之后,请求进入控制器之前执行,在控制器处理完请求之后,响应返回之前执行。 在拦截器之后,目标方法执行前后执行。
使用范围 Servlet 规范的一部分,只能用于基于 ServletWeb 应用。 Web 框架提供的功能,只能用于特定的 Web 框架,如 Spring MVC 是一种通用的编程范式,可以用于各种类型的应用,不仅限于 Web 应用。
实现方式 需要实现 javax.servlet.Filter 接口,并重写 doFilter 方法。 在 Spring MVC 中,需要实现 HandlerInterceptor 接口,并重写 preHandlepostHandleafterCompletion 方法。 Spring 框架中,可以使用 XML 配置或注解(如 @Aspect@Before@After 等)来定义切面和通知。

过滤器和拦截器的拦截对象

  • 过滤器:对请求和响应进行拦截和处理,可基于 URL、请求方法、请求头、请求参数等规则进行过滤。例如可以对所有请求进行字符编码过滤。
  • 拦截器:在 Web 框架中拦截请求,通常基于 URL 匹配规则来决定是否拦截请求。比如在 Spring MVC 里,可以配置拦截器对特定 URL 模式的请求进行拦截。
过滤器和拦截器的区别
  1. 实现机制
    • 拦截器是基于 Java 的反射机制实现的。例如在 Spring MVC 中,拦截器通过反射调用控制器方法前后执行额外逻辑。
    • 过滤器是基于函数回调实现的。当请求进入 Servlet 容器时,会回调过滤器的 doFilter 方法。
  2. 依赖关系
    • 拦截器不依赖于 Servlet 容器,它是 Web 框架层面的组件。像 Spring MVC 的拦截器可以独立于 Servlet 容器进行逻辑处理。
    • 过滤器依赖于 Servlet 容器,它是 Servlet 规范的一部分,只能在 Servlet 容器中使用。
  3. 作用范围
    • 拦截器主要对控制器的请求起作用,在现代 Web 框架中可以对各种形式的控制器请求进行拦截处理。
    • 过滤器可以对几乎所有的请求起作用,包括静态资源请求等。
  4. 访问对象
    • 拦截器可以访问 action 上下文、值栈里的对象(在如 Struts 等框架中),在Spring MVC 里能访问请求、响应、控制器方法等相关信息。
    • 过滤器不能直接访问 action 上下文、值栈里的对象,它主要操作请求和响应对象。
  5. 调用时机和频率
    • 拦截器在一次请求处理过程中,preHandlepostHandleafterCompletion 方法会按顺序执行,针对每次请求都会进行相应操作。
    • 过滤器每次请求和响应经过时都会执行 doFilter 方法,而不是只在容器初始化时执行一次。
  6. IOC 容器的访问
    • 拦截器可以获取 IOC 容器中的各个 bean,能在拦截器里注入一个 service 来调用业务逻辑。例如在 Spring MVC 拦截器中可以注入 Service 层的 Bean
    • 过滤器通常不能直接获取 IOC 容器中的 bean,不过可以通过一些方式(如在 ServletContext 中获取 Spring 上下文)间接获取。

1.2、忽略URL配置

1.2.1、过滤器

Spring 中自定义过滤器(Filter)一般只有一个方法,返回值是 void,当请求到达web容器时,会探测当前请求地址是否配置有过滤器,有则调用该过滤器的方法(可能会有多个过滤器),然后才调用真实的业务逻辑,至此过滤器任务完成。过滤器并没有定义业务逻辑执行前、后等,仅仅是请求到达就执行。

特别注意:过滤器方法的入参有requestresponseFilterChain,其中FilterChain是过滤器链,使用比较简单,而requestresponse则关联到请求流程,因此可以对请求参数做过滤和修改,同时FilterChain过滤链执行完,并且完成业务流程后,会返回到过滤器,此时也可以对请求的返回数据做处理。

public class Filter_1 implements Filter {

    private FilterConfig filterConfig;

    private List<String> whiteList = Arrays.asList("/api/business/contract/notifyAsync","/api/account/notify");

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws
            IOException, ServletException {

       if (whiteList.contains(((HttpServletRequest) servletRequest).getRequestURI())) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }
        
        //添加url进行判断忽略下面的方法
           SpaceHttpServletRequestWrapper requestWrapper = new SpaceHttpServletRequestWrapper(
                (HttpServletRequest) servletRequest);
        filterChain.doFilter(requestWrapper, servletResponse);
    }

}

1.2.2、拦截器

 @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(interceptor_1)
                .addPathPatterns("/**")
                .excludePathPatterns("/develop/swagger/**");//忽略的

    }

1.3、启动顺序

谁先配置,谁先启动,DispatcherServlet之后那就顺序反过来执行了,具体看测试结果

order(2), 数字越小,越先执行

1.3、过滤器和拦截器

1、preHandle 返 回falsepostHandle 不会执行,但 afterCompletion会执行

2、拦截器链是按照配置的顺序依次执行的,当一个拦截器的 preHandle 方法返回 false 时,后续拦截器的 preHandle 方法不会再执行,并且已经执行过 preHandle 方法且返回 true 的拦截器的 postHandle 方法不会执行,但它们的 afterCompletion 方法会执行。

1. postHandle 方法

postHandle 方法是在处理器处理请求之后、视图渲染之前被调用。当拦截器链中某个拦截器的 preHandle 方法返回 false 时,请求处理流程会中断,后续的处理器不会被调用,也就不会进入到视图渲染阶段,所以已经执行过 preHandle 方法且返回 true 的拦截器的 postHandle 方法不会执行。

2. afterCompletion 方法

afterCompletion 方法是在整个请求处理完成后(包括视图渲染完成)被调用,主要用于资源清理等操作。无论请求处理是否正常完成,已经执行过 preHandle 方法且返回 true 的拦截器的 afterCompletion 方法都会执行。

1.3.1、过滤器1

package com.hlj.proj.config.filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @author HealerJean
 * @version 1.0v
 * @ClassName SpaceParamsFilter
 * @Date 2019/9/29  14:33.
 * @Description 
 */
public class Filter_1 implements Filter {

    private FilterConfig filterConfig;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws
            IOException, ServletException {
        System.out.println("############ Filter_1 在DispatcherServlet之前执行############");

        filterChain.doFilter(servletRequest, servletResponse);

        System.out.println("############ Filter_1 在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后############");
    }

    @Override
    public void destroy() {
        this.filterConfig = null;
    }
}

1.3.2、过滤器2

package com.hlj.proj.config.filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @author HealerJean
 * @version 1.0v
 * @ClassName SpaceParamsFilter
 * @Date 2019/9/29  14:33.
 * @Description 空格过滤
 */
public class Filter_2 implements Filter {

    private FilterConfig filterConfig;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws
            IOException, ServletException {
        System.out.println("############ Filter_2 在DispatcherServlet之前执行############");

        filterChain.doFilter(servletRequest, servletResponse);

        System.out.println("############ Filter_2 在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后############");
    }

    @Override
    public void destroy() {
        this.filterConfig = null;
    }
}

1.3.3、拦截器1

package com.hlj.proj.config.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author HealerJean
 * @ClassName Interceptor_1
 * @date 2019/11/21  22:08.
 * @Description
 */
@Component
@Slf4j
public class Interceptor_1 implements HandlerInterceptor {

    /**
     * 在DispatcherServlet之前执行
     * */
    public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
        System.out.println("************ Interceptor_1  在DispatcherServlet之前执行**********");
        return true;
    }

    /**
     * 在controller执行之后的DispatcherServlet之后执行
     * */
    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception {
        System.out.println("************ Interceptor_1  在controller执行之后的DispatcherServlet之后执行**********");
    }

    /**
     * 在页面渲染完成返回给客户端之前执行
     * */
    @Override
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
            throws Exception {
        System.out.println("************ Interceptor_1  在页面渲染完成返回给客户端之前执行**********");
    }
}

1.3.4、拦截器2

package com.hlj.proj.config.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author HealerJean
 * @ClassName Interceptor_1
 * @date 2019/11/21  22:08.
 * @Description
 */
@Component
@Slf4j
public class Interceptor_2 implements HandlerInterceptor {

    /**
     * 在DispatcherServlet之前执行
     * */
    public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
        System.out.println("************ Interceptor_2  在DispatcherServlet之前执行**********");
        return true;
    }

    /**
     * 在controller执行之后的DispatcherServlet之后执行
     * */
    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception {
        System.out.println("************ Interceptor_2  在controller执行之后的DispatcherServlet之后执行**********");
    }

    /**
     * 在页面渲染完成返回给客户端之前执行
     * */
    @Override
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
            throws Exception {
        System.out.println("************ Interceptor_2  在页面渲染完成返回给客户端之前执行**********");
    }
}

1.3.7、配置:注意配置顺序

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Resource
    private Interceptor_1 interceptor_1;
    @Resource
    private Interceptor_2 interceptor_2;


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(interceptor_1)
                .addPathPatterns("/**")
                .excludePathPatterns("/develop/swagger/**");

        registry.addInterceptor(interceptor_2)
                .addPathPatterns("/**")
                .excludePathPatterns("/develop/swagger/**");

    }


    @Bean
    public FilterRegistrationBean filter1() {
        FilterRegistrationBean fitler = new FilterRegistrationBean();
        fitler.setFilter(new Filter_1());
        fitler.addUrlPatterns("/hlj/*");
        fitler.setName("filter_1");
        fitler.setDispatcherTypes(DispatcherType.REQUEST);
        return fitler;
    }

    @Bean
    public FilterRegistrationBean filter2() {
        FilterRegistrationBean fitler = new FilterRegistrationBean();
        fitler.setFilter(new Filter_2());
        fitler.addUrlPatterns("/hlj/*");
        fitler.setName("filter_2");
        fitler.setDispatcherTypes(DispatcherType.REQUEST);
        return fitler;
    }

}

1.3.6、测试启动

############ Filter_1 在DispatcherServlet之前执行############
############ Filter_2 在DispatcherServlet之前执行############
************ Interceptor_1  在DispatcherServlet之前执行**********
************ Interceptor_2  在DispatcherServlet之前执行**********
 样例--------GET请求------请求参数{} com.hlj.proj.controller.DemoController.get[58]
************ Interceptor_2  在controller执行之后的DispatcherServlet之后执行**********
************ Interceptor_1  在controller执行之后的DispatcherServlet之后执行**********
************ Interceptor_2  在页面渲染完成返回给客户端之前执行**********
************ Interceptor_1  在页面渲染完成返回给客户端之前执行**********
############ Filter_2 在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后############
############ Filter_1 在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后############

谁配置在前,谁先执行,然后反过来

2、实用过滤器

主要是重写HttpServletRequestWrapper

// 404是不会进入的
// 重写getParameterValues方法,通过循环取出每一个请求结果,再对请求结果进行过滤
@Override
public String[] getParameterValues(String parameter) {

}

//  404是不会进入的
//  重写getParameter方法 对请求结果进行过滤
@Override
public String getParameter(String name) {

}

2.1、空格过滤器

2.1.1、普通请求的空格过滤器

2.1.1.1、SpaceParamsFilter

/**
 * @author HealerJean
 * @version 1.0v
 * @ClassName SpaceParamsFilter
 * @Date 2019/9/29  14:33.
 * @Description 普通请求空格过滤
 */
public class SpaceParamsFilter implements Filter {

    private FilterConfig filterConfig;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws
            IOException, ServletException {
        filterChain.doFilter(new SpaceParameHttpServletRequestWrapper(
                (HttpServletRequest) servletRequest), servletResponse);
    }

    @Override
    public void destroy() {
        this.filterConfig = null;
    }

}

2.11.2、SpaceParameHttpServletRequestWrapper


package com.hlj.proj.config.filter;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * @author HealerJean
 * @version 1.0v
 * @ClassName SpaceParameHttpServletRequestWrapper
 * @Date 2019/9/29  14:44.
 * 空格过滤处理
 * 下面的将构造器中将所有的请求的参数获取了(包括404),但是其实并没有进行空格过滤处理),
 * 一般普通请求空格过滤用下面的就可以了
 */
public class SpaceParameHttpServletRequestWrapper extends HttpServletRequestWrapper {

    /**
     * 下面的将构造器中将所有的请求的参数获取了(包括404),但是其实并没有进行空格过滤处理),
     * 一般普通请求空格过滤用下面的就可以了
     */
    public SpaceParameHttpServletRequestWrapper(HttpServletRequest servletRequest) throws IOException {
        super(servletRequest);
    }
    @Override
    public String[] getParameterValues(String parameter) {
        String[] values = super.getParameterValues(parameter);
        if (values == null) {
            return new String[0];
        }
        int count = values.length;
        String[] encodedValues = new String[count];
        for (int i = 0; i < count; i++) {
            encodedValues[i] = values[i].trim();
        }
        return encodedValues;
    }

    @Override
    public String getParameter(String name) {
        String value = super.getParameter(name);
        if (value == null) {
            return null;
        }
        return value.trim();
    }



    // private Map<String, String[]> params = new HashMap<>();
    //
    // public SpaceParameHttpServletRequestWrapper(HttpServletRequest servletRequest) throws IOException {
    //     super(servletRequest);
    //
    //     Map<String, String[]> requestMap = servletRequest.getParameterMap();
    //     this.params.putAll(requestMap);
    //     this.modifyParameterValues();
    // }
    //
    // /**
    //  *   404是不会进入的
    //  *  重写getParameterValues方法,通过循环取出每一个请求结果,再对请求结果进行过滤
    //  */
    // @Override
    // public String[] getParameterValues(String name) {
    //     return params.get(name);
    // }
    //
    // /**
    //  * 重写getParameter方法 对请求结果进行过滤
    //  **/
    // @Override
    // public String getParameter(String name) {
    //     String[]values = params.get(name);
    //     if (values == null || values.length == 0) {
    //         return null;
    //     }
    //     return values[0];
    // }
    // public void modifyParameterValues() {
    //     Set<String> set = params.keySet();
    //     Iterator<String> it = set.iterator();
    //     while (it.hasNext()) {
    //         String key = it.next();
    //         String[] values = params.get(key);
    //         values[0] = values[0].trim();
    //         params.put(key, values);
    //     }
    // }


}


2.1.1.3、过滤器配置

public class InterceptorConfig implements WebMvcConfigurer {

@Bean
    public FilterRegistrationBean spaceJsonFilter() {
        FilterRegistrationBean fitler = new FilterRegistrationBean();
        fitler.setFilter(new SpaceJsonFilter());
        fitler.addUrlPatterns("/hlj/*");
        fitler.setName("SpaceJsonFilter");
        fitler.setDispatcherTypes(DispatcherType.REQUEST);
        return fitler;
    }
}

2.1.2、Json请求空格过滤

2.1.2.1、SpaceJsonFilter

package com.hlj.proj.config.filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @author HealerJean
 * @version 1.0v
 * @ClassName SpaceParamsFilter
 * @Date 2019/9/29  14:33.
 * @Description Josn请求空格过滤
 */
public class SpaceJsonFilter implements Filter {

    private FilterConfig filterConfig;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws
            IOException, ServletException {
        filterChain.doFilter(new SpaceJsonHttpServletRequestWrapper(
                (HttpServletRequest) servletRequest), servletResponse);
    }

    @Override
    public void destroy() {
        this.filterConfig = null;
    }
}

2.1.2.2、SpaceJsonHttpServletRequestWrapper

package com.hlj.proj.config.filter;


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.hlj.proj.utils.JsonUtils;
import org.apache.commons.lang3.StringUtils;

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.*;

/**
 * @author HealerJean
 * @version 1.0v
 * @ClassName SpaceParameHttpServletRequestWrapper
 * @Date 2019/9/29  14:44.
 * @Description
 */
public class SpaceJsonHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private byte[] body;

    /**
     * 预先出初始化数据
     */
    public SpaceJsonHttpServletRequestWrapper(HttpServletRequest servletRequest) throws IOException {
        super(servletRequest);
        BufferedReader reader = servletRequest.getReader();
        String line = null;
        StringBuilder sb = new StringBuilder();
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
        String json = sb.toString();
        if (StringUtils.isNotBlank(json)) {
            json = JsonUtils.toJsonString(trimSpace(json));
        }
        body = json.getBytes("utf-8");
    }


    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    /**
     * 重写getInputStream方法  Json类型的请求参数必须通过流才能获取到值
     */
    @Override
    public ServletInputStream getInputStream() {
        ByteArrayInputStream bis = new ByteArrayInputStream(body);
        return new ServletInputStream() {
            @Override
            public int read() {
                return bis.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

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

            @Override
            public void setReadListener(ReadListener readListener) {
            }

        };
    }


    public static Map<String, Object> trimSpace(String jsonString) {
        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<Object> list = new ArrayList<>();
                Iterator<Object> it = ((JSONArray) o).iterator();
                while (it.hasNext()) {
                    Object obj = it.next();
                    if (obj instanceof JSONObject) {
                        list.add(trimSpace(obj.toString()));
                    } else {
                        list.add(obj.toString().trim());
                    }
                }
                map.put(k.toString(), list);
            } else if (o instanceof JSONObject) {
                // 如果内层是json对象的话,继续解析
                map.put(k.toString(), trimSpace(o.toString()));
            } else {
                // 如果内层是普通对象的话,直接放入map中
                map.put(k.toString(), o.toString().trim());
            }
        }
        return map;
    }
}


2.1.2.3、过滤器配置

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {

    @Bean
    public FilterRegistrationBean spaceFilter() {
        FilterRegistrationBean fitler = new FilterRegistrationBean();
        fitler.setFilter(new SpaceFilter());
        fitler.addUrlPatterns("/*");
        fitler.setName("SpaceFilter");
        fitler.setDispatcherTypes(DispatcherType.REQUEST);
        return fitler;
    }
}

2.1.3、结合上面二者

2.1.3.1、SpaceFilter

package com.hlj.proj.config.filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @author HealerJean
 * @version 1.0v
 * @ClassName SpaceParamsFilter
 * @Date 2019/9/29  14:33.
 * @Description 空格过滤
 */
public class SpaceFilter implements Filter {

    private FilterConfig filterConfig;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws
            IOException, ServletException {
        filterChain.doFilter(new SpaceHttpServletRequestWrapper(
                (HttpServletRequest) servletRequest), servletResponse);
    }

    @Override
    public void destroy() {
        this.filterConfig = null;
    }
}

2.1.3.2、SpaceHttpServletRequestWrapper

package com.hlj.proj.config.filter;


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.hlj.proj.utils.JsonUtils;
import org.apache.commons.lang3.StringUtils;

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.*;

/**
 * @author HealerJean
 * @version 1.0v
 * @ClassName SpaceParameHttpServletRequestWrapper
 * @Date 2019/9/29  14:44.
 * @Description
 */
public class SpaceHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private byte[] body;

    /**
     * 预先出初始化数据
     */
    public SpaceHttpServletRequestWrapper(HttpServletRequest servletRequest) throws IOException {
        super(servletRequest);
        BufferedReader reader = servletRequest.getReader();
        String line = null;
        StringBuilder sb = new StringBuilder();
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
        String json = sb.toString();
        if (StringUtils.isNotBlank(json)) {
            json = JsonUtils.toJsonString(trimSpace(json));
        }
        body = json.getBytes("utf-8");
    }


    @Override
    public String[] getParameterValues(String parameter) {
        String[] values = super.getParameterValues(parameter);
        if (values == null) {
            return new String[0];
        }
        int count = values.length;
        String[] encodedValues = new String[count];
        for (int i = 0; i < count; i++) {
            encodedValues[i] = values[i].trim();
        }
        return encodedValues;
    }

    @Override
    public String getParameter(String name) {
        String value = super.getParameter(name);
        if (value == null) {
            return null;
        }
        return value.trim();
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    /**
     * 重写getInputStream方法  Json类型的请求参数必须通过流才能获取到值
     */
    @Override
    public ServletInputStream getInputStream() {
        ByteArrayInputStream bis = new ByteArrayInputStream(body);
        return new ServletInputStream() {
            @Override
            public int read() {
                return bis.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

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

            @Override
            public void setReadListener(ReadListener readListener) {
            }

        };
    }


    public static Map<String, Object> trimSpace(String jsonString) {
        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<Object> list = new ArrayList<>();
                Iterator<Object> it = ((JSONArray) o).iterator();
                while (it.hasNext()) {
                    Object obj = it.next();
                    if (obj instanceof JSONObject) {
                        list.add(trimSpace(obj.toString()));
                    } else {
                        list.add(obj.toString().trim());
                    }
                }
                map.put(k.toString(), list);
            } else if (o instanceof JSONObject) {
                // 如果内层是json对象的话,继续解析
                map.put(k.toString(), trimSpace(o.toString()));
            } else {
                // 如果内层是普通对象的话,直接放入map中
                map.put(k.toString(), o.toString().trim());
            }
        }
        return map;
    }

}

2.1.3.3、配置过滤器

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {

  @Bean
    public FilterRegistrationBean spaceJsonFilter() {
        FilterRegistrationBean fitler = new FilterRegistrationBean();
        fitler.setFilter(new SpaceFilter());
        fitler.addUrlPatterns("/hlj/*");
        fitler.setName("SpaceJsonFilter");
        fitler.setDispatcherTypes(DispatcherType.REQUEST);
        return fitler;
    }
}

2.2、Xss 攻击过滤器

2.2.1、XssFilter

package com.hlj.proj.config.filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;


/**
 * @author HealerJean
 * @ClassName XssFilter
 * @Date 2019/11/21  17:38.
 * @Description 防Xss攻击Filter ,只能阻止普通的http请求,不能阻止Json请求,如果需要组织Json请求则需要按照空格过滤器一样进行配置
 */
public class XssFilter implements Filter {

    private FilterConfig filterConfig;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws
            IOException, ServletException {
        filterChain.doFilter(new XssHttpServletRequestWrapper(
                (HttpServletRequest) servletRequest), servletResponse);
    }

    @Override
    public void destroy() {
        this.filterConfig = null;
    }
}

2.2.2、SpaceParameHttpServletRequestWrapper

package com.hlj.proj.config.filter;


import org.apache.commons.lang3.StringEscapeUtils;

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


public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {

    public XssHttpServletRequestWrapper(HttpServletRequest servletRequest) {
        super(servletRequest);
    }

    /**
     * 只有确定的controller才回访问这里
     */
    @Override
    public String[] getParameterValues(String parameter) {
        String[] values = super.getParameterValues(parameter);
        if (values == null) {
            return new String[0];
        }
        int count = values.length;
        String[] encodedValues = new String[count];
        for (int i = 0; i < count; i++) {
            encodedValues[i] = cleanXSS(values[i]);
        }
        return encodedValues;
    }

    @Override
    public String getParameter(String parameter) {
        String value = super.getParameter(parameter);
        if (value == null) {
            return null;
        }
        return cleanXSS(value);
    }

    @Override
    public String getHeader(String name) {
        String value = super.getHeader(name);
        if (value == null) {
            return null;
        }
        return cleanXSS(value);
    }

    private String cleanXSS(String value) {
        return StringEscapeUtils.escapeXml10(value);
    }
}

2.2.3、配置过滤器

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {

 @Bean
    public FilterRegistrationBean xssSqlFilter() {
        FilterRegistrationBean fitler = new FilterRegistrationBean();
        fitler.setFilter(new XssFilter());
        fitler.addUrlPatterns("/hlj/*");
        fitler.setName("XssFilter");
        fitler.setDispatcherTypes(DispatcherType.REQUEST);
        return fitler;
    }
}

3、实用拦截器

3.1、打印访问日志

@Component
@Slf4j
public class UrlInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        log.info("admin:请求地址:[{}];访问ip:[{}]", httpServletRequest.getRequestURL(), IpUtil.getIp());
        return true;
    }

}

3.2、自定义注解方法请求次数限制

3.2.1、设置默认方法允许进入的次数

@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EntryTimes {

    /**
     * 方法允许进入的次数
     * @return
     */
    int value() default 1;

    /**
     * 可以的前缀 url前缀
     * @return
     */
    String prefix() default "";
}

3.2.2、aop拦截

import com.duodian.admore.zhaobutong.annotation.EntryTimes;
import com.duodian.admore.zhaobutong.bean.Code;
import com.duodian.admore.zhaobutong.bean.Response;
import com.duodian.admore.zhaobutong.util.AesUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 控制每个用户访问Controller方法的次数
 * Created by fengchuanbo on 2017/5/25.
 */
@Aspect
@Component
public class MethodEntryTimesLimitInterceptor {

    private static final String METHOD_CAN_ENTRY_TIMES_KEY = "method:entry:times:";

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 需要有 EntryTimes 标注,并且第一个参数需要是 AuthUser才可以
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around("@annotation(com.duodian.admore.zhaobutong.annotation.EntryTimes)")
    public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String token = request.getParameter("token");
        String aes = AesUtils.LoginDecrypt(token);
        Long userId = Long.valueOf(aes.split("#")[0]);
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        EntryTimes annotation = method.getAnnotation(EntryTimes.class);
        int times = annotation.value();
        String key = METHOD_CAN_ENTRY_TIMES_KEY + ":" + annotation.prefix() + ":" +  userId;
        // 没有整个方法使用一个redis链接,是为了方式方法执行时间过长一直占用redis链接。
        Long increment = getEntryTimes(key);
        Object retVal;
        try {
            // 放在try里面,才能执行finally
            if (increment > times){
                // 设置十秒钟超时,防止finally在系统崩溃或重启时没有执行造成用户不能操作。
                expireKey(key,10);
                return Response.of(Code.ACTION_FREQUENT);
            }
            //调用核心逻辑
            retVal = pjp.proceed();
        }finally {
            deleteKey(key);
        }
        return retVal;
    }

    private Long getEntryTimes(String key){
        return stringRedisTemplate.opsForValue().increment(key,1);
    }

    private void deleteKey(String key){
        stringRedisTemplate.delete(key);
    }
//添加缓存紧急数据的增加
    private void expireKey(String key, int seconds){
        stringRedisTemplate.boundValueOps(key).expire(seconds, TimeUnit.SECONDS);
    }
}


//或者可以参考 opt项目
    public Long increase(final String key,final Long seconds){
        return redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection redisConnection) throws DataAccessException {
                redisConnection.select(DB_INDEX);
                byte[] keyBytes = stringRedisSerializer.serialize(key);

                Long val = redisConnection.incr(keyBytes);
                redisConnection.expire(keyBytes,seconds);
                return val;
            }
        });
    }


public String getRunStatus(){
    return redisTemplate.execute(new RedisCallback<String>() {
        public String doInRedis(RedisConnection redisConnection) throws DataAccessException {
            redisConnection.select(DB_INDEX);

            byte[] result = redisConnection.get(stringRedisSerializer.serialize(KEY));
            return stringRedisSerializer.deserialize(result);
        }
    });
}


ContactAuthor