【SpringBoot学习笔记 十】深入理解SpringBoot拦截器

简介: 【SpringBoot学习笔记 十】深入理解SpringBoot拦截器

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

SpringBoot使用拦截器

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

接下来我们实践下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

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

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

对比SpringMVC实现方式

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

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

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

总结一下

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

相关文章
|
Ubuntu Shell 开发工具
Ubuntu 20.04 配置 zsh
Ubuntu 20.04 配置 zsh
968 0
Ubuntu 20.04 配置 zsh
|
编解码 分布式计算 网络协议
Netty高性能网络框架(一)
Netty高性能网络框架(一)
|
Java 数据库 数据安全/隐私保护
【SpringBoot】Validator组件+自定义约束注解实现手机号码校验和密码格式限制
【SpringBoot】Validator组件+自定义约束注解实现手机号码校验和密码格式限制
1254 1
|
前端开发 Java 数据安全/隐私保护
Spring Boot3自定义异常及全局异常捕获
Spring Boot3自定义异常及全局异常捕获
1671 1
|
前端开发 JavaScript Java
Spring Boot入门(十七) 之 登录拦截器
Spring Boot入门(十七) 之 登录拦截器
332 0
|
安全 数据安全/隐私保护 虚拟化
win11家庭版怎么升级专业版
Windows 11家庭版用户常需升级到专业版以解锁远程桌面、组策略和BitLocker等高级功能。在升级前,备份数据、确保系统更新至最新。购买正版密钥后,通过“设置”-&gt;“系统”-&gt;“激活”输入密钥进行升级。遵循提示完成升级过程,系统会自动应用专业版特性。如有问题,参考官方文档或寻求技术支持。
win11家庭版怎么升级专业版
|
存储 安全 API
在ModelScope中,访问令牌通常用于身份验证和授权
随着人工智能技术的发展,ModelScope(魔搭)作为开放的模型即服务(MaaS)平台,提供了丰富的预训练模型资源。本文介绍了如何在ModelScope中使用访问令牌进行身份验证和授权,包括获取和使用访问令牌的详细步骤及示例代码,确保用户安全地访问模型资源。
259 3
|
XML Java 数据库连接
SpringBoot集成Flowable:打造强大的工作流管理系统
在企业级应用开发中,工作流管理是一个核心组件,它能够帮助我们定义、执行和管理业务流程。Flowable是一个开源的工作流和业务流程管理(BPM)平台,它提供了强大的工作流引擎和建模工具。结合SpringBoot,我们可以快速构建一个高效、灵活的工作流管理系统。本文将探讨如何将Flowable集成到SpringBoot应用中,并展示其强大的功能。
2475 1
|
Java Spring
使用Gradle创建SpringBoot项目
使用Gradle创建SpringBoot项目
1127 0
|
NoSQL Java Redis
Springboot整合redis(一般人都能看懂的Lettuce版本)
去年学习的Redis,刚刚学习完就迫不及待的在实战中用了一下,走了很多坑不过幸好都填上了,需求的不断变化发现用不上Redis,一开始去掉了,后来想想加进来比较合适。这篇文章主要讲解Springboot如何整合开发Redis实现一个基本的案例。使用的是目前Springboot2.x的Lettuce版本。希望对你有帮助。
1269 1