拦截器和过滤器

过滤器

过滤器实际上就是对web资源进行拦截,做一些处理后再交给下一个过滤器或servlet处理,通常都是用来拦截request进行处理的,也可以对返回的response进行拦截处理

为什么使用过滤器

在Web开发中,经常会有这样的需求:在所有接口中去除用户输入的非法字符,以防止引起业务异常。要实现这个功能,可以有很多方法,如:

在前端参数传入时进行校验,先过滤非法字符,然后返回用户界面提示用户重新输入。
后端接收前端没有过滤的数据,然后过滤非法字符。
利用filter处理项目中所有非法字符。
很明显,前两种实现方法会存在重复代码,因为每个前端页面或后端都需要处理,这样会导致代码很难维护。如果用过滤器来实现,则只需要用过滤器对所有接口进行过滤处理。这样非常方便,同时不会出现冗余代码。

如何使用过滤器

(1)新建类,实现Filter抽类类。

(2)重写init、doFilter、destroy方法。

(3)在Spring Boot入口类中添加注解@ServletComponentScan,以注册Filter。

init():该方法在容器启动初始化过滤器时被调用,它在Filter的整个生命周期只会被调用一次,这个方法必须执行成功,否则过滤器会不起作用。

doFilter():容器中的每一次请求都会调用该方法,FilterChain用来调用下一个过滤器Filter。

destroy():容器销毁时被调用。一般在方法中销毁或关闭资源,也只会被调用一次。

首先写一个简单的controller

@RestController
@Slf4j
@RequestMapping("/api/filter")
public class FilterUserController {
    @GetMapping("/getUserList")
    public List<String> getUser() {
        log.info("开始业务逻辑处理。");
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        log.info("业务逻辑处理结束。");
        return list;
    }
}

定义一个过滤器

@Slf4j
@Order(1)  //如果有多个Filter,则序号越小,越早被执行
//@Component//无需添加此注解,在启动类添加@ServletComponentScan注解后,会自动将带有@WebFilter的注解进行注入!
//这里的urlPatterns为接口里的路径过滤条件
@WebFilter(filterName = "timeFilter", urlPatterns = "/api/filter/*")
public class TimeFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("初始化过滤器:{}", filterConfig.getFilterName());
    }
    @Override
    public void destroy() {
        log.info("销毁过滤器");
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("开始执行");
        long startTime = System.currentTimeMillis();
        filterChain.doFilter(servletRequest, servletResponse);
        long endTime = System.currentTimeMillis();
        log.info("请求:{},耗时:{}ms", getUrlFrom(servletRequest), (endTime - startTime));
        log.info("结束执行");
    }
    private String getUrlFrom(ServletRequest servletRequest) {
        if (servletRequest instanceof HttpServletRequest) {
            return ((HttpServletRequest) servletRequest).getRequestURL().toString();
        }
        return "";
    }
}

在启动类上添加注解

@ServletComponentScan(basePackages = "com.binlog.study.filter")

拦截器

SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。

SpringMVC拦截器提供三个方法分别是preHandle、postHandle、afterCompletion,我们就是通过重写这几个方法来对用户的请求进行拦截处理的。

preHandle() :这个方法将在请求处理之前进行调用。「注意」:如果该方法的返回值为false ,将视为当前请求结束,不仅自身的拦截器会失效,还会导致其他的拦截器也不再执行。
postHandle():只有在 preHandle() 方法返回值为true 时才会执行。会在Controller 中的方法调用之后,DispatcherServlet 返回渲染视图之前被调用。「有意思的是」:postHandle() 方法被调用的顺序跟 preHandle() 是相反的,先声明的拦截器 preHandle() 方法先执行,而postHandle()方法反而会后执行。
afterCompletion():只有在 preHandle() 方法返回值为true 时才会执行,在整个请求结束之后, DispatcherServlet 渲染了对应的视图之后执行。

如何使用拦截器

首先定义controller

@RestController
@Slf4j
@RequestMapping("/api/interceptor")
public class InterceptorUserController {
    @GetMapping("/setSession")
    @ResponseBody
    public Object setSession(HttpServletRequest request) {
        //将用户信息存放到session中
        InterceptorUserEntity user = new InterceptorUserEntity();
        user.setId(001);
        user.setName("张三");
        request.getSession().setAttribute("user", user);
        return "已进行登录!";
    }
    /**
     * 用户登录后跳转到首页
     *
     * @return
     */
    @GetMapping("/index")
    public Object index() {
        return "这里是首页!";
    }
    /**
     * 登录页面
     *
     * @return
     */
    @GetMapping("/login")
    public Object login() {
        return "请进行登录!";
    }
}

实现拦截器

@Component
@Slf4j
public class UserInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //业务拦截相关规则
        //从session中获取用户的信息
        InterceptorUserEntity user = (InterceptorUserEntity) request.getSession().getAttribute("user");
        //判断用户是否登录
        if (null == user) {
            response.sendRedirect(request.getContextPath() + "/api/interceptor/login");
            return false;
        }
        //需要返回true,否则请求不会被控制器处理
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后),如果异常发生,则该方法不会被调用");
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("在整个请求结束之后被调用,也就是在DispatcherServlet渲染了对应的视图之后执行(主要是用于进行资源清理工作)");
    }
}

使用@Configuration注解写一个拦截器的配置文件

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Autowired
    private UserInterceptor userInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(userInterceptor).addPathPatterns("/api/interceptor/**").excludePathPatterns("/**/login", "/**/setSession");
    }
}

拦截器和过滤器谁先执行

在Spring MVC中,拦截器先于过滤器执行。

当客户端发送一个请求时,请求会先被过滤器(Filter)拦截,再被拦截器(Interceptor)拦截。过滤器是在Servlet容器中执行的,而拦截器是在Spring容器中执行的。过滤器可以对请求进行处理、修改或者直接拒绝请求,而拦截器则可以在Controller方法执行前或执行后对请求进行一些处理,比如日志记录、请求参数验证等。

因此,可以把过滤器看作是Servlet容器提供的组件,用于对请求和响应进行处理,而拦截器则是Spring框架提供的组件,用于在Controller方法执行前后对请求进行拦截和处理。在一个Web应用程序中,通常会同时使用拦截器和过滤器来完成不同的任务

posted @ 2023-02-22 19:56  刚刚好。  阅读(183)  评论(1编辑  收藏  举报