Spring Boot 拦截器(Interceptor)详解

简介: 本文介绍Spring Boot拦截器的原理与使用,涵盖自定义拦截器创建、注册配置、执行顺序及典型应用场景,助力提升系统安全性与可维护性。(238字)

Spring Boot 拦截器(Interceptor)详解

引言

Spring Boot拦截器是Spring MVC框架的重要组成部分,用于在请求处理的各个阶段执行预处理和后处理逻辑。拦截器在Web应用的安全控制、日志记录、性能监控、权限验证等方面发挥着重要作用,是构建高质量Web应用的基础组件之一。

拦截器基础概念

拦截器的作用和原理

拦截器是Spring MVC框架提供的机制,用于在Controller方法执行前后、视图渲染前后执行特定逻辑。拦截器遵循责任链模式,多个拦截器可以组成拦截器链,按照配置顺序依次执行。

拦截器生命周期

拦截器具有完整的生命周期管理,包含三个主要方法:

  • preHandle():Controller方法执行前调用
  • postHandle():Controller方法执行后,视图渲染前调用
  • afterCompletion():整个请求处理完成后调用

拦截器实现方式

实现HandlerInterceptor接口

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CustomInterceptor implements HandlerInterceptor {
   

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 
            throws Exception {
   
        System.out.println("preHandle: 请求处理前执行");

        // 获取请求信息
        String requestURI = request.getRequestURI();
        String method = request.getMethod();
        String userAgent = request.getHeader("User-Agent");

        System.out.println("请求URI: " + requestURI);
        System.out.println("请求方法: " + method);
        System.out.println("User-Agent: " + userAgent);

        // 验证逻辑
        if (requestURI.contains("/admin") && !isAdminUser(request)) {
   
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("Access denied");
            return false; // 阻止请求继续执行
        }

        return true; // 继续执行
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, 
                          Object handler, ModelAndView modelAndView) throws Exception {
   
        System.out.println("postHandle: Controller执行后,视图渲染前执行");

        // 可以修改ModelAndView
        if (modelAndView != null) {
   
            modelAndView.addObject("currentTime", System.currentTimeMillis());
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                               Object handler, Exception ex) throws Exception {
   
        System.out.println("afterCompletion: 请求处理完成后执行");

        if (ex != null) {
   
            System.out.println("请求处理发生异常: " + ex.getMessage());
        }

        // 记录请求处理时间
        Long startTime = (Long) request.getAttribute("startTime");
        if (startTime != null) {
   
            long duration = System.currentTimeMillis() - startTime;
            System.out.println("请求处理耗时: " + duration + "ms");
        }
    }

    private boolean isAdminUser(HttpServletRequest request) {
   
        // 简单的管理员验证逻辑
        String adminToken = request.getHeader("X-Admin-Token");
        return "admin-secret-token".equals(adminToken);
    }
}

使用@Component注解

import org.springframework.stereotype.Component;

@Component
public class ComponentInterceptor implements HandlerInterceptor {
   

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 
            throws Exception {
   
        System.out.println("ComponentInterceptor preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, 
                          Object handler, ModelAndView modelAndView) throws Exception {
   
        System.out.println("ComponentInterceptor postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                               Object handler, Exception ex) throws Exception {
   
        System.out.println("ComponentInterceptor afterCompletion");
    }
}

Spring Boot拦截器配置

使用WebMvcConfigurer

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
   

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
   
        // 注册自定义拦截器
        registry.addInterceptor(new CustomInterceptor())
                .addPathPatterns("/**") // 拦截所有请求
                .excludePathPatterns("/login", "/register", "/public/**"); // 排除特定路径

        // 注册组件拦截器
        registry.addInterceptor(new ComponentInterceptor())
                .addPathPatterns("/api/**"); // 只拦截API请求
    }
}

使用Bean配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class InterceptorBeanConfig {
   

    @Bean
    public CustomInterceptor customInterceptor() {
   
        return new CustomInterceptor();
    }
}

常见拦截器实现

日志拦截器

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LoggingInterceptor implements HandlerInterceptor {
   
    private static final Logger logger = LoggerFactory.getLogger(LoggingInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 
            throws Exception {
   
        String requestURI = request.getRequestURI();
        String method = request.getMethod();
        String remoteAddr = request.getRemoteAddr();
        String userAgent = request.getHeader("User-Agent");

        logger.info("请求开始 - URI: {}, Method: {}, IP: {}, User-Agent: {}", 
                   requestURI, method, remoteAddr, userAgent);

        // 记录开始时间
        request.setAttribute("startTime", System.currentTimeMillis());

        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                               Object handler, Exception ex) throws Exception {
   
        String requestURI = request.getRequestURI();
        int status = response.getStatus();

        Long startTime = (Long) request.getAttribute("startTime");
        long duration = startTime != null ? System.currentTimeMillis() - startTime : 0;

        logger.info("请求结束 - URI: {}, Status: {}, Duration: {}ms", 
                   requestURI, status, duration);

        if (ex != null) {
   
            logger.error("请求异常 - URI: {}, Error: {}", requestURI, ex.getMessage(), ex);
        }
    }
}

权限拦截器

public class AuthorizationInterceptor implements HandlerInterceptor {
   

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 
            throws Exception {
   
        String token = request.getHeader("Authorization");

        if (token == null || !isValidToken(token)) {
   
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("{\"error\":\"Unauthorized\"}");
            return false;
        }

        // 验证权限
        String requiredPermission = getRequiredPermission(request.getRequestURI());
        if (!hasPermission(token, requiredPermission)) {
   
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            response.getWriter().write("{\"error\":\"Forbidden\"}");
            return false;
        }

        return true;
    }

    private boolean isValidToken(String token) {
   
        // 简单的token验证逻辑
        return token.startsWith("Bearer ") && token.length() > 10;
    }

    private String getRequiredPermission(String uri) {
   
        // 根据URI确定所需权限
        if (uri.contains("/admin")) {
   
            return "ADMIN";
        } else if (uri.contains("/user")) {
   
            return "USER";
        }
        return "GUEST";
    }

    private boolean hasPermission(String token, String requiredPermission) {
   
        // 权限验证逻辑
        return true; // 简化示例
    }
}

性能监控拦截器

import java.util.concurrent.ConcurrentHashMap;

public class PerformanceInterceptor implements HandlerInterceptor {
   
    private final ConcurrentHashMap<String, RequestStats> stats = new ConcurrentHashMap<>();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 
            throws Exception {
   
        request.setAttribute("startTime", System.currentTimeMillis());
        request.setAttribute("startNanoTime", System.nanoTime());
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                               Object handler, Exception ex) throws Exception {
   
        Long startTime = (Long) request.getAttribute("startTime");
        Long startNanoTime = (Long) request.getAttribute("startNanoTime");

        if (startTime != null && startNanoTime != null) {
   
            long duration = System.currentTimeMillis() - startTime;
            long nanoDuration = System.nanoTime() - startNanoTime;

            String uri = request.getRequestURI();
            String method = request.getMethod();
            String fullUri = method + " " + uri;

            stats.computeIfAbsent(fullUri, k -> new RequestStats())
                 .update(duration, nanoDuration);

            // 记录慢请求
            if (duration > 1000) {
    // 超过1秒的请求
                System.out.println("慢请求: " + fullUri + ", 耗时: " + duration + "ms");
            }
        }
    }

    public void printStats() {
   
        stats.forEach((uri, stat) -> {
   
            System.out.printf("URI: %s, Avg: %.2fms, Min: %dms, Max: %dms, Count: %d%n",
                            uri, stat.getAverage(), stat.getMin(), stat.getMax(), stat.getCount());
        });
    }

    private static class RequestStats {
   
        private long totalDuration = 0;
        private long totalNanoDuration = 0;
        private long requestCount = 0;
        private long minDuration = Long.MAX_VALUE;
        private long maxDuration = 0;

        public synchronized void update(long duration, long nanoDuration) {
   
            totalDuration += duration;
            totalNanoDuration += nanoDuration;
            requestCount++;
            minDuration = Math.min(minDuration, duration);
            maxDuration = Math.max(maxDuration, duration);
        }

        public double getAverage() {
   
            return requestCount > 0 ? (double) totalDuration / requestCount : 0;
        }

        public long getMin() {
   
            return minDuration == Long.MAX_VALUE ? 0 : minDuration;
        }

        public long getMax() {
   
            return maxDuration;
        }

        public long getCount() {
   
            return requestCount;
        }
    }
}

参数验证拦截器

import org.springframework.util.StringUtils;

public class ValidationInterceptor implements HandlerInterceptor {
   

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

        // 验证请求参数
        String[] requiredParams = getRequiredParams(request.getRequestURI());
        for (String param : requiredParams) {
   
            String value = request.getParameter(param);
            if (!StringUtils.hasText(value)) {
   
                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                response.getWriter().write("{\"error\":\"Missing required parameter: " + param + "\"}");
                return false;
            }
        }

        // 验证请求体(JSON)
        if (isJsonRequest(request)) {
   
            String requestBody = getRequestBody(request);
            if (!isValidJson(requestBody)) {
   
                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                response.getWriter().write("{\"error\":\"Invalid JSON format\"}");
                return false;
            }
        }

        return true;
    }

    private String[] getRequiredParams(String uri) {
   
        // 根据URI返回必需参数
        if (uri.contains("/user/create")) {
   
            return new String[]{
   "username", "email"};
        } else if (uri.contains("/order/create")) {
   
            return new String[]{
   "productId", "quantity"};
        }
        return new String[]{
   };
    }

    private boolean isJsonRequest(HttpServletRequest request) {
   
        String contentType = request.getContentType();
        return contentType != null && contentType.contains("application/json");
    }

    private String getRequestBody(HttpServletRequest request) throws Exception {
   
        StringBuilder sb = new StringBuilder();
        String line;
        try (java.io.BufferedReader reader = request.getReader()) {
   
            while ((line = reader.readLine()) != null) {
   
                sb.append(line);
            }
        }
        return sb.toString();
    }

    private boolean isValidJson(String json) {
   
        try {
   
            // 简单的JSON验证
            if (json == null || json.trim().isEmpty()) {
   
                return false;
            }
            return json.trim().startsWith("{") && json.trim().endsWith("}");
        } catch (Exception e) {
   
            return false;
        }
    }
}

CORS拦截器

public class CorsInterceptor implements HandlerInterceptor {
   

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

        // 设置CORS头
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", 
                          "GET, POST, PUT, DELETE, OPTIONS, PATCH");
        response.setHeader("Access-Control-Allow-Headers", 
                          "Content-Type, Authorization, X-Requested-With, X-Admin-Token");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Credentials", "true");

        // 处理预检请求
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
   
            response.setStatus(HttpServletResponse.SC_OK);
            return false; // 阻止后续处理
        }

        return true;
    }
}

拦截器链管理

拦截器执行顺序

在Spring Boot中,拦截器的执行顺序非常重要,可以通过以下方式控制:

// 在配置类中按添加顺序执行
@Configuration
public class OrderedInterceptorConfig implements WebMvcConfigurer {
   

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
   
        // 第一个执行
        registry.addInterceptor(new FirstInterceptor())
                .addPathPatterns("/**");

        // 第二个执行
        registry.addInterceptor(new SecondInterceptor())
                .addPathPatterns("/**");

        // 第三个执行
        registry.addInterceptor(new ThirdInterceptor())
                .addPathPatterns("/**");
    }
}

条件拦截器

public class ConditionalInterceptor implements HandlerInterceptor {
   

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

        // 根据条件决定是否执行
        if (shouldIntercept(request)) {
   
            // 执行拦截逻辑
            System.out.println("条件拦截器执行: " + request.getRequestURI());
            return true;
        }

        return true; // 不拦截,继续执行
    }

    private boolean shouldIntercept(HttpServletRequest request) {
   
        String uri = request.getRequestURI();
        String method = request.getMethod();

        // 根据URI和方法判断是否需要拦截
        return uri.startsWith("/api/") && "POST".equals(method);
    }
}

高级拦截器应用

限流拦截器

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

public class RateLimitInterceptor implements HandlerInterceptor {
   
    private final ConcurrentHashMap<String, AtomicInteger> requestCounters = new ConcurrentHashMap<>();
    private final long windowSize = 60000; // 1分钟窗口
    private final int maxRequests = 100; // 每分钟最大请求数

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

        String clientIP = getClientIP(request);
        long currentTime = System.currentTimeMillis();
        String key = clientIP + "_" + (currentTime / windowSize);

        AtomicInteger counter = requestCounters.computeIfAbsent(key, k -> new AtomicInteger(0));
        int currentCount = counter.incrementAndGet();

        if (currentCount > maxRequests) {
   
            response.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS);
            response.getWriter().write("{\"error\":\"Rate limit exceeded\"}");
            return false;
        }

        // 清理过期的计数器
        cleanExpiredCounters(currentTime);

        return true;
    }

    private String getClientIP(HttpServletRequest request) {
   
        String xForwardedFor = request.getHeader("X-Forwarded-For");
        if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
   
            return xForwardedFor.split(",")[0].trim();
        }

        String xRealIP = request.getHeader("X-Real-IP");
        if (xRealIP != null && !xRealIP.isEmpty()) {
   
            return xRealIP;
        }

        return request.getRemoteAddr();
    }

    private void cleanExpiredCounters(long currentTime) {
   
        long cutoffTime = (currentTime / windowSize) - 2; // 保留2个窗口的数据
        requestCounters.entrySet().removeIf(entry -> {
   
            String[] parts = entry.getKey().split("_");
            if (parts.length == 2) {
   
                try {
   
                    long window = Long.parseLong(parts[1]);
                    return window < cutoffTime;
                } catch (NumberFormatException e) {
   
                    return true;
                }
            }
            return false;
        });
    }
}

缓存拦截器

public class CacheInterceptor implements HandlerInterceptor {
   
    private final ConcurrentHashMap<String, CachedResponse> cache = new ConcurrentHashMap<>();
    private final long cacheTimeout = 300000; // 5分钟

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

        String cacheKey = generateCacheKey(request);
        CachedResponse cached = cache.get(cacheKey);

        if (cached != null && !cached.isExpired()) {
   
            // 返回缓存的响应
            cached.writeTo(response);
            return false; // 阻止后续处理
        }

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, 
                          Object handler, ModelAndView modelAndView) throws Exception {
   

        String cacheKey = generateCacheKey(request);
        if (response.getStatus() == 200) {
   
            // 缓存响应
            CachedResponse cachedResponse = new CachedResponse(response);
            cache.put(cacheKey, cachedResponse);
        }
    }

    private String generateCacheKey(HttpServletRequest request) {
   
        return request.getMethod() + "_" + request.getRequestURI() + 
               "_" + request.getQueryString();
    }

    private static class CachedResponse {
   
        private final byte[] content;
        private final int status;
        private final java.util.Map<String, String> headers;
        private final long timestamp;

        public CachedResponse(HttpServletResponse response) {
   
            // 这里需要捕获响应内容,实际实现会更复杂
            this.status = response.getStatus();
            this.headers = new java.util.HashMap<>();
            response.getHeaderNames().forEach(name -> 
                headers.put(name, response.getHeader(name)));
            this.timestamp = System.currentTimeMillis();
            this.content = new byte[0]; // 简化示例
        }

        public boolean isExpired() {
   
            return System.currentTimeMillis() - timestamp > 300000; // 5分钟
        }

        public void writeTo(HttpServletResponse response) throws Exception {
   
            response.setStatus(status);
            headers.forEach(response::setHeader);
            // response.getOutputStream().write(content);
        }
    }
}

数据脱敏拦截器

public class DataMaskingInterceptor implements HandlerInterceptor {
   

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, 
                          Object handler, ModelAndView modelAndView) throws Exception {
   

        if (modelAndView != null && modelAndView.getModel() != null) {
   
            // 对模型数据进行脱敏处理
            modelAndView.getModel().forEach((key, value) -> {
   
                if (value instanceof java.util.Map) {
   
                    maskSensitiveData((java.util.Map<String, Object>) value);
                } else if (value instanceof java.util.List) {
   
                    maskSensitiveDataInList((java.util.List<Object>) value);
                }
            });
        }
    }

    private void maskSensitiveData(java.util.Map<String, Object> data) {
   
        data.replaceAll((key, value) -> {
   
            if (isSensitiveField(key)) {
   
                return maskValue(value);
            }
            return value;
        });
    }

    private void maskSensitiveDataInList(java.util.List<Object> list) {
   
        for (int i = 0; i < list.size(); i++) {
   
            Object item = list.get(i);
            if (item instanceof java.util.Map) {
   
                maskSensitiveData((java.util.Map<String, Object>) item);
            }
        }
    }

    private boolean isSensitiveField(String fieldName) {
   
        return fieldName.toLowerCase().contains("password") ||
               fieldName.toLowerCase().contains("phone") ||
               fieldName.toLowerCase().contains("id_card") ||
               fieldName.toLowerCase().contains("email");
    }

    private Object maskValue(Object value) {
   
        if (value instanceof String) {
   
            String str = (String) value;
            if (str.length() <= 4) {
   
                return str.replaceAll(".", "*");
            } else {
   
                return str.substring(0, 2) + "****" + str.substring(str.length() - 2);
            }
        }
        return "***MASKED***";
    }
}

拦截器与Spring Security集成

Spring Security拦截器链

@Configuration
@EnableWebSecurity
public class SecurityInterceptorConfig {
   

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
   
        http
            .authorizeHttpRequests(authz -> authz
                .requestMatchers("/public/**").permitAll()
                .anyRequest().authenticated()
            )
            .addFilterBefore(securityInterceptor(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

    @Bean
    public CustomSecurityInterceptor securityInterceptor() {
   
        return new CustomSecurityInterceptor();
    }
}

自定义安全拦截器

public class CustomSecurityInterceptor implements HandlerInterceptor {
   

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

        String token = request.getHeader("Authorization");

        if (token != null && token.startsWith("Bearer ")) {
   
            String jwtToken = token.substring(7);
            try {
   
                // 验证JWT token
                if (isValidToken(jwtToken)) {
   
                    // 设置认证信息到SecurityContext
                    setAuthentication(jwtToken);
                }
            } catch (Exception e) {
   
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                return false;
            }
        }

        return true;
    }

    private boolean isValidToken(String token) {
   
        // JWT验证逻辑
        return true; // 简化示例
    }

    private void setAuthentication(String token) {
   
        // 设置Spring Security认证信息
        org.springframework.security.core.Authentication auth = 
            new org.springframework.security.authentication.UsernamePasswordAuthenticationToken(
                "user", null, java.util.Collections.emptyList());
        org.springframework.security.core.context.SecurityContextHolder.getContext()
            .setAuthentication(auth);
    }
}

拦截器性能优化

异步拦截器

public class AsyncInterceptor implements AsyncHandlerInterceptor {
   

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 
            throws Exception {
   
        if (request.isAsyncStarted()) {
   
            // 异步请求处理
            request.setAttribute("asyncStartTime", System.currentTimeMillis());
        }
        return true;
    }

    @Override
    public void afterConcurrentHandlingStarted(HttpServletRequest request, 
                                              HttpServletResponse response, 
                                              Object handler) throws Exception {
   
        System.out.println("异步处理开始");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                               Object handler, Exception ex) throws Exception {
   
        if (request.getAttribute("asyncStartTime") != null) {
   
            long startTime = (Long) request.getAttribute("asyncStartTime");
            long duration = System.currentTimeMillis() - startTime;
            System.out.println("异步请求处理耗时: " + duration + "ms");
        }
    }
}

拦截器缓存优化

@Component
public class CachedInterceptor implements HandlerInterceptor {
   
    private final com.github.benmanes.caffeine.cache.Cache<String, Boolean> permissionCache;

    public CachedInterceptor() {
   
        this.permissionCache = com.github.benmanes.caffeine.cache.Caffeine.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(java.time.Duration.ofMinutes(10))
            .build(this::checkPermission);
    }

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

        String resource = request.getRequestURI();
        String token = request.getHeader("Authorization");

        if (token != null && permissionCache.get(resource)) {
   
            return true;
        }

        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        return false;
    }

    private Boolean checkPermission(String resource) {
   
        // 权限检查逻辑
        return true; // 简化示例
    }
}

拦截器测试

单元测试

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest
class InterceptorTest {
   

    @Autowired
    private MockMvc mockMvc;

    @Test
    void testCustomInterceptor() throws Exception {
   
        mockMvc.perform(get("/api/test")
                .header("X-Admin-Token", "admin-secret-token"))
                .andExpect(status().isOk());
    }

    @Test
    void testUnauthorizedAccess() throws Exception {
   
        mockMvc.perform(get("/admin/protected"))
                .andExpect(status().isUnauthorized());
    }
}

集成测试

@SpringBootTest
@AutoConfigureTestDatabase
class InterceptorIntegrationTest {
   

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    void testRateLimitInterceptor() {
   
        // 发送多个请求测试限流
        for (int i = 0; i < 150; i++) {
   
            ResponseEntity<String> response = restTemplate.getForEntity("/api/test", String.class);
            if (i >= 100) {
   
                // 超过限流应该返回429
                assertEquals(HttpStatus.TOO_MANY_REQUESTS, response.getStatusCode());
            }
        }
    }
}

实际应用场景

API网关拦截器

public class ApiGatewayInterceptor implements HandlerInterceptor {
   

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

        // API版本控制
        String apiVersion = request.getHeader("API-Version");
        if (apiVersion == null) {
   
            apiVersion = "v1"; // 默认版本
        }

        // API限流
        if (!isWithinRateLimit(request)) {
   
            response.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS);
            response.getWriter().write("{\"error\":\"API rate limit exceeded\"}");
            return false;
        }

        // API认证
        if (!isValidApiKey(request)) {
   
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("{\"error\":\"Invalid API key\"}");
            return false;
        }

        return true;
    }

    private boolean isWithinRateLimit(HttpServletRequest request) {
   
        // 限流逻辑
        return true; // 简化示例
    }

    private boolean isValidApiKey(HttpServletRequest request) {
   
        String apiKey = request.getHeader("X-API-Key");
        // API密钥验证逻辑
        return apiKey != null && apiKey.length() > 10;
    }
}

多租户拦截器

public class TenantInterceptor implements HandlerInterceptor {
   

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

        String tenantId = request.getHeader("X-Tenant-ID");
        if (tenantId == null || tenantId.trim().isEmpty()) {
   
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.getWriter().write("{\"error\":\"Tenant ID is required\"}");
            return false;
        }

        // 设置租户上下文
        TenantContext.setCurrentTenant(tenantId);

        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                               Object handler, Exception ex) throws Exception {
   
        // 清理租户上下文
        TenantContext.clear();
    }
}

class TenantContext {
   
    private static final ThreadLocal<String> currentTenant = new ThreadLocal<>();

    public static void setCurrentTenant(String tenantId) {
   
        currentTenant.set(tenantId);
    }

    public static String getCurrentTenant() {
   
        return currentTenant.get();
    }

    public static void clear() {
   
        currentTenant.remove();
    }
}

最佳实践和注意事项

性能考虑

  1. 避免在拦截器中执行耗时操作
  2. 使用缓存减少重复计算
  3. 合理设置拦截器执行顺序
  4. 及时清理资源

安全考虑

  1. 验证输入参数的安全性
  2. 防止敏感信息泄露
  3. 实现适当的错误处理
  4. 使用HTTPS传输

调试和监控

// 添加拦截器执行时间监控
public class MonitoringInterceptor implements HandlerInterceptor {
   
    private final io.micrometer.core.instrument.MeterRegistry meterRegistry;

    public MonitoringInterceptor(io.micrometer.core.instrument.MeterRegistry meterRegistry) {
   
        this.meterRegistry = meterRegistry;
    }

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

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                               Object handler, Exception ex) throws Exception {
   
        Long startTime = (Long) request.getAttribute("startTime");
        if (startTime != null) {
   
            long duration = System.currentTimeMillis() - startTime;

            io.micrometer.core.instrument.Timer.Sample sample = 
                io.micrometer.core.instrument.Timer.start(meterRegistry);

            sample.stop(io.micrometer.core.instrument.Timer.builder("interceptor.execution.time")
                .tag("interceptor", this.getClass().getSimpleName())
                .register(meterRegistry));
        }
    }
}

总结

Spring Boot拦截器是构建Web应用的重要组件,通过合理使用拦截器可以实现请求预处理、安全控制、日志记录、性能监控等多种功能。在实际开发中,需要根据具体需求选择合适的拦截器实现方式,并注意性能优化和安全考虑,以构建高质量的Web应用程序。



关于作者



🌟 我是suxiaoxiang,一位热爱技术的开发者

💡 专注于Java生态和前沿技术分享

🚀 持续输出高质量技术内容



如果这篇文章对你有帮助,请支持一下:




👍 点赞


收藏


👀 关注



您的支持是我持续创作的动力!感谢每一位读者的关注与认可!


目录
相关文章
|
安全 Java Spring
Spring Boot 过滤器(Filter)详解
本文详解Spring Boot中过滤器的原理与实践,涵盖Filter接口、执行流程、@Component与FilterRegistrationBean两种实现方式、执行顺序控制及典型应用场景如日志记录、权限验证。对比拦截器,突出其在Servlet容器层的通用性与灵活性,助力构建高效稳定的Web应用。
4701 0
|
Java 数据处理
【十二】springboot整合WebService
【十二】springboot整合WebService
1194 0
|
JSON Java 应用服务中间件
HttpServletRequest核心方法以及获取请求参数
HttpServletRequest核心方法以及获取请求参数
2992 0
|
4月前
|
缓存 监控 Java
《深入理解Spring》拦截器(Interceptor)——请求处理的艺术
Spring拦截器是Web开发中实现横切关注点的核心组件,基于AOP思想,可在请求处理前后执行日志记录、身份验证、权限控制等通用逻辑。相比Servlet过滤器,拦截器更贴近Spring容器,能访问Bean和上下文,适用于Controller级精细控制。通过实现`HandlerInterceptor`接口的`preHandle`、`postHandle`和`afterCompletion`方法,可灵活控制请求流程。结合配置类注册并设置路径匹配与执行顺序,实现高效复用与维护。常用于认证鉴权、性能监控、统一异常处理等场景,提升应用安全性与可维护性。
|
11月前
|
前端开发 JavaScript Java
微服务——SpringBoot使用归纳——Spring Boot中使用拦截器——拦截器的快速使用
本文介绍了在Spring Boot中使用拦截器的方法,包括拦截器的基本原理、定义与配置步骤。拦截器是AOP的一种实现,主要用于拦截对动态资源的请求,如判断用户权限或结合WebSocket使用。文章详细讲解了通过实现`HandlerInterceptor`接口来自定义拦截器,并重写`preHandle`、`postHandle`和`afterCompletion`三个核心方法。
1611 0
|
前端开发 JavaScript Java
【Spring Boot】 深入理解Spring Boot拦截器:自定义设计与实现全攻略
【Spring Boot】 深入理解Spring Boot拦截器:自定义设计与实现全攻略
506 0
|
存储 算法 安全
SpringBoot 接口加密解密实现
【10月更文挑战第18天】
|
监控 Java 数据安全/隐私保护
如何用Spring Boot实现拦截器:从入门到实践
如何用Spring Boot实现拦截器:从入门到实践
781 5
|
XML 缓存 算法
SpringBoot2 | SpingBoot FilterRegistrationBean 注册组件 | FilterChain 责任链源码分析(九)
SpringBoot2 | SpingBoot FilterRegistrationBean 注册组件 | FilterChain 责任链源码分析(九)
408 0
|
Java API Spring
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中拦截器的入门教程和实战项目场景实现的详细指南。
357 0
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现