• SpringMVC Day 10 : 拦截器


    前言

    拦截器是Spring MVC框架提供的一种强大的机制,用于在请求到达控制器之前或之后进行预处理和后处理。它可以拦截并处理请求,对请求进行必要的修改或验证,以及在请求返回给客户端之前进行额外的操作。拦截器可以帮助我们实现各种需求,如身份验证、日志记录、性能监控等。

    在本篇博文中,我们将深入研究拦截器的概念、工作原理和使用方法。我们将学习如何创建和配置拦截器,以及如何将其应用于Spring MVC应用程序中。最后,我们还将探讨一些实际应用场景,并给出一些拦截器的最佳实践。

    一、前期准备

    1、新建项目,结构如下

    2、导入依赖
    1. <dependencies>
    2. <dependency>
    3. <groupId>org.springframeworkgroupId>
    4. <artifactId>spring-webmvcartifactId>
    5. <version>5.3.23version>
    6. dependency>
    7. <dependency>
    8. <groupId>org.projectlombokgroupId>
    9. <artifactId>lombokartifactId>
    10. <version>1.18.30version>
    11. dependency>
    12. <dependency>
    13. <groupId>ch.qos.logbackgroupId>
    14. <artifactId>logback-classicartifactId>
    15. <version>1.4.5version>
    16. dependency>
    17. dependencies>
    3、配置 web.xml 
    1. "1.0" encoding="UTF-8"?>
    2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    5. version="4.0">
    6. <servlet>
    7. <servlet-name>dispatcherservlet-name>
    8. <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
    9. <load-on-startup>1load-on-startup>
    10. servlet>
    11. <servlet-mapping>
    12. <servlet-name>dispatcherservlet-name>
    13. <url-pattern>/url-pattern>
    14. servlet-mapping>
    15. web-app>

     用于配置 Servlet 的映射和加载。在 Spring MVC 中,它用于配置 DispatcherServlet 的初始化和请求映射。

    具体来说,这段配置的作用如下:

    1. 定义了一个名为 "dispatcher" 的 Servlet,并指定了 org.springframework.web.servlet.DispatcherServlet 作为其处理类。
    2. 设置了 load-on-startup 属性为 1,表示在应用启动时就加载该 Servlet。
    3. 使用 元素将 "dispatcher" Servlet 映射到所有的请求路径上(即 /),意味着所有的请求都会经过该 Servlet 进行处理。

     这段配置的作用是将所有的请求交给 DispatcherServlet 处理,并让它成为应用的核心控制器。DispatcherServlet 将根据请求的 URL 和其他配置信息,将请求分发给相应的处理器方法进行处理,然后返回响应结果。

    4、新建 User 类
    1. @Data
    2. public class User {
    3. private String userName;
    4. private String password;
    5. }
    5、新建一个 LoginService 接口
    1. public interface LoginService {
    2. /**
    3. * 登录认证
    4. * @param userName
    5. * @param password
    6. * @return
    7. */
    8. User auth(String userName,String password);
    9. }
    6、新建一个 LoginServiceImpl 实现类
    1. @Service
    2. public class LoginServiceImpl implements LoginService {
    3. @Override
    4. public User auth(String userName, String password) {
    5. if ("qiu".equals(userName) && "123".equals(password)){
    6. User user = new User();
    7. user.setUserName(userName);
    8. user.setPassword(password);
    9. return user;
    10. }
    11. throw new RuntimeException("账号密码错误");
    12. }
    13. }

    在业务类这里做一个简单的业务处理,判断输出的账号密码是否正确,然后抛出一个账号或密码错误的异常。

    7、新建一个 index.html 页面
    1. html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>首页title>
    6. head>
    7. <body>
    8. <h1>home pageh1>
    9. body>
    10. html>

     用于登录跳转的页面。

    二、实现拦截器

    1、编写一个 controller 
    1. @RestController
    2. @RequiredArgsConstructor
    3. public class UserController {
    4. private final LoginService service;
    5. @PostMapping("/auth")
    6. public ResultVO login(String userName, String password, HttpSession session){
    7. User auth = service.auth(userName, password);
    8. session.setAttribute("user",auth);
    9. return new ResultVO();
    10. }
    11. }

     该类中定义了一个名为UserController的控制器类,它只包含一个成员变量service和一个方法login。成员变量service是一个LoginService类型的对象,它通过构造函数注入到UserController中。方法login用于处理POST请求,并将用户名、密码和会话信息作为参数传入。在方法中,它调用service对象的auth方法进行登录验证,并将验证结果保存到会话中。最后,它返回一个ResultVO对象,这个对象可以被转换成JSON格式的数据并返回给客户端。

    2、编写登录页面 login.html
    1. html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>用户登录title>
    6. <script src="js/JQuery文件.txt.js">script>
    7. head>
    8. <body>
    9. <h1>用户登录h1>
    10. <form id="f1">
    11. 账号:<input type="text" name="userName"/><br>
    12. 密码:<input type="password" name="password"/><br>
    13. <input type="button" value="登录"/>
    14. form>
    15. <script>
    16. $(function(){
    17. $(':button').on('click', function(){
    18. let formData = $('#f1').serialize();
    19. $.ajax({
    20. url: '../auth',
    21. type: 'post',
    22. data: formData,
    23. success: function (result) {
    24. if(result.code === 200) {
    25. location.href = 'index.html';
    26. }
    27. },
    28. error:function (error) {
    29. alert(error)
    30. }
    31. });
    32. });
    33. })
    34. script>
    35. body>
    36. html>

    该页面包含一个表单,用户需要输入账号和密码进行登录。表单中的数据通过serialize()方法序列化为字符串,并在点击登录按钮时发送到服务器。

    在脚本部分,使用jQuery的(function())方法来确保页面加载完成后再执行代码。当用户点击登录按钮时,使用(function())方法来确保页面加载完成后再执行代码。当用户点击登录按钮时,使用.ajax()方法发送POST请求到服务器的'../auth'路径,并将表单数据作为请求参数。成功回调函数中判断返回结果的状态码是否为200,如果是,则跳转到'index.html'页面;错误回调函数中显示错误信息。

    总体来说,这段代码实现了一个简单的用户登录功能,通过使用jQuery库简化了AJAX请求的编写过程,并在成功或失败时给予用户相应的提示。

    3、编写拦截器
    1. /**
    2. * @Date 2023-10-27
    3. * @Author qiu
    4. * 认证拦截器
    5. * 拦截所有的请求,如果未登录则返回 401(未登录,未认证) 状态码
    6. */
    7. @Slf4j
    8. public class AuthInterceptor implements HandlerInterceptor {
    9. /**
    10. * 在调用 controller 的请求方法之前执行
    11. * 如果此方法返回 false ,则请求不会继续往下执行
    12. * @param request
    13. * @param response
    14. * @param handler
    15. * @return
    16. * @throws Exception
    17. */
    18. @Override
    19. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    20. log.info("执行 preHandle 方法");
    21. HttpSession session = request.getSession();
    22. // 如果 session 为 null 表示用户未登录
    23. if ( session.getAttribute("user") == null ){
    24. ResultVO resultVO = new ResultVO();
    25. // 设置 401 状态码
    26. resultVO.setCode(HttpStatus.UNAUTHORIZED.value());
    27. resultVO.setMessage("未登录,请登录!");
    28. response.setContentType("application/json;charset=utf-8");
    29. String json = new ObjectMapper().writeValueAsString(resultVO);
    30. response.getWriter().println(json);
    31. return false;
    32. }
    33. return true;
    34. }
    35. /**
    36. * 在调用 controller 方法之后,返回之前执行
    37. * @param request
    38. * @param response
    39. * @param handler
    40. * @param modelAndView
    41. * @throws Exception
    42. */
    43. @Override
    44. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    45. log.info("执行 postHandle 方法");
    46. }
    47. /**
    48. * 调用 controller 方法并返回之后执行
    49. * (注意:只有在 preHandle 返回 TRUE ,才会执行)
    50. * @param request
    51. * @param response
    52. * @param handler
    53. * @param ex
    54. * @throws Exception
    55. */
    56. @Override
    57. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    58. log.info("执行 afterCompletion 方法");
    59. }
    60. }

    这段代码是一个认证拦截器的实现示例。拦截器是Spring MVC框架提供的一种机制,用于在请求到达控制器之前或之后进行预处理和后处理操作。

    这个认证拦截器的作用是拦截所有的请求,在调用控制器方法之前进行认证操作。具体功能如下:

    1. preHandle方法:

      • 在调用控制器方法之前执行。
      • 首先获取请求的session对象。
      • 如果session中未存储用户信息(即用户未登录),则返回401状态码,并返回未登录提示信息。
      • 通过设置response的内容类型为application/json;charset=utf-8,将结果以JSON格式返回给客户端。
      • 如果preHandle方法返回false,则请求不会继续往下执行,即不会调用控制器方法;如果返回true,则请求会继续执行。
    2. postHandle方法:

      • 在调用控制器方法之后,返回之前执行。
      • 这个方法可以对请求的结果进行一些处理,比如修改ModelAndView对象的数据等。
    3. afterCompletion方法:

      • 在调用控制器方法并返回之后执行。
      • 注意,该方法只有在preHandle方法返回true的情况下才会执行。
      • 可以进行一些清理工作,比如释放资源等。

    这个认证拦截器的主要作用是在请求到达控制器之前检查用户是否已登录,如果未登录,则返回401状态码。使用拦截器可以很方便地实现对请求的统一认证、授权等处理,提高应用程序的安全性和可维护性。

    4、配置 dispatcher-servlet.xml
    1. "1.0" encoding="UTF-8"?>
    2. <beans xmlns="http://www.springframework.org/schema/beans"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xmlns:context="http://www.springframework.org/schema/context"
    5. xmlns:mvc="http://www.springframework.org/schema/mvc"
    6. xsi:schemaLocation="http://www.springframework.org/schema/beans
    7. http://www.springframework.org/schema/beans/spring-beans.xsd
    8. http://www.springframework.org/schema/context
    9. https://www.springframework.org/schema/context/spring-context.xsd
    10. http://www.springframework.org/schema/mvc
    11. https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    12. <context:component-scan base-package="edu.nf.ch10"/>
    13. <mvc:annotation-driven/>
    14. <mvc:default-servlet-handler/>
    15. <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    16. <property name="prefix" value="/WEB-INF/jsp/"/>
    17. <property name="suffix" value=".jsp"/>
    18. bean>
    19. <mvc:interceptors>
    20. <mvc:interceptor>
    21. <mvc:mapping path="/**"/>
    22. <mvc:exclude-mapping path="/static/login.html"/>
    23. <mvc:exclude-mapping path="/auth"/>
    24. <mvc:exclude-mapping path="/static/js/**"/>
    25. <bean class="edu.nf.ch10.interceptor.AuthInterceptor"/>
    26. mvc:interceptor>
    27. mvc:interceptors>
    28. beans>

    这段代码是一个Spring MVC配置文件,用于配置Spring MVC框架的相关组件和拦截器。

    具体功能如下:

    1. component-scan:

      • 配置组件扫描的基础包路径,使得Spring能够自动扫描并装配标有注解的组件(比如控制器、服务等)。
    2. mvc:annotation-driven:

      • 启用Spring MVC的注解驱动,使得控制器类中的注解生效,如@RequestMapping、@ResponseBody等。
    3. mvc:default-servlet-handler:

      • 配置静态资源的处理,默认的Servlet将会处理静态资源请求,如.css、.js、.jpg等文件。
    4. InternalResourceViewResolver:

      • 配置内部资源视图解析器,用于将逻辑视图名解析为实际的物理视图地址。
      • 设置了视图前缀和后缀,例如将逻辑视图名"index"解析为"/WEB-INF/jsp/index.jsp"。
    5. mvc:interceptors:

      • 配置拦截器。
      • 在这个示例中,定义了一个具体的拦截器AuthInterceptor,并设置了哪些请求会经过该拦截器。
      • 使用mvc:mapping配置需要拦截的请求路径,使用mvc:exclude-mapping配置不需要拦截的请求路径。
      • 这里的配置表示除了/static/login.html、/auth和/static/js/**这些路径外,其他所有请求都会经过AuthInterceptor拦截器。

    通过这个配置文件,你可以实现以下功能:

    1. 启用Spring MVC框架的注解驱动,使得控制器类中的注解生效。
    2. 配置静态资源的处理,让默认的Servlet处理静态资源请求。
    3. 配置内部资源视图解析器,将逻辑视图名解析为实际的物理视图地址。
    4. 配置拦截器,对请求进行拦截并进行相应的处理,比如认证、授权、日志记录等。

    总之,这段配置文件用于配置Spring MVC框架的相关组件和拦截器,以便实现Web应用程序的功能和需求。

    5、运行效果
    1)非登录效果

     在未登录的时候,我们是不能进到 index.html 页面的,因为我们的拦截器对它进行了拦截,只能登录了才能进去。 

    2)登录效果
     

     我们登录之后就可以跳转到首页了吧。

     

    三、使用拦截器的好处

    使用拦截器的好处包括:

    1. 统一处理:可以在拦截器中统一处理一些公共业务逻辑,比如权限校验、日志记录等,避免重复代码的出现。

    2. 粒度控制:可以根据具体的业务场景,选择拦截器拦截请求,实现粒度控制,从而更好地满足业务需求。

    3. 解耦合:通过拦截器可以将多个模块的功能解耦合,降低各组件之间的耦合度,提高代码可维护性和可扩展性。

    4. 提高安全性:通过拦截器可以对请求进行安全控制,比如防止SQL注入、XSS攻击等,提高Web应用系统的安全性。

    5. 性能优化:通过拦截器可以对请求进行缓存、预处理等操作,以提高Web应用系统的性能。

    总之,使用拦截器可以帮助我们更好地管理请求,增强代码的可维护性和可读性,提高Web应用系统的安全性和性能。

    四、gitee 案例

    地址:ch10 · qiuqiu/SpringMVC - 码云 - 开源中国 (gitee.com) 

  • 相关阅读:
    40.组合总和 II
    51单片机STC89C52RC——6.1 中断系统
    CSS 面试题及答案
    【21天学习经典算法】列表的分类与顺序查找(附Python完整代码)
    Redis系列9:Geo 类型赋能亿级地图位置计算
    设计模式-责任链模式(Chain of Responsibility)
    RabbitMQ的RPM包安装和Python读写操作
    js Fetch返回数据res.json()报错问题
    常用命令记录
    RepVgg实战:使用RepVgg实现图像分类(一)
  • 原文地址:https://blog.csdn.net/zhiqiuqiu2/article/details/134153298