java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot拦截Http请求

SpringBoot实现拦截Http请求并设置请求头

作者:你这个代码我看不懂

这篇文章主要为大家详细介绍了如何使用SpringBoot实现拦截Http请求并设置请求头,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

ClientHttpRequestInterceptor拦截器

在Spring Boot中,你可以通过拦截RestTemplate请求并在发送前设置请求头,通常有以下两种方式实现:

1. 使用ClientHttpRequestInterceptor

通过自定义拦截器ClientHttpRequestInterceptor,在intercept方法中修改请求头,然后将拦截器添加到RestTemplate实例中。

步骤:

示例代码:

import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.util.Collections;

public class HeaderInterceptor implements ClientHttpRequestInterceptor {
    
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, 
                                      ClientHttpRequestExecution execution) throws IOException {
        // 添加请求头
        request.getHeaders().add("Authorization", "Bearer your-token");
        request.getHeaders().add("Custom-Header", "custom-value");
        
        return execution.execute(request, body);
    }
}

// 配置RestTemplate
@Bean
public RestTemplate restTemplate() {
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.setInterceptors(Collections.singletonList(new HeaderInterceptor()));
    return restTemplate;
}

2. 使用RestTemplateBuilder(推荐)

通过RestTemplateBuilder可以更方便地添加拦截器,支持链式调用。

示例代码:

import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {
    
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder
                .additionalInterceptors(new HeaderInterceptor())
                .build();
    }
}

3. 动态设置请求头(针对特定请求)

如果某些请求头需要动态设置(如从上下文获取token),可以在拦截器中加入逻辑:

public class DynamicHeaderInterceptor implements ClientHttpRequestInterceptor {
    
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, 
                                      ClientHttpRequestExecution execution) throws IOException {
        // 从安全上下文或其他来源动态获取值
        String token = SecurityContextHolder.getContext().getAuthentication().getCredentials().toString();
        request.getHeaders().add("Authorization", "Bearer " + token);
        
        return execution.execute(request, body);
    }
}

注意事项

如果需要更细粒度的控制,可以考虑使用RestTemplateexchange()方法直接传入HttpEntity(包含头部信息),但这种方式需要在每次调用时显式设置。

统一拦截所有HTTP请求

要统一拦截所有HTTP请求(包括RestTemplate、WebClient、Feign等),可以通过以下几种方式实现:

1. 使用Spring的ClientHttpRequestInterceptor(仅适用于RestTemplate)

适用于所有通过RestTemplate发起的请求,但对其他客户端无效。

2. 使用Servlet Filter(最通用)

Filter可以拦截所有进入容器的HTTP请求,包括:

示例代码:

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@Component
@Order(1) // 执行顺序
public class GlobalHttpFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        
        // 添加全局请求头
        if (httpRequest.getHeader("Authorization") == null) {
            // 这里可以设置默认认证头或其他通用头
        }
        
        // 记录请求日志
        System.out.println("Request URL: " + httpRequest.getRequestURL());
        System.out.println("Method: " + httpRequest.getMethod());
        
        chain.doFilter(request, response); // 继续执行过滤器链
    }
}

3. 使用AOP面向切面编程

通过AOP拦截所有HTTP客户端调用方法:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class HttpRequestAspect {

    @Around("execution(* org.springframework.web.client.RestTemplate.*(..)) || " +
            "execution(* org.springframework.web.reactive.function.client.WebClient.*(..))")
    public Object aroundHttpRequest(ProceedingJoinPoint joinPoint) throws Throwable {
        
        // 在方法执行前添加请求头
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
            if (args[i] instanceof HttpEntity) {
                HttpEntity<?> entity = (HttpEntity<?>) args[i];
                HttpHeaders headers = new HttpHeaders();
                headers.putAll(entity.getHeaders());
                headers.add("Global-Header", "common-value");
                
                // 替换为新的HttpEntity
                args[i] = new HttpEntity<>(entity.getBody(), headers);
            }
        }
        
        return joinPoint.proceed(args);
    }
}

4. 使用OkHttp的Interceptor(如果使用OkHttp作为底层客户端)

import okhttp3.Interceptor;
import okhttp3.Response;
import java.io.IOException;

public class GlobalOkHttpInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        return chain.proceed(chain.request().newBuilder()
                .addHeader("Authorization", "Bearer token")
                .addHeader("X-Request-ID", UUID.randomUUID().toString())
                .build());
    }
}

5. 推荐方案:组合使用

拦截方式适用范围优点缺点
Servlet Filter所有HTTP请求最通用,覆盖全面无法区分内部/外部请求
AOP方法级别拦截精确控制,可区分业务配置相对复杂
ClientHttpRequestInterceptor仅RestTemplate简单易用局限性大
OkHttp Interceptor使用OkHttp的客户端性能好需要统一HTTP客户端

最佳实践建议:

根据你的需求,Servlet Filter通常是最合适的统一拦截方案。

手动拦截并设置(反射):

public void setRequestHeader(String key, String value) {
        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
            return;
        }
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (null == attributes) {
            return;
        }
        HttpServletRequest request = attributes.getRequest();
        Class<? extends HttpServletRequest> requestClass = request.getClass();
        try {
            // 反射获取request属性
            Field headerField = requestClass.getDeclaredField("headerMap");
            headerField.setAccessible(true);
            Map<String, String> headerMap = (Map<String, String>) headerField.get(request);
            headerMap.put(key, value);
        } catch (Exception e) {
            log.error("set header error: ", e);
        }
    }

ClientHttpRequestInterceptor 会在 每次 通过配置了该拦截器的 RestTemplate 实例发起 HTTP 调用之前执行。

工作原理

当你调用 RestTemplate 的任何方法(如 getForObject, postForEntity, exchange 等)时,Spring 会按以下顺序处理:

关键特性

示例场景验证

假设你有以下配置和代码:

// 1. 定义一个打印日志的拦截器
public class LoggingInterceptor implements ClientHttpRequestInterceptor {
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        System.out.println("【拦截器执行】准备发送请求至: " + request.getURI());
        // ... 可以在这里添加请求头
        return execution.execute(request, body);
    }
}

// 2. 将拦截器添加到 RestTemplate
@Bean
public RestTemplate restTemplate() {
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.setInterceptors(Collections.singletonList(new LoggingInterceptor()));
    return restTemplate;
}

// 3. 在服务中多次使用 RestTemplate
@Service
public class MyService {

    @Autowired
    private RestTemplate restTemplate;

    public void fetchData() {
        // 第一次调用
        String result1 = restTemplate.getForObject("https://api.example.com/data/1", String.class);
        
        // 第二次调用
        String result2 = restTemplate.getForObject("https://api.example.com/data/2", String.class);
    }
}

当你调用 myService.fetchData() 时,控制台会输出:

【拦截器执行】准备发送请求至: https://api.example.com/data/1
【拦截器执行】准备发送请求至: https://api.example.com/data/2

这清楚地证明了拦截器为每次请求都执行了一次。

总结

你可以放心地依赖 ClientHttpRequestInterceptor 来执行诸如添加认证令牌、设置公共请求头、记录请求日志、重试逻辑等需要在每个请求发出前完成的统一操作。它的设计初衷就是为此类场景服务的。

Attribute和Header

下面是一个详细的对比表格,帮助你更清晰地理解:

特性Attribute(属性)Header(头信息)
作用域服务器端的一次请求生命周期内(Request Scope)**HTTP协议本身,在客户端和服务器之间传输
可见性仅服务器内部可见(如Filter, Interceptor, Controller, Service, JSP/Thymeleaf等)客户端和服务器都可见,可通过浏览器开发者工具或抓包工具查看
用途在服务器处理请求的过程中,在不同组件间传递数据。例如:
- Filter校验用户后,将用户信息存入attribute供Controller使用
- Interceptor计算耗时后,将耗时结果存入attribute
传递HTTP协议的元数据。例如:
- Content-Type: 声明请求体/响应体的格式(如application/json)
- Authorization: 传递认证凭证(如Bearer token)
- User-Agent: 声明客户端类型
- Custom-Header: 自定义业务头(如X-Request-ID用于全链路追踪)
生命周期从请求进入服务器开始,到服务器返回响应结束。请求结束即销毁。请求头(Request Header) 随请求从客户端发往服务器。
响应头(Response Header) 随响应从服务器发往客户端。
数据类型可以是任何Java对象(Object)只能是字符串(String)或字符串数组(String[])
操作方式 (Java)request.setAttribute("key", value);
Object value = request.getAttribute("key");
获取请求头:
String value = request.getHeader("Header-Name");
设置响应头:
response.setHeader("Header-Name", "value");
response.addHeader("Header-Name", "value");

类比帮助理解

你可以把它们想象成寄送一个实体包裹:

代码示例

1. 操作 Header (在Servlet Filter或Controller中)

// 从客户端发来的请求中获取头信息
String authHeader = httpRequest.getHeader("Authorization");
String userAgent = httpRequest.getHeader("User-Agent");

// 向客户端发送响应时设置头信息
httpResponse.setHeader("Content-Type", "application/json");
httpResponse.addHeader("X-Custom-Header", "MyValue");

2. 操作 Attribute (在Filter, Interceptor, Controller中)

// 在Filter中设置属性
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    // 认证成功后,将用户信息存入attribute
    User user = authenticateUser(httpRequest);
    httpRequest.setAttribute("currentUser", user); // 存入任何对象
    chain.doFilter(request, response);
}

// 在Controller中获取和使用属性
@GetMapping("/api/profile")
public ResponseEntity getUserProfile(HttpServletRequest request) {
    // 从attribute中取出之前存入的对象
    User currentUser = (User) request.getAttribute("currentUser");
    // ... 使用user对象处理业务
    return ResponseEntity.ok(currentUser.getProfile());
}

总结

记住这个最简单的原则:

到此这篇关于SpringBoot实现拦截Http请求并设置请求头的文章就介绍到这了,更多相关SpringBoot拦截Http请求内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文