Spring Boot 拦截器:解锁5大实用场景

一、Spring Boot中拦截器是什么

  在Spring Boot中,拦截器(Interceptor)是一种基于AOP(面向切面编程)思想的组件,用于在请求处理前后插入自定义逻辑,实现权限校验、日志记录、性能监控等非业务功能。

  其核心作用是在不修改业务代码的前提下,对请求进行统一处理,类似于Servlet中的Filter,但更贴近Spring MVC的体系。

二、拦截器与过滤器的区别

在这里插入图片描述

三、拦截器主要方法

在实现拦截器时,HandlerInterceptor接口提供了三个主要方法,它们在请求处理的不同阶段发挥着重要作用。

preHandle
  这个方法在请求进入Controller之前被调用 。它的返回值是一个布尔类型,如果返回true,请求将继续被处理,进入Controller。
   如果返回false,请求将被拦截,后续的Controller方法将不会被执行。在这个方法中,我们通常可以进行一些前置的处理操作,比如用户认证、权限校验、日志记录等。​

postHandle
  当Controller方法执行完毕后,视图渲染之前,这个方法会被调用 。

  在这个方法中,我们可以对ModelAndView进行一些操作,比如添加额外的模型数据,修改视图名称等。需要注意的是,如果在preHandle方法中返回了false,这个方法将不会被执行。​

afterCompletion
  在整个请求处理完成,包括视图渲染之后,这个方法会被调用。无论请求处理过程中是否发生异常,只要preHandle方法返回true,这个方法就会被执行。

  我们可以在这个方法中进行一些资源清理、记录日志等收尾工作。如果请求处理过程中发生了异常,异常信息会通过 ex 参数传递进来,我们可以根据这个参数进行相应的异常处理。

@Component
public class MyInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 在请求处理之前执行的逻辑System.out.println("preHandle被调用,请求即将进入Controller");return true; // 返回true表示放行请求,返回false则拦截请求}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {// 在请求处理之后,视图渲染之前执行的逻辑System.out.println("postHandle被调用,Controller方法已执行完毕,视图即将渲染");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 在整个请求处理完成,包括视图渲染之后执行的逻辑System.out.println("afterCompletion被调用,整个请求已处理完毕");}
}
@Configuration
public class WebMvcConfig1 implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**") // 拦截所有请求.excludePathPatterns("/static/**"); // 排除/static目录下的静态资源请求}
}

四、5 种常见的拦截器使用场景

1.用户认证拦截器

  用户认证拦截器的作用就是在用户请求进入 Controller 之前,验证用户的登录状态。如果用户已经登录,允许请求继续处理。如果用户未登录,则返回错误信息或者重定向到登录页面。

@Component
public class JwtHandlerInterceptor implements HandlerInterceptor {@Autowiredprivate JwtTokenProvider jwtTokenProvider;@Autowiredprivate UserInfoService userInfoService;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (!(handler instanceof HandlerMethod)) {return true;}// 取出tokenString token = request.getHeader("token");if (!jwtTokenProvider.validateToken(token)) {response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);response.getWriter().write("{\"error\": \"Token已失效,请重新登录\"}");return false;}HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();// 判断方法上是否有NoAuth注解,如果有则跳过认证if (method.isAnnotationPresent(NoAuth.class)) {return true;}String username = jwtTokenProvider.getUsernameFromJWT(token);User user = userInfoService.getUserInfoByUserName(username);if (user == null) {response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);response.getWriter().write("{\"error\": \"用户不存在\"}");return false;}// 判断角色权限HasRoles hasRoles = handlerMethod.getMethodAnnotation(HasRoles.class);if (!Objects.isNull(hasRoles)) {// 检查用户是否有所需角色String[] roles = hasRoles.value();boolean hasRole = false;// 角色校验 ...if (!hasRole) {response.setStatus(HttpServletResponse.SC_FORBIDDEN);response.getWriter().write("{\"error\": \"权限不足\"}");return false;}}// 将用户信息放入请求属性request.setAttribute("currentUser", user);return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}
}

  在WebMvcConfig配置类中,将JwtHandlerInterceptor注册到Spring的拦截器链中,并设置拦截路径为所有请求,排除了登录和注册接口,因为这两个接口不需要用户登录就可以访问。

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new JwtHandlerInterceptor()).addPathPatterns("/**")// 拦截所有请求.excludePathPatterns("/login","/register");// 排除登录和注册接口}}

  通过这样的配置,就可以实现对用户登录状态的验证,确保只有登录用户才能访问受保护的资源。

2.日志记录拦截器

  日志记录对于系统的运维和故障排查非常重要。日志记录拦截器可以在请求处理的前后记录请求的相关信息,比如请求的 URL、请求方法、请求参数、客户端 IP 等。这些日志信息可以帮助我们了解系统的运行情况,分析用户行为,在出现问题时快速定位问题。

@Component
public class RequestLoggingInterceptor implements HandlerInterceptor {private static final Logger logger = LoggerFactory.getLogger(RequestLoggingInterceptor.class);@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {logger.info("Request URL: {}, Method: {}, IP: {}", request.getRequestURI(), request.getMethod(), request.getRemoteAddr());// 记录请求参数Map<String, String[]> paramMap = request.getParameterMap();StringBuilder params = new StringBuilder();if (!paramMap.isEmpty()) {for (Map.Entry<String, String[]> entry : paramMap.entrySet()) {params.append(entry.getKey()).append("=").append(String.join(",", entry.getValue())).append("&");}if (params.length() > 0) {params.deleteCharAt(params.length() - 1);}}// 记录请求体(仅POST/PUT/PATCH请求)String method = request.getMethod();String requestBody = "";if (HttpMethod.POST.matches(method) ||HttpMethod.PUT.matches(method) ||HttpMethod.PATCH.matches(method)) {// 使用包装请求对象来多次读取请求体ContentCachingRequestWrapper wrappedRequest =new ContentCachingRequestWrapper(request);// 为了触发内容缓存,我们需要获取一次输入流if (wrappedRequest.getContentLength() > 0) {wrappedRequest.getInputStream().read();requestBody = new String(wrappedRequest.getContentAsByteArray(),wrappedRequest.getCharacterEncoding());}}logger.info("Request URL: {}, Method: {}, IP: {},params: {},requestBody: {}", request.getRequestURI(), request.getMethod(), request.getRemoteAddr(), params, requestBody);return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {logger.info("Request to {} has been completed", request.getRequestURI());if (ex != null) {logger.error("An exception occurred during request handling", ex);}}
}

3.性能监控拦截器

  性能监控对于优化系统性能至关重要。性能监控拦截器可以用来计算和记录请求的处理时间,通过分析这些时间数据,我们可以找出系统中的性能瓶颈,进而进行针对性的优化。

@Component
public class PerformanceInterceptor implements HandlerInterceptor {private static final ThreadLocal<Long> startTimeThreadLocal = new ThreadLocal<>();@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {long startTime = System.currentTimeMillis();startTimeThreadLocal.set(startTime);return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {// 计算请求处理时间long endTime = System.currentTimeMillis();long startTime = startTimeThreadLocal.get();long executeTime = endTime - startTime;System.out.println("Request URL: " + request.getRequestURL() + ", Execution Time: " + executeTime + "ms");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {startTimeThreadLocal.remove();}
}

4.接口限流拦截器
  在高并发场景下,接口限流是保护系统的重要手段 。接口限流拦截器可以限制单位时间内对某个接口的访问次数,防止因大量请求导致系统资源耗尽,从而保证系统的稳定性和可用性 。

@Component
public class AccessLimitInterceptor implements HandlerInterceptor {@Autowiredprivate RedisTemplate<String, Integer> redisTemplate;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (!(handler instanceof HandlerMethod)) {return true;}HandlerMethod handlerMethod = (HandlerMethod) handler;AccessLimit accessLimit = handlerMethod.getMethodAnnotation(AccessLimit.class);if (accessLimit == null) {return true;}int seconds = accessLimit.seconds();int maxCount = accessLimit.maxCount();boolean needLogin = accessLimit.needLogin();if (needLogin) {// 检查用户登录状态,这里省略具体实现// 如果未登录,返回错误信息return false;}String key = request.getRemoteAddr() + request.getRequestURI();Integer count = redisTemplate.opsForValue().get(key);if (count == null) {redisTemplate.opsForValue().set(key, 1, seconds, TimeUnit.SECONDS);} else if (count < maxCount) {redisTemplate.opsForValue().increment(key, 1);} else {render(response, "请求过于频繁,请稍后再试");return false;}return true;}private void render(HttpServletResponse response, String msg) throws IOException {response.setContentType("application/json;charset=UTF-8");PrintWriter out = response.getWriter();out.write(msg);out.flush();out.close();}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}}

5.数据加密解密拦截器
  在数据传输和存储过程中,保护敏感数据的安全至关重要。数据加密解密拦截器可以在请求到达Controller之前对敏感数据进行加密,在响应返回给客户端之前对加密数据进行解密,确保数据在传输和处理过程中的安全性。

  比如在用户登录时,对用户输入的密码进行加密后再传输到服务器。在从数据库中查询用户的身份证号、手机号等敏感信息时,对查询结果进行解密后再返回给前端。

@Component
public class EncryptionInterceptor implements HandlerInterceptor {/*** 密钥*/private static final String KEY = "secret_key";@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 假设请求参数中有一个名为"sensitiveData"的敏感数据需要加密String sensitiveData = request.getParameter("sensitiveData");if (sensitiveData != null) {SecretKey secretKey = new SecretKeySpec(KEY.getBytes(), "AES");byte[] encryptedData = AESUtil.encrypt(sensitiveData, secretKey);String encryptedDataStr = Base64.getEncoder().encodeToString(encryptedData);// 将加密后的数据重新放回请求参数中request.setAttribute("sensitiveData", encryptedDataStr);}return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 假设响应中有一个名为"sensitiveResponseData"的敏感数据需要解密String sensitiveResponseData = (String) request.getAttribute("sensitiveResponseData");if (sensitiveResponseData != null) {SecretKey secretKey = new SecretKeySpec(KEY.getBytes(), "AES");byte[] decodedData = Base64.getDecoder().decode(sensitiveResponseData);String decryptedData = AESUtil.decrypt(decodedData, secretKey);// 将解密后的数据重新放回请求属性中,方便后续处理request.setAttribute("sensitiveResponseData", decryptedData);}}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}
}

五、总结

  在 Spring Boot 开发中,拦截器就像是一把 “万能钥匙”,为我们提供了丰富的功能扩展和业务处理的可能性。

  希望大家在实际项目中,能够根据业务需求,灵活地运用这些拦截器,让我们的 Spring Boot 应用更加健壮、高效、安全。

  同时,拦截器还有很多值得深入探讨的地方,比如如何在拦截器中优雅地处理事务、如何实现更加复杂的动态拦截规则等 ,后续我们可以一起继续探索。

  如果大家在使用拦截器的过程中有任何问题或心得,欢迎在留言区分享交流。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/web/80530.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue百日学习计划Day24-28天详细计划-Gemini版

总目标: 在 Day 24-27 熟练掌握 Vue.js 的各种模板语法&#xff0c;包括文本插值、属性绑定、条件渲染、列表渲染、事件处理和表单绑定&#xff0c;并能结合使用修饰符。 所需资源: Vue 3 官方文档 (模板语法): https://cn.vuejs.org/guide/essentials/template-syntax.htmlVu…

分布式微服务系统架构第125集:AI大模型

加群联系作者vx&#xff1a;xiaoda0423 仓库地址&#xff1a;https://webvueblog.github.io/JavaPlusDoc/ https://1024bat.cn/ 一、user 表&#xff08;用户表&#xff09; sql 复制编辑 create table if not exists user (id bigint auto_increment comment id pri…

机器学习 Day16 聚类算法 ,数据降维

聚类算法 1.简介 1.1 聚类概念 无监督学习&#xff1a;聚类是一种无监督学习算法&#xff0c;不需要预先标记的训练数据 相似性分组&#xff1a;根据样本之间的相似性自动将样本归到不同类别 相似度度量&#xff1a;常用欧式距离作为相似度计算方法 1.2 聚类vs分类 聚类&…

【Linux】第十八章 调优系统性能

1. 系统管理员可以使用哪个命令来更改tuned守护进程的设置&#xff1f; tuned 的调优配置集存储在 /usr/lib/tuned&#xff08;默认&#xff09; 和 /etc/tuned&#xff08;自定义 或当前有效&#xff09;目录下。每个配置集都有一个单独的目录&#xff0c;目录中包含 tuned.c…

【JVS更新日志】企业文档AI助手上线、低代码、智能BI、智能APS、AI助手5.14更新说明!

项目介绍 JVS是企业级数字化服务构建的基础脚手架&#xff0c;主要解决企业信息化项目交付难、实施效率低、开发成本高的问题&#xff0c;采用微服务配置化的方式&#xff0c;提供了低代码数据分析物联网的核心能力产品&#xff0c;并构建了协同办公、企业常用的管理工具等&…

ollama调用千问2.5-vl视频图片UI界面小程序分享

1、问题描述&#xff1a; ollama调用千问2.5-vl视频图片内容&#xff0c;通常用命令行工具不方便&#xff0c;于是做了一个python UI界面与大家分享。需要提前安装ollama&#xff0c;并下载千问qwen2.5vl:7b 模型&#xff0c;在ollama官网即可下载。 &#xff08;8G-6G 显卡可…

Web 架构之会话保持深度解析

文章目录 一、引言二、会话保持的基本概念2.1 什么是会话2.2 为什么需要会话保持 三、会话保持的常见实现方式3.1 基于客户端的会话保持3.1.1 Cookie 方式3.1.2 URL 重写方式 3.2 基于服务器端的会话保持3.2.1 负载均衡器会话保持3.2.2 会话共享 四、会话保持可能遇到的问题及解…

Maven 项目中将本地依赖库打包到最终的 JAR 中

文章目录 前言详细步骤 前言 在现代后端开发中&#xff0c;构建高效且可扩展的 Web 应用程序通常依赖于多种第三方库和内部依赖。这些依赖可以来自公共仓库&#xff0c;也可能是公司内部自研的库或尚未发布到公共仓库的 JAR 包。本文将详细介绍如何在 Maven 项目中处理本地依赖…

快速定位到源码位置的插件 - vite/webpack

1. vite-plugin-vue-devtools npm i vite-plugin-vue-devtools -D vite.config.js中配置 import vueDevTools from vite-plugin-vue-devtoolsexport default defineConfig({server: {port: 5173,host: 0.0.0.0},plugins: [vue(),vueJsx(),vueDevTools({componentInspector: t…

基于AH1101芯片的5V升18.6V LED恒流背光供电方案设计

基于AH1101芯片的5V升18.6V LED恒流背光供电方案设计 在现代电子设备中&#xff0c;LED背光技术因其高效、节能、寿命长等优点被广泛应用于各类显示设备。本文将详细介绍如何利用AH1101高效升压恒流驱动芯片&#xff0c;实现从5V输入电压升压至18.6V&#xff0c;为LED背光板提供…

16.1 - VDMA视频转发实验之TPG

文章目录 1 实验任务2 系统框图3 硬件设计3.1 IP核配置3.2 注意事项 4 软件设计4.1 注意事项4.2 工程源码4.2.1 main.c文件 1 实验任务 基于14.1&#xff0c;使用Xilinx TPG&#xff08;Test Pattern Generator&#xff09; IP提供视频源&#xff0c;将视频数据通过VDMA写入PS…

认识Docker/安装Docker

一、认识Docker Docker的定义 Docker 是一个开源的应用容器引擎&#xff0c;允许开发者将应用及其依赖打包到一个轻量级、可移植的容器中。容器化技术使得应用可以在任何支持 Docker 的环境中运行&#xff0c;确保环境一致性。 Docker的核心组件 Docker Engine&#xff1a;负责…

实用工具:微软软件PowerToys(完全免费),实现多台电脑共享鼠标和键盘(支持window系统)

实用工具&#xff1a;微软软件 PowerToys 让多台电脑共享鼠标和键盘 在如今的数字化办公与生活场景中&#xff0c;我们常常会面临同时使用多台电脑的情况。例如&#xff0c;办公时可能一台电脑用于处理工作文档&#xff0c;另一台用于运行专业软件或查看资料&#xff1b;家庭环…

西门子 Teamcenter13 Eclipse RCP 开发 1.1 工具栏 普通按钮

西门子 Teamcenter13 Eclipse RCP 开发 1.1 工具栏 普通按钮 1 配置文件2 插件控制3 命令框架 位置locationURI备注菜单栏menu:org.eclipse.ui.main.menu添加到传统菜单工具栏toolbar:org.eclipse.ui.main.toolbar添加到工具栏 style 值含义显示效果push普通按钮&#xff08;默…

React中巧妙使用异步组件Suspense优化页面性能。

文章目录 前言一、为什么需要异步组件&#xff1f;1. 性能瓶颈分析2. 异步组件的价值 二、核心实现方式1. React.lazy Suspense&#xff08;官方推荐&#xff09;2. 路由级代码分割&#xff08;React Router v6&#xff09; 总结 前言 在 React 应用中&#xff0c;随着功能复…

现在环保方面有什么新的技术动态

环保领域的技术发展迅速&#xff0c;尤其在“双碳”目标、数字化转型和可持续发展背景下&#xff0c;涌现出许多创新技术和应用。以下是当前环保领域的新技术动态&#xff08;截至2024年&#xff09;&#xff1a; 一、碳中和与碳减排技术 CCUS&#xff08;碳捕集、利用与封存&a…

solidwors插件 开发————仙盟创梦IDE

SolidWorks VBS SolidWorks 支持通过 VBScript&#xff08;.vbs&#xff09;脚本 进行简单的二次开发&#xff08;如自动化建模、批量操作等&#xff09;&#xff0c;但严格来说这属于 脚本编程&#xff0c;而非传统的插件&#xff08;Plug-in&#xff09;开发&#xff08;插件…

docker(二)初识 docker

在第一章的容器化架构中&#xff0c;我们已经了解到了 docker 是一个容器化技术&#xff0c;本章将详细介绍什么是虚拟化、容器化技术&#xff0c;以及什么是 docker。 一、物理机 VS 虚拟化 VS 容器化 物理机&#xff1a; 实际的服务器或者计算机。是相对于虚拟机而言的对实体…

ChatGPT + DeepSeek 联合润色的 Prompt 模板指令合集,用来润色SCI论文太香了!

对于非英语母语的作者来说,写SCI论文的时候经常会碰到语法错误、表达不够专业、结构不清晰以及术语使用不准确等问题。传统的润色方式要么成本高、效率低,修改过程又耗时又费力。虽然AI工具可以帮助我们来润色论文,但单独用ChatGPT或DeepSeek都会存在内容泛泛、专业性不足的…

python打包exe报错:处理文件时错误:Excel xlsx file; not supported

背景&#xff1a;最近用python写一个excel解析工具&#xff0c;然后打包成exe可执行文件的时候&#xff0c;遇到这样的问题 1.在我自己编译器运行是可以正常将上传后的excel进行解析&#xff0c;但是在打包成exe后&#xff0c;就无法正常解析excel 问题排查&#xff1a; 1.切换…