【笔记07】JavaEE 中的 Filter、拦截器和 Shiro

简介: ① 当提到【登陆】二字的时候,我总能在脑子里面想起三个词:【过滤器】、【拦截器】、【Shiro】② 在我脑中它们三个都有拦截请求的功能,但具体是干啥的我无法说出来。根据费曼学习法,如果我无法说出来,就代表我不会。③ 接下来学习一下过滤器、拦截器和 Shiro,并记录一下。

前言

① 当提到【登陆】二字的时候,我总能在脑子里面想起三个词:【过滤器】、【拦截器】、【Shiro】
② 在我脑中它们三个都有拦截请求的功能,但具体是干啥的我无法说出来。根据费曼学习法,如果我无法说出来,就代表我不会。
③ 接下来学习一下过滤器、拦截器和 Shiro,并记录一下。

一、过滤器(Filter)

(1) 概念

① 官方文档:A filter is an object that performs filtering tasks on either the request to a resource (a servlet or static content), or on the response from a resource, or both.
过滤器是一个对象,它对资源(servlet 或静态资源)的请求或对来自资源的响应执行筛选(过滤)任务,或对两者都执行筛选(过滤)任务。

② 其他同学的博客:当访问服务器的资源时,过滤器可以把请求拦截下来,去执行一些特殊的功能。过滤器页可以把响应拦截下来,控制某些东西是否可以返回给客户端。

③ 提炼一下:Filter 用来拦截、过滤、筛选客户端的请求和服务器的响应。

(2) 过滤器的一般作用

  • Authentication Filters 身份验证(登陆)
  • Logging and Auditing Filters 日志和审核
  • Image conversion Filters 图片转换
  • Data compression Filters 数据压缩
  • Encryption Filters 加密过滤
  • Filters that trigger resource access events 触发资源访问事件的过滤器

(3) 在代码中使用

在 SpringBoot 项目中使用 Filter 的时候,在 Filter 类上要加上 @Component 注解,否则没有效果。【迷糊点】

下面通过 @WebFilter 的方式使用 Filter,暂时不知道 xml 配置文件的方式在 SpringBoot 项目中如何使用。

@WebFilter("/*") // 拦截全部的请求
// SpringBoot 项目中使用 Filter 的话,@Component 注解是必要的
@Component
public class AppleFilter implements Filter {

    /**
     * 客户端的每次请求都会经过 Filter 的 doFilter 方法,从而实现过滤功能
     * ① chain.doFilter 前的代码是对【请求】执行过滤功能
     * ② chain.doFilter 后的代码是对【响应】执行过滤功能
     */
    @Override
    public void doFilter(ServletRequest request,
                         ServletResponse response,
                         FilterChain filterChain)
            throws IOException, ServletException {
        System.out.println("① chain.doFilter 前的代码是对【请求】执行过滤功能");

        filterChain.doFilter(request, response);

        System.out.println("② chain.doFilter 后的代码是对【响应】执行过滤功能");
    }

}
@RestController
@RequestMapping("/apples")
public class AppleController {

    @GetMapping("/apple1")
    @ResponseBody
    public String apple1() {
        System.out.println("AppleController - apple1");
        return "AppleController - apple1";
    }

}

执行结果:
① chain.doFilter 前的代码是对【请求】执行过滤功能
AppleController - apple1
② chain.doFilter 后的代码是对【响应】执行过滤功能

Filter 是一个接口,Filter 中有三个方法:init()、doFilter()、destroy()
其中,init() 和 destroy() 是默认(default)方法,无需实现(implements)Filter 的类必须实现这两个方法。doFilter 不是默认方法,实现(implements)Filter 接口的类必须实现 doFilter 方法。

init() 方法被调用:called by the web container to indicate to a filter that it is being placed into service.
Filter 的 init 方法由 Web 容器调用,用于指示某个 Filter 被应用于到了服务之中。
Filter 的 init 方法在启动 SpringBoot 项目(运行 main 方法)的过程中被调用。

doFilter() 方法被调用:called by the container each time a request/response pair is passed through the chain due to a client request for a resource at the end of the chain.

destroy() 方法被调用:called by the web container to indicate to a filter that it is being taken out of service.
destroy() 方法由 Web 容器调用,用于指示某个 Filter 已经从服务中停止了。

下面的代码是我第一次学习 Filter 的时候写的,现在来重读一下。
该代码作用:通过拦截器进行登录验证。
① chain.doFilter 之前的代码是对【请求】进行过滤,chain.doFilter 之后的代码是对【响应】进行过滤
② 在下面的代码中,先把 ServletRequest 类型的 req 和 ServletResponse 类型的 resp 转换为 HttpServletRequest 的类型的 request 和 HttpServletResponse 类型的 response。便于对客户端的请求进行筛选和对响应进行处理。
③ HttpServletRequest 的类型的 request 的 getRequestURI 方法可以获取到客户端请求的部分路径(不包含协议、域名、端口号)的字符串,可通过客户端路径进行筛选,判断哪些路径需要进行登录,哪些路径需要直接通过(不用登录)。
④ 调用 chain.doFilter 方法后,才可进入下一链条调用。

public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
  // 需要通过 HttpServletRequest 类型的 request 拿到 Session
  HttpServletRequest request = ((HttpServletRequest) req);
  // 需要通过HttpServletResponse类型的response重定向
  HttpServletResponse response = (HttpServletResponse) resp;
  String uri = request.getRequestURI();

  if (uri.contains("/asset/")) { // 资源文件优先放开
    chain.doFilter(req, resp);
  } else if (uri.contains("/admin") || uri.contains("/remove") || uri.contains("save")) { // 需要进行过滤的
    Object user = request.getSession().getAttribute("user");
    if (user != null) { // 登录成功过
      chain.doFilter(req, resp);
    } else { // 没有登录成功过
      response.sendRedirect(request.getContextPath() + "/page/login.jsp");
    }
  } else { // (login.jsp、captcha)
    System.out.println(uri);
    chain.doFilter(req, resp);
  }
}

(4) Filter 的优先级

当有多个 Filter 的时候,哪个 Filter 先执行?
① 通过注解方式使用 Filter:按照 Filter 的字母顺序,小的哪个 Filter 先执行。
如:CharsetFilter 和 LoginFilter,CharSetFilter 的(C)比 LoginFilter 的(L)小,所以 CharSetFilter 先执行。
② 通过 xml 配置文件使用 Filter:先配置哪个 Filter 就先执行哪个 Filter。

下面是测试代码:


@Component
@WebFilter("/*")
public class CharsetFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("CharsetFilter");
        chain.doFilter(request, response);
    }
}

@Component
@WebFilter("/*")
public class LoginFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("LoginFilter");
        chain.doFilter(request, response);
    }
}
@RestController
@RequestMapping("/filterOrders")
public class FilterOrderController {

    @GetMapping("/filterOrder1")
    @ResponseBody
    public String filterOrder1() {
        System.out.println("FilterOrderController - filterOrder1");
        return "FilterOrderController - filterOrder1";
    }

}

执行结果:
CharsetFilter
LoginFilter
FilterOrderController - filterOrder1

(5) Filter 的生命周期方法

public class LoginFilter implements Filter {
    // 项目从 Web 容器中取消部署时调用(当 Filter 从 Web 容器中移除时调用)
    public void destroy() {
        // 一般做资源的回收操作
    }

    public void doFilter() {

    }

    // 项目部署到 Web 容器时调用(当 Filter 被加载到 Web 容器中)
    public void init() {
        // 一般做一些资源的一次性加载、初始化
    }
}

(6) @WebFilter 的 dispatchTypes 属性

  • REQUEST:只拦截客户端直接发送的请求【默认值】
  • FORWARD:只拦截客户端转发的请求

    @WebFilter(value = "/*", dispatcherTypes = DispatcherType.FORWARD)

(7) 其他点赞高的创作者的文章

关于 Java 的 Filter(阅读量高)


二、SpringBoot 项目中配置 SpringMVC

  • 默认情况下,SpringBoot 已经内置了很多 SpringMVC 的常用配置
  • 若想在 SpringBoot 的默认配置的基础上增加自己的配置,可使用 WebMvcConfigurer 接口
  • 在 SpringBoot 项目中使用 SpringMVC 的拦截器也需要配置一下(如下面的代码所示)
/**
 * 在 SpringBoot 对 SpringMVC 默认配置的基础上增加自己的配置
 */
@Configuration
//@EnableWebMvc // 完全自定义 SpringMVC 的配置
public class SpringMVCConfig implements WebMvcConfigurer {

    /**
     * 配置拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {

    }
}
  • 若想完全自定义 SpringMVC 的配置,可使用 @EnableWebMvc 注解


三、拦截器(Interceptor)

在 SpringMVC 框架中,拦截器是 Java 面试中的常客。

(1) 简介

拦截器(Interceptor)的功能与过滤器(Filter)类似,但有本质的区别。

① 过滤器(Filter)

  • 是 Servlet 规范的一部分
  • 能过滤任意请求,如 html、js、jsp
  • 请求抵达 Servlet 之前,响应抵达客户端之前都可过滤
  • 常用于:编码设置、登录校验等

② 拦截器(Interceptor)

  • 是 SpringMVC 的一部分
  • 只能拦截 DispatcherServlet 能拦截到的内容,一般用来拦截 controller
  • 常用于:抽取 controller 的公共代码

除了 jsp,DispatcherServlet 都可以拦截。

(2) 使用拦截器

① 使用

  • 创建一个拦截器类(QYInterceptor)实现 HandlerInterceptor 接口
  • 创建一个 SpringMVC 配置类(QYWebMvcConfig),实现 WebMvcConfigurer 接口。实现该接口的 addInterceptor 方法
/**
 * SpringMVC 的拦截器
 */
public class QYInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        System.out.println("QYInterceptor - preHandle - " + request.getRequestURL());
        // 必须返回 true 才会进入下一个步骤
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response,
                           Object handler, // 通过 handler 可得知是哪个控制器的哪个方法
                           ModelAndView modelAndView) throws Exception {
        System.out.println("QYInterceptor - postHandle - " + handler);
    }

    /**
     * 请求响应结束后调用 afterCompletion
     * 请求结果返回给客户端后调用 afterCompletion
     */
    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler,
                                Exception ex) throws Exception {
        // 下面这种代码执行完之后 afterCompletion 方法被调用                        
        // response.sendRedirect("");
        // request.getRequestDispatcher("").forward(request, response);
        // response.getWriter().write("");
        System.out.println("QYInterceptor - afterCompletion");
    }

}
/**
 * 在 SpringBoot 对 SpringMVC 默认配置的基础上增加自己的配置
 */
@Configuration
//@EnableWebMvc // 完全自定义 SpringMVC 的配置
public class QYMVCConfig implements WebMvcConfigurer {

    /**
     * 配置拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        String[] pathPatterns = new String[]{"/sysUsers/login", "/asset/**", "/**/*.html"};
        registry.addInterceptor(new QYInterceptor())
                .addPathPatterns("/**") // 拦截全部路径
                .excludePathPatterns(pathPatterns);
    }
}

执行结果:
在这里插入图片描述

  • HandlerInterceptor 接口中方法的都是默认方法(在接口中有该方法的默认实现)
  • HandlerInterceptor 接口中有 3 个默认方法,分别是:preHandle、postHandle、afterCompletion

② 三个默认方法的解析:

  • preHandle:在 controller 的处理方法之前调用(该方法返回值为 true 才执行 controller 里面的内容)

-- 通常在 preHandle 中进行初始化、请求预处理(可实现登录验证)
-- preHandle 返回 true 才会执行后面的调用。若返回 false,不会调用 controller 处理方法、postHandle 和 afterCompletion
-- 当有多个拦截器时,preHandle 按照正序执行

  • postHandle:在 controller 的处理方法之后,DispatcherServlet 进行视图渲染之前调用

-- 可在 postHandle 中进行请求后续加工处理操作
-- 当有多个拦截器时,postHandle 按照逆序执行

  • afterCompletion:在 DispatcherServlet 进行视图渲染之后调用

-- 一般在这里进行资源回收操作
-- 当有多个拦截器时,afterCompletion按照逆序执行


四、Shiro

......

相关文章
|
4月前
javaWeb过滤器Filter(一)
javaWeb过滤器Filter(一)
|
8月前
|
前端开发 Java 数据库连接
【SpringMVC】JSR 303与interceptor拦截器快速入门
JSR 303是Java规范请求(Java Specification Request)的缩写,意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。 JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,Hibernate Validator 是 Bean Validation 的参考实现 . Hibe
QGS
|
存储 调度 数据安全/隐私保护
入门SpringMVC之Interceptor拦截器
SpringMVC中的Interceptor拦截器,它的主要作用是拦截指定的用户需求,并进行相应的预处理与后处理。
QGS
66 0
|
设计模式 监控 前端开发
【JavaWeb】Filter过滤器到底是什么
【JavaWeb】Filter过滤器到底是什么
【JavaWeb】Filter过滤器到底是什么
Java:SpringBoot集成filter过滤器、interceptor拦截器
Java:SpringBoot集成filter过滤器、interceptor拦截器
159 0
Java:SpringBoot集成filter过滤器、interceptor拦截器
|
存储 JSON Java
Springboot Filter 多过滤器的使用
Springboot Filter 多过滤器的使用
333 0
Springboot Filter 多过滤器的使用
Springboot 使用Filter, 拦截器 执行了两次 问题剖析
Springboot 使用Filter, 拦截器 执行了两次 问题剖析
610 0
Springboot 使用Filter, 拦截器 执行了两次 问题剖析
|
安全 Java 容器
SpringMVC - Filter、Interceptor、AOP 区别
SpringMVC - Filter、Interceptor、AOP 区别
179 0
SpringMVC - Filter、Interceptor、AOP 区别
|
前端开发 JavaScript Java
java SpringMVC Filter登录拦截器
java SpringMVC Filter登录拦截器
141 0
|
Java Spring 容器
如何使用Spring管理Filter和Servlet
如果要在filter或者servlet中使用spring容器管理业务对象,通常需要使用WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext())来获得WebApplicationContext,然后调用WebApplicationContext.getBean(“beanName”)来获得对象的引用,这实际上是使用了依赖查找来获得对象,并且在filter或者servlet代码中硬编码了应用对象的bean名字。
280 1

相关课程

更多