如何通过Filter为HttpServletRequest添加自定义请求头?
如何在Filter中为HttpServletRequest添加自定义请求头
你遇到的问题很典型——原生的HttpServletRequest是只读设计,没有提供直接修改或添加请求头的方法,因为它是对底层HTTP请求的封装,规范上不允许随意篡改原始请求数据。虽然你看到Tomcat的CoyoteRequest内部有请求头列表,但直接操作它属于依赖容器私有实现,耦合性极高,换用Jetty等其他Web容器就会失效,非常不推荐。
正确的做法是使用**HttpServletRequestWrapper**包装原始请求,通过重写相关方法注入自定义请求头,这是Servlet规范提供的标准解决方案,兼容性拉满。
步骤1:自定义HttpServletRequestWrapper子类
创建一个包装类,重写获取请求头的方法,把自定义请求头和原始请求头整合在一起:
public class CustomHeaderRequestWrapper extends HttpServletRequestWrapper { private final Map<String, String> customHeaders; public CustomHeaderRequestWrapper(HttpServletRequest request) { super(request); this.customHeaders = new HashMap<>(); } // 提供添加自定义请求头的方法 public void addCustomHeader(String name, String value) { customHeaders.put(name, value); } @Override public String getHeader(String name) { // 优先返回自定义请求头,没有则用原始请求的 String customHeader = customHeaders.get(name); if (customHeader != null) { return customHeader; } return super.getHeader(name); } @Override public Enumeration<String> getHeaders(String name) { List<String> headers = new ArrayList<>(); String customHeader = customHeaders.get(name); if (customHeader != null) { headers.add(customHeader); } // 合并原始请求的同名称头信息 Enumeration<String> originalHeaders = super.getHeaders(name); while (originalHeaders.hasMoreElements()) { headers.add(originalHeaders.nextElement()); } return Collections.enumeration(headers); } @Override public Enumeration<String> getHeaderNames() { Set<String> headerNames = new HashSet<>(customHeaders.keySet()); // 合并原始请求的所有头名称 Enumeration<String> originalHeaderNames = super.getHeaderNames(); while (originalHeaderNames.hasMoreElements()) { headerNames.add(originalHeaderNames.nextElement()); } return Collections.enumeration(headerNames); } }
步骤2:在CorsFilter中使用包装类修改请求
修改你的CorsFilter的doFilter方法,用自定义包装类包裹原始请求,添加自定义头后再传给过滤器链:
@Component @Order(Ordered.HIGHEST_PRECEDENCE) public class CorsFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { System.out.println("--------------------------------------"); HttpServletRequest request = (HttpServletRequest) req; // 用自定义包装类包裹原始请求 CustomHeaderRequestWrapper wrappedRequest = new CustomHeaderRequestWrapper(request); // 添加自定义请求头 wrappedRequest.addCustomHeader("X-Custom-Header", "your-custom-value"); final HttpServletResponse response = (HttpServletResponse) res; response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE"); // 别忘了把自定义头加入跨域允许列表 response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, X-Custom-Header"); response.setHeader("Access-Control-Max-Age", "3600"); if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); } else { // 传递包装后的请求到后续过滤器/处理器 chain.doFilter(wrappedRequest, res); } } }
补充说明
- 你之前用
req.setAttribute("test", "test")是往请求的属性中存储数据,这和请求头是两个概念:属性是服务器内部传递数据的载体,而请求头是HTTP协议层面的字段,前端或后续处理器需要通过getHeader方法获取。 - 跨域场景下必须把自定义请求头加入
Access-Control-Allow-Headers,否则浏览器会拦截这个头信息,导致请求失败。
内容的提问来源于stack exchange,提问作者Jan Testowy
相关产品推荐
相关产品推荐

