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

news2025/5/24 18:21:11

一、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 {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 在请求处理之前执行的逻辑
        System.out.println("preHandle被调用,请求即将进入Controller");
        return true; // 返回true表示放行请求,返回false则拦截请求
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 在请求处理之后,视图渲染之前执行的逻辑
        System.out.println("postHandle被调用,Controller方法已执行完毕,视图即将渲染");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 在整个请求处理完成,包括视图渲染之后执行的逻辑
        System.out.println("afterCompletion被调用,整个请求已处理完毕");
    }
}
@Configuration
public class WebMvcConfig1 implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor())
               .addPathPatterns("/**") // 拦截所有请求
                .excludePathPatterns("/static/**"); // 排除/static目录下的静态资源请求
    }
}

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

1.用户认证拦截器

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

@Component
public class JwtHandlerInterceptor implements HandlerInterceptor {

    @Autowired
    private JwtTokenProvider jwtTokenProvider;

    @Autowired
    private UserInfoService userInfoService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        // 取出token
        String 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;
    }


    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public 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 {

    @Override
    public 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);

    @Override
    public 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;
    }

    @Override
    public 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<>();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        long startTime = System.currentTimeMillis();
        startTimeThreadLocal.set(startTime);
        return true;
    }

    @Override
    public 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");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        startTimeThreadLocal.remove();
    }
}

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

@Component
public class AccessLimitInterceptor implements HandlerInterceptor {

    @Autowired
    private RedisTemplate<String, Integer> redisTemplate;

    @Override
    public 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();
    }
            
    @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 {

    }

}

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

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

@Component
public class EncryptionInterceptor implements HandlerInterceptor {

    /**
     * 密钥
     */
    private static final String KEY = "secret_key";

    @Override
    public 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;
    }

    @Override
    public 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);
        }
    }

    @Override
    public 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.coloradmin.cn/o/2384784.html

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

相关文章

有两个Python脚本都在虚拟环境下运行,怎么打包成一个系统服务,按照顺序启动?

环境&#xff1a; SEMCP searx.webapp python 问题描述&#xff1a; 有两个python脚本都在虚拟环境下运行&#xff0c;怎么打包成一个系统服务&#xff0c;按照顺序启动&#xff1f; 解决方案&#xff1a; 将这两个 Python 脚本打包成有启动顺序的系统服务&#xff0c;最…

Python 脚本执行命令的深度探索:方法、示例与最佳实践

在现代软件开发过程中&#xff0c;Python 脚本常常需要与其他工具和命令进行交互&#xff0c;以实现自动化任务、跨工具数据处理等功能。Python 提供了多种方式来执行外部命令&#xff0c;并获取其输出&#xff0c;重定向到文件&#xff0c;而不是直接在终端中显示。这种能力使…

PotPlayer 4K 本地万能影音播放器

今日分享一款来自吾爱论坛大佬分享的啥都能播的的本地播放器&#xff0c;不管是不管是普通视频、4K超清、蓝光3D&#xff0c;还是冷门格式&#xff0c;它基本都能搞定。而且运行流畅不卡顿&#xff0c;电脑配置低也能靠硬件加速&#xff0c;让你根本停不下来。 自带解码器&…

2025年电工杯A题第一版本Q1-Q4详细思路求解+代码运行

A题 光伏电站发电功率日前预测问题 问题背景 光伏发电是通过半导体材料的光电效应&#xff0c;将太阳能直接转化为电能的技术。光伏电站是由众多光伏发电单元组成的规模化发电设施。 光伏电站的发电功率主要由光伏板表面接收到的太阳辐射总量决定&#xff0c;不同季节太阳光…

基于阿里云DashScope API构建智能对话指南

背景 公司想对接AI智能体&#xff0c;用于客服系统&#xff0c;经过调研和实施&#xff0c;觉得DashScope 符合需求。 阿里云推出的DashScope灵积模型服务为开发者提供了便捷高效的大模型接入方案。本文将详细介绍如何基于DashScope API构建一个功能完善的智能对话系统&#x…

九州未来十三载:开源赋能 智启未来

2012年&#xff0c;九州未来以“开源赋能云边变革”为使命&#xff0c;开启中国开放云边基础架构服务的探索之路。十三载坚守深耕&#xff0c;我们始终以开源为翼&#xff0c;以算力为基&#xff0c;在科技浪潮中砥砺前行&#xff0c;见证并推动着AI时代的算力变革。 坚守初心丨…

2025年AI搜索引擎发展洞察:技术革新与市场变革

引言&#xff1a;AI搜索的崛起与市场格局重塑 2024-2025年&#xff0c;AI搜索市场迎来了前所未有的变革期。随着DeepSeek-R1等先进大语言模型的推出&#xff0c;传统搜索引擎、AI原生搜索平台以及各类内容平台纷纷加速智能化转型&#xff0c;推动搜索技术从基础信息检索向深度…

dify调用Streamable HTTP MCP应用

一、概述 上一篇文章&#xff0c;介绍了使用python开发Streamable HTTP MCP应用&#xff0c;链接&#xff1a;https://www.cnblogs.com/xiao987334176/p/18872195 接下来介绍dify如何调用MCP 二、插件 安装插件 需要安装2个插件&#xff0c;分别是&#xff1a;Agent 策略(支持 …

HCIP实验五

一、实验拓扑图&#xff1a; 二、实验需求分析&#xff1a; 1. PreVal策略&#xff1a;要求确保R4通过R2到达192.168.10.0/24 &#xff0c;需在R4上针对去往该网段路由配置PreVal策略&#xff0c;为经R2的路径赋予更高优先值&#xff0c;影响本地路由表选路。 2. AS Path策略…

vivado fpga程序固化

一般下载到fpga上的程序在掉电之后就会丢失&#xff0c;如果想要掉电之后程序不丢失&#xff0c;就需要将比特流文件固化到板载的flash上。 以下以我的7a100t开发板为例&#xff0c;介绍程序固化的流程 点击OK就可以下载了。 一个奇怪的问题 有一次我的一个工程固化之后&…

OpenCV CUDA模块图像特征检测与描述------图像中快速检测特征点类cv::cuda::FastFeatureDetector

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::cuda::FastFeatureDetector 是 OpenCV 的 CUDA 加速模块中的一部分&#xff0c;用于在图像中快速检测特征点。FAST&#xff08;Features fro…

SpringMVC(结合源码浅析工作流程)

SpringMVC 概念 Spring MVC 是基于前端控制器&#xff08;Front Controller&#xff09;设计模式的 Web 框架&#xff0c;在 Web 应用中指一个统一的入口&#xff0c;用来接收所有客户端请求&#xff0c;并统一进行分发、处理。在 SpringMVC 中&#xff0c;前端控制器就是 Di…

学习STC51单片机13(芯片为STC89C52RC)

我去&#xff0c;兄弟们我们今天来学习一个牛逼 的硬件&#xff0c;它叫超声波测距模块HC—SR04 硬件&#xff1a;HC—SR04 哎&#xff0c;想当初最想要玩的就是这个模块&#xff0c;科技感十足&#xff0c;那现在就让我们玩玩吧 超声波测距传感器 原理就是说需要给Trig 10u…

Claude 4 系列 Opus 4 与 Sonnet 4正式发布:Claude 4新特性都有哪些?

随着 Claude 4 系列&#xff08;Opus 4 与 Sonnet 4&#xff09;的正式发布&#xff0c;Anthropic 把自家大模型从“会聊天”推进到“能当自主代理”──不仅推理更深、上下文更长&#xff0c;还内置代码执行、多模态理解、工具调用等一揽子全新能力&#xff1b;同时&#xff0…

深度“求索”:DeepSeek+Dify构建个人知识库

目录 前言 环境部署 安装Docker 安装Dify 配置Dify 部署知识库 创建应用 前言 在当今数字化信息爆炸的时代&#xff0c;数据隐私和个性化知识管理成为企业和个人关注的焦点。Dify&#xff0c;作为一款备受瞩目的开源 AI 应用开发平台&#xff0c;为用户提供了完整的私有…

基于R语言的空间异质性数据分析技术

在自然和社会科学领域&#xff0c;存在大量与地理或空间相关的数据&#xff0c;这些数据通常具有显著的空间异质性。传统的统计学方法在处理这类数据时往往力不从心。基于R语言的一系列空间异质性数据分析方法&#xff0c;如地理加权回归&#xff08;GWR&#xff09;、地理加权…

网络学习-TCP协议(七)

一、TCP协议 TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;是一种面向连接的、可靠的、基于字节流的传输层通信协议。 1、三次握手 客户端&#xff1a; 1、先发起连接&#xff0c;发送SYN置1&#xff0c;seqnum12345(随机值)----半连接…

基于微信小程序的高校校园微活动管理系统设计与实现(源码+定制+开发)高校微信小程序校园活动发布与互动平台开发 面向大学生群体的校园活动移动平台设计与实现

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

云计算与大数据进阶 | 27、存储系统如何突破容量天花板?可扩展架构的核心技术与实践—— 分布式、弹性扩展、高可用的底层逻辑(上)

数据中心里&#xff0c;存储系统是至关重要的组成部分。由于相关硬件组件与存储操作系统的多样性和复杂性&#xff0c;如何在保证存储稳定、安全、可靠的同时&#xff0c;实现灵活扩展和自服务&#xff0c;一直是困扰数据中心全面云化的难题。 简单来说&#xff0c;现在的难题…

IvorySQL-WASM:免安装的数据库探索之旅

简介 为了降低社区用户的使用门槛&#xff0c;提升使用体验&#xff0c;IvorySQL 社区特别推出了 IvorySQL-WASM 项目&#xff0c;帮助用户快速在线 Demo。 IvorySQL-WASM 基于开源的 Postgres-WASM 框架开发。它允许用户直接在网页浏览器中体验 IvorySQL&#xff0c;无需本地…