springboot(12)拦截器 Interceptor

1. 什么是拦截器

拦截器 Interceptor 同 Filter 过滤器一样,它俩都是面向切面编程——AOP 的具体实现(AOP切面编程只是一种编程思想而已)。
拦截器是Spring Boot框架中的一种机制,它可以在处理请求或响应时拦截执行某些操作。拦截器可以用于许多不同的用途,例如:

  • 认证和授权
  • 记录请求日志
  • 防止恶意请求
  • 修改请求或响应

2. springboot拦截器的生命周期

Spring Boot拦截器的生命周期如下:

  1. preHandle方法:在请求处理之前执行,返回true将继续执行请求,返回false将中止请求。
  2. postHandle方法:在请求处理之后执行,但在视图被渲染之前执行。可以通过ModelAndView参数来访问渲染的视图。
  3. afterCompletion方法:在请求完成后执行,即在视图被渲染之后执行。在这个方法中可以进行一些资源清理操作。

通过实现HandlerInterceptor接口并覆盖这三个方法,我们可以实现自己的拦截器,并在Spring Boot中使用它们。

3. 如何使用拦截器?

3.1 方式(一)

Spring Boot中使用拦截器非常简单。只需要实现HandlerInterceptor接口,并在Spring Boot配置文件中注册该拦截器即可。

以下是一个示例拦截器的代码:

public class MyInterceptor implements HandlerInterceptor {
    
    

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        // 在请求处理之前执行操作
        return true; // 如果返回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 {
    
    
        // 在请求完成后执行操作
    }
}

在Spring Boot中注册拦截器的方法有很多种,以下是其中一种方法:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    
    

    @Autowired
    private MyInterceptor myInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
    
        registry.addInterceptor(myInterceptor);
    }
}

3.2 方式(二)

3.2.1 问题:

我们的拦截器是单例,因此不管用户请求多少次都只有一个拦截器实现,即线程不安全,那我们应该怎么记录时间呢?

3.2.2 解决方案:

如记录一下请求的处理时间,得到一些慢请求(如处理时间超过500毫秒),从而进行性能改进,一般的反向代理服务器如 apache 都具有这个功能,但此处我们演示一下使用拦截器怎么实现。

解决方案是使用 ThreadLocal,它是线程绑定的变量,提供线程局部变量(一个线程一个 ThreadLocal,A线程的ThreadLocal 只能看到A线程的 ThreadLocal,不能看到B线程的 ThreadLocal)。

拦截器代码示例:

package com.buba.intercepter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.NamedThreadLocal;
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;

public class MyIntercepter implements HandlerInterceptor {
    
    
    //NamedThreadLocal:Spring提供的一个命名的ThreadLocal实现。
    private NamedThreadLocal<Long> startTimeThreadLocal = new NamedThreadLocal<>("StopWatch-StartTime");
    private Logger logger = LoggerFactory.getLogger(MyIntercepter.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        long beginTime = System.currentTimeMillis();//1、开始时间
        startTimeThreadLocal.set(beginTime);//线程绑定变量(该数据只有当前请求的线程可见)
        return true;//继续流程
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
    
        long endTime = System.currentTimeMillis();//2、结束时间
        long beginTime = startTimeThreadLocal.get();//得到线程绑定的局部变量(开始时间)
        long consumeTime = endTime - beginTime;//3、消耗的时间
        if(consumeTime > 500) {
    
    //此处认为处理时间超过500毫秒的请求为慢请求
            //TODO 记录到日志文件
            logger.info(String.format("%s consume %d millis", request.getRequestURI(), consumeTime));
        } else {
    
    
            // 测试的时候由于请求时间未超过500,所以启用该代码
            logger.info(String.format("%s consume %d millis", request.getRequestURI(), consumeTime));
        }
    }
}

拦截器配置类代码实现:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
    
//        registry.addInterceptor();
        registry.addInterceptor(new MyIntercepter()).addPathPatterns("/test/*");
    }
}

4. 判断用户的登录状态

4.1登录状态 (一)

要判断用户的登录状态,可以在preHandle方法中进行。假设你的应用程序使用基于Session的身份验证,那么你可以检查用户是否已经登录,并根据需要执行相应的操作。例如,如果用户未登录,则可以将请求重定向到登录页面。

以下是一个示例:

public class MyInterceptor implements HandlerInterceptor {
    
    

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        HttpSession session = request.getSession();
        String user = (String) session.getAttribute("user");
        if (user == null) {
    
    
            // 用户未登录,重定向到登录页面
            response.sendRedirect("/login");
            return false;
        }
        return true;
    }

    // postHandle 和 afterCompletion 方法的代码省略
}

在这个示例中,我们检查 HttpSession 中是否存在名为 “user” 的属性。如果该属性不存在,则说明用户未登录,我们将请求重定向到登录页面。如果用户已经登录,则返回 true,继续执行请求处理。

当然,这只是一个示例。实际上,你可能需要更复杂的逻辑来处理用户登录状态。但是,这个示例说明了如何在拦截器中检查用户登录状态。

4.2 登录状态 (二)

要在Spring Boot中使用拦截器来检查用户的登录状态,可以在preHandle方法中进行。假设你的应用程序使用基于token的身份验证,那么你可以检查请求头中是否存在有效的token,并根据需要执行相应的操作。例如,如果token无效或过期,则可以将请求重定向到登录页面。

以下是一个示例拦截器的代码:

public class TokenInterceptor implements HandlerInterceptor {
    
    

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        String token = request.getHeader("Authorization");
        if (token == null) {
    
    
            // 用户未登录,重定向到登录页面
            response.sendRedirect("/login");
            return false;
        }
        // 根据token检查用户是否已登录
        boolean isValid = checkToken(token);
        if (!isValid) {
    
    
            // token无效或过期,重定向到登录页面
            response.sendRedirect("/login");
            return false;
        }
        return true;
    }

    // postHandle 和 afterCompletion 方法的代码省略
}

在这个示例中,我们从请求头中获取token,并检查其是否有效。如果token不存在或无效,则将请求重定向到登录页面。如果token有效,则返回 true,继续执行请求处理。

当然,这只是一个示例。实际上,你可能需要更复杂的逻辑来处理token的验证和过期。但是,这个示例说明了如何在拦截器中检查用户登录状态。

5. 指定的接口不被拦截

要在Spring Boot中使用拦截器,除了注册你的拦截器之外,还需要指定哪些请求应该被拦截。默认情况下,Spring Boot会拦截所有请求,但你可以通过配置来指定哪些请求应该被拦截。以下是一个示例:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    
    

    @Autowired
    private MyInterceptor myInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
    
        registry.addInterceptor(myInterceptor)
                .excludePathPatterns("/register"); // 指定register接口不被拦截
    }
}

在这个示例中,我们使用excludePathPatterns方法指定了/register接口不被拦截。这意味着所有其他的请求都会被拦截,并且会通过我们的拦截器进行处理。

6. 总结

本文介绍了Spring Boot框架中的拦截器机制,以及如何实现和注册拦截器。拦截器可以用来实现许多不同的功能,例如认证和授权、记录请求日志、防止恶意请求等等。在本文中,我们还介绍了如何在拦截器中检查用户的登录状态,以及如何指定哪些请求应该被拦截,哪些请求不拦截。

猜你喜欢

转载自blog.csdn.net/Bilal_0/article/details/129968370