一、前言

再说拦截器之前,先讲讲过滤器,想必我们对过滤器是非常熟悉的,在Servlet里面的web.xml里面的<filter>标签。那么许多人老是被这两个东西搞得晕头转向的。这里我举个例子说明他俩的区别:


  • 过滤器:当你有一堆东西的时候,你只希望选择符合你要求的某一些东西。定义这些要求的工具,就是过滤器。就好比在一堆沙子中淘出金子,把沙子过滤掉
  • 拦截器:当你有了金子,准备高兴兴的准备回家娶媳妇,路上突然遇到了强盗
    • 情况一:必须忍痛割爱,把金子给他,但是你又于心不忍,于是偷偷藏了一些,仅存的金子又分出一些给了强盗,这算保证住了小命。
    • 情况二:你坚决不给,于是强盗把你杀了,金子拿了,最后人才两矢。

小结
过滤器 就是直接pass大多数的,留下一部分的精英
拦截器 就是想尽办法在从这些精英中在阻拦你前进,甚至是不让你继续通过,能通过的一定是最强的。

所以过滤器一定是在拦截器之前执行
在这里插入图片描述

一、spring中的拦截器

在web开发中,拦截器是经常用到的功能。它可以帮我们验证是否登陆、过滤静态资源等。

在Spring中的拦截器分为了两种:

  1. HanlerInterceptor接口:拦截请求地址的拦截器
  2. MethodInterceptor接口:拦截方法的拦截器

1.1 HandlerInterceptor拦截器🔥

HandlerInterceptor是springMVC项目中的拦截器,它拦截的目标是请求的地址, 比MethodInterceptor先执行。

实现一个HandlerInterceptor拦截器可以直接实现HandlerInterceptor接口,也可以继承HandlerInterceptorAdapter类。

这两种方法殊途同归,其实HandlerInterceptorAdapter也就是声明了HandlerInterceptor接口中所有方法的默认实现,而我们在继承他之后只需要重写必要的方法。

下面我们来看下HandlerInterceptor的源码:

public interface HandlerInterceptor {

	default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return true;
	}

	
	default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable ModelAndView modelAndView) throws Exception {
	}

	default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable Exception ex) throws Exception {
	}
}
  • preHandler:目标方法执行完成之前
  • postHanlder:目标方法执行完成之后
  • afterComplete:页面渲染以后

一般而言,我们的登录拦截操做,等相关的拦截操做都可以定义在preHandler方法里面。

1.2 MethodInterceptor

MethodInterceptor是AOP项目中的拦截器,它拦截的目标是方法。

实现MethodInterceptor拦截器大致也分为两种,一种是实现MethodInterceptor接口,另一种利用AspectJ的注解或配置。

让我们看看MethodInterceptor的源码:

public interface MethodInterceptor extends Interceptor {
    Object invoke(MethodInvocation var1) throws Throwable;
}

这里我着重使用以下HandlerInterceptor

二、如何使用HandlerInterceptor

2.1 使用步骤

  1. 编写一个拦截器类,实现HandlerInterceptor接口,并实现preHandler方法(核心)
  2. 编写一个配置类,实现WebMvcConfigurer接口
  3. 实现接口里的addInterceptors方法
  4. 在方法里,将定义的拦截器类,注入到Spring容器里,并配置拦截规则

2.2 案例实战

老规矩,还是使用之前Thymeleaf阶段的一个登录进行演示

【前置工作】
controller层跳转逻辑

	// 去登陆页
    @GetMapping(value = {"/", "/login"})
    public String loginPage() {
        return "login";
    }

    // 登录请求
    @PostMapping(value = "/login")
    public String main(User user, HttpSession session, Model model) {
        // 这块可以写一些数据库账号密码的逻辑操作
        // ...
        // 如果密码账号无误,ok登录 把登录信息放到Session作用域中
        // 这里只做非空判断
        if (!StringUtils.isEmpty(user.getUsername()) && user.getPassword().equalsIgnoreCase("123456")) {
            // 保存登陆成功的用户
            session.setAttribute("loginUser", user);
            return "redirect:/main.html";
        } else {
            model.addAttribute("msg", "账号密码错误");
            return "login";
        }
    }

    // 解决表单重复提交 重定向
    @GetMapping("/main.html")
    public String mainPage(HttpSession session, Model model) {
        // 拦截器里面进行登陆的拦截判断操做
        return "main";
    }

【步骤一】
编写一个LoginInterceptor类

@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // logback日志输出获取拦截的URI
        log.info("拦截的请求路径是{}"+request.getRequestURI());

        // 登录拦截 需要在执行之前
        HttpSession session = request.getSession();
        Object loginUser = session.getAttribute("loginUser");

        if (loginUser != null) {
            return true; // 放行
        }
        // 阻止,并重定向到登录页
        request.setAttribute("msg", "请先登录");
        request.getRequestDispatcher("/").forward(request,response);
        return false;
    }
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}

【步骤二】
将拦截器注入并配置拦截规则

@Configuration
public class MyConfig implements WebMvcConfigurer {

    /**
     * 配置拦截规则与注入拦截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // addPathPattern 添加拦截规则 /** 拦截所有包括静态资源
        // excludePathPattern 排除拦截规则 所以我们需要放开静态资源的拦截
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/")
                .excludePathPatterns("/login")
                .excludePathPatterns("/css/**","/fonts/**","/images/**","/js/**");
    }
}

🔥🔥说明:如果我们在addInterceptors的方法里面使用addPathPattern添加拦截路径的时候,使用/**的方式进行拦截请求,那么这也就将静态资源也拦截掉了,所以我们可以通过excludePathPattern来排除想放行的资源

【拦截效果】

在这里插入图片描述
【日志输出拦截URI】
在这里插入图片描述

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐