拦截器的概念无需赘述,在SpringMVC的一篇Blog中我就详细聊过【Spring MVC学习笔记 七】深入理解SpringMVC拦截器原理,所以关于拦截器和过滤器的区别、拦截器的作用等就不再赘述了,这里主要探讨下SpringBoot是如何使用拦截器的。按照如下步骤我们来处理登录拦截这样一个场景,即未登录之前请求都被转发到login.html界面

SpringBoot使用拦截器

在 Spring Boot 中定义拦截器十分的简单,只需要创建一个拦截器类,并实现 HandlerInterceptor 接口即可

springboot拦截器详解 springboot 拦截器原理_SpringBoot


接下来我们实践下SpringBoot的拦截器使用过程

1 预置正常的SpringMVC请求

首先我们预置一个正常的MVC请求模型,然后再去看拦截器如何发挥作用

1 定义处理登录请求的Controller

LoginController

package com.example.springboot.controller;

import com.example.springboot.model.Person;
import com.example.springboot.model.User;
import lombok.extern.log4j.Log4j;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpSession;
import java.util.Map;

/**
 * * @Name LoginController
 * * @Description
 * * @author tianmaolin
 * * @Data 2021/10/15
 */
@Controller
@Slf4j
public class LoginController {
    @PostMapping("/user/login")
    public String doLogin(User user, Map<String, Object> map, HttpSession session) {
        if (user != null && StringUtils.hasText(user.getUsername()) && "123456".equals(user.getPassword())) {
            session.setAttribute("loginUser", user);
            log.info("登陆成功,用户名:" + user.getUsername());
            //防止重复提交使用重定向
            return "redirect:/main.html";
        } else {
            map.put("msg", "用户名或密码错误");
            log.error("登陆失败");
            return "login";
        }
    }

}

2 添加要跳转的html页面

在template下添加如下两个html文件:

login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>

<body>

<div class="login-container"  >
    <p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
    <form action="/user/login" method="post">

        <div >
            <input type="text" name="username" th:placeholder="#{username}" />
        </div>
        </br>
        <div>
            <input type="password" name="password" th:placeholder="#{password}" />
        </div>
        </br>
        <button id="submit" type="submit" th:text="#{loginBtn}"></button>
        <button type="btn" th:text="#{registerBtn}"></button><br>
        <!--thymeleaf 模板引擎的参数用()代替 ?-->
        <a class="btn btn-sm" th:href="@{/login.html(l='zh_CN')}">中文</a>|
        <a class="btn btn-sm" th:href="@{/login.html(l='en_US')}">English</a>
    </form>


</div>

</body>
</html>

main.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>

<body>

恭喜你终于登录成功了,能看到我这个页面,说明你通过了拦截器的考验,成功把用户信息放到了session里,你的用户信息为:

<div th:object="${session.loginUser}" >
    <p th:text="*{username}">username</p>
    <p th:text="*{password}">password</p>
</div>

</body>
</html>

2 定义拦截器

我们在component文件夹下新增拦截器

LoginInterceptor.java

package com.example.springboot.component;

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;

@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    /**
     * 目标方法执行前
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Object loginUser = request.getSession().getAttribute("loginUser");
        if (loginUser == null) {
            //未登录,返回登陆页
            request.setAttribute("msg", "您没有权限进行此操作,请先登陆!");
            request.getRequestDispatcher("/login.html").forward(request, response);
            return false;
        } else {
            //放行
            log.info("preHandle执行成功");
            return true;
        }
    }

    /**
     * 目标方法执行后
     *
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("postHandle执行{}", modelAndView);
    }

    /**
     * 页面渲染后
     *
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("afterCompletion执行异常{}", ex);
    }
}

3 注册拦截器

定义好后我们需要把拦截器进行注册,使用 registry.addInterceptor() 方法将拦截器注册到容器中后,我们便可以继续指定拦截器的拦截规则了

MyMvcConfig.java

package com.example.springboot.config;
import com.example.springboot.component.LoginInterceptor;
import com.example.springboot.component.MyLocalResolver;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.*;

@Configuration
@Slf4j
public class MyMvcConfig implements WebMvcConfigurer {


    //添加视图控制器
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //当访问/时会跳转到登录页
        registry.addViewController("/").setViewName("login");
        registry.addViewController("/login.html").setViewName("login");
        //添加视图映射 main.html 指向  main.html
        registry.addViewController("/main.html").setViewName("main");
    }

    //将自定义的区域信息解析器以组件的形式添加到容器中
    @Bean
    public LocaleResolver localeResolver(){
        return new MyLocalResolver();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        log.info("注册拦截器");
        registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**") //拦截所有请求,包括静态资源文件
                .excludePathPatterns("/", "/login","/login.html", "/index.html", "/user/login", "/css/**", "/images/**", "/js/**", "/fonts/**"); //放行登录页,登陆操作,静态资源
    }
}

在指定拦截器拦截规则时,调用了两个方法,这两个方法的说明如下:

  • addPathPatterns:该方法用于指定拦截路径,例如拦截路径为“/**”,表示拦截所有请求,包括对静态资源的请求。
  • excludePathPatterns:该方法用于排除拦截路径,即指定不需要被拦截器拦截的请求。

至此,拦截器的基本功能已经完成

4 查看实现效果

我们测试下,访问http://localhost:8080/main.html

springboot拦截器详解 springboot 拦截器原理_spring boot_02


然后我们输入错误的密码,显示:

springboot拦截器详解 springboot 拦截器原理_spring_03


最后我们输入正确用户名和密码:

springboot拦截器详解 springboot 拦截器原理_springboot拦截器详解_04

对比SpringMVC实现方式

其实SpringBoot的拦截器实现和SpringMVC的大同小异,但却把配置干掉了,转而体现在代码里,我们之前的拦截配置是在:

springmvc-servlet.xml文件,通过该文件注入拦截器以及定义拦截规则

springboot拦截器详解 springboot 拦截器原理_SpringBoot_05


现在我们都通过代码进行配置注入了,本质上没什么区别。有个需要注意的地方:

springboot拦截器详解 springboot 拦截器原理_SPRINGBOOT拦截器_06

总结一下

本篇Blog详细介绍了SpringBoot的拦截器实现方式,其实可以发现,和SpringMVC实现方式上本质是一致的,只是拦截器的注入方式不同,通过代码比通过配置确乎好理解很多,之后我们处理相关请求的拦截时也更加方便了,如果想看各种不同拦截器的实现方式,参照我之前的这两篇Blog:【Spring MVC学习笔记 七】深入理解SpringMVC拦截器原理,【Java Web编程 十】深入理解Servlet过滤器