文章目录
1. 什么是拦截器
拦截器 Interceptor 同 Filter 过滤器一样,它俩都是面向切面编程——AOP 的具体实现(AOP切面编程只是一种编程思想而已)。
拦截器是Spring Boot框架中的一种机制,它可以在处理请求或响应时拦截执行某些操作。拦截器可以用于许多不同的用途,例如:
- 认证和授权
- 记录请求日志
- 防止恶意请求
- 修改请求或响应
2. springboot拦截器的生命周期
Spring Boot拦截器的生命周期如下:
preHandle
方法:在请求处理之前执行,返回true
将继续执行请求,返回false
将中止请求。postHandle
方法:在请求处理之后执行,但在视图被渲染之前执行。可以通过ModelAndView
参数来访问渲染的视图。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框架中的拦截器机制,以及如何实现和注册拦截器。拦截器可以用来实现许多不同的功能,例如认证和授权、记录请求日志、防止恶意请求等等。在本文中,我们还介绍了如何在拦截器中检查用户的登录状态,以及如何指定哪些请求应该被拦截,哪些请求不拦截。