spring 通过筛选器向请求添加自定义标题

lmvvr0a8  于 2023-02-03  发布在  Spring
关注(0)|答案(8)|浏览(131)

我有一个扩展Filter类的类,它看起来像:

@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;

        req.setAttribute("test", "test");

        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");
        response.setHeader("Access-Control-Max-Age", "3600");
        if ("OPTIONS".equalsIgnoreCase(((HttpServletRequest) req).getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
        } else {
            chain.doFilter(req, res);
        }
    }

问题是我可以添加一个自定义的headerrequest吗?我尝试了requestreq中可用的所有方法,但没有找到任何方法。但是在debug模式下,我发现CoyoteRequest中有headers的列表。我如何添加我的自定义header

dluptydi

dluptydi1#

不能在HttpServletRequest对象中设置头,但可以使用 Package 类。
看看这个指南:http://wilddiary.com/adding-custom-headers-java-httpservletrequest/
以防将来链接失效:

final class MutableHttpServletRequest extends HttpServletRequestWrapper {
    // holds custom header and value mapping
    private final Map<String, String> customHeaders;

    public MutableHttpServletRequest(HttpServletRequest request){
        super(request);
        this.customHeaders = new HashMap<String, String>();
    }

    public void putHeader(String name, String value){
        this.customHeaders.put(name, value);
    }

    public String getHeader(String name) {
        // check the custom headers first
        String headerValue = customHeaders.get(name);

        if (headerValue != null){
            return headerValue;
        }
        // else return from into the original wrapped object
        return ((HttpServletRequest) getRequest()).getHeader(name);
    }

    public Enumeration<String> getHeaderNames() {
        // create a set of the custom header names
        Set<String> set = new HashSet<String>(customHeaders.keySet());

        // now add the headers from the wrapped request object
        @SuppressWarnings("unchecked")
        Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaderNames();
        while (e.hasMoreElements()) {
            // add the names of the request headers into the list
            String n = e.nextElement();
            set.add(n);
        }

        // create an enumeration from the set and return
        return Collections.enumeration(set);
    }
}

用法:

@Override
public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain) throws IOException, ServletException {
    HttpServletRequest req = (HttpServletRequest) request;
    MutableHttpServletRequest mutableRequest = new MutableHttpServletRequest(req);
    ...
    mutableRequest.putHeader("x-custom-header", "custom value");
    chain.doFilter(mutableRequest, response);
}
krcsximq

krcsximq2#

Guy Smorodinsky的答案是可以的,但是对于Spring来说,您可能需要覆盖一些额外的方法,比如Enumeration<String> getHeaders(String name),因为Spring在使用@RequestHeader注解获取头值时使用了这个方法。
示例代码可能如下所示:

@Override
  public Enumeration<String> getHeaders(String name) {
    Set<String> headerValues = new HashSet<>();
    headerValues.add(this.headers.get(name));

    Enumeration<String> underlyingHeaderValues = ((HttpServletRequest) getRequest()).getHeaders(name);
    while (underlyingHeaderValues.hasMoreElements()) {
      headerValues.add(underlyingHeaderValues.nextElement());
    }

    return Collections.enumeration(headerValues);
  }
b5buobof

b5buobof3#

我遵循了Guy Smorodinsky的答案,它对我很有效,但我不得不向MutableHttpServletRequest添加另一个方法:

@Override
    public Enumeration<String> getHeaders(String name) {
        Set<String> set = new HashSet<>();
        Optional.ofNullable(customHeaders.get(name)).ifPresent(h -> set.add(h));
        Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaders(name);
        while (e.hasMoreElements()) {
            // add the names of the request headers into the list
            String n = e.nextElement();
            set.add(n);
        }
        Optional.ofNullable(customHeaders.get(name)).ifPresent(h -> set.add(h));
        return Collections.enumeration(set);
    }
92vpleto

92vpleto4#

如果出于某种原因,添加新的过滤器或拦截器不是一个选项,反射和丑陋的代码是可以的。您可以执行以下操作,以编程方式修改控制器方法主体中的现有头部:
警告!这是丑陋的,绝对不应该在生产中使用!

private void modifyHeaders(HttpServletRequest request, String headerToFind, String valueToReplace) {
    Field field = request.getHeaderNames().getClass().getDeclaredField("val$names");
    field.setAccessible(true);
    Field field2 = field.get(request.getHeaderNames()).getClass().getDeclaredField("headers");
    field2.setAccessible(true);
    Field field3 = field2.get(field.get(request.getHeaderNames())).getClass().getDeclaredField("headers");
    field3.setAccessible(true);

    Object objects = field3.get(field2.get(field.get(request.getHeaderNames())));
    
    List array = new ArrayList();

    int length = Array.getLength(objects);
    for (int i = 0; i < length; i++) {
        array.add(Array.get(objects, i));
    }

    for (Object obj : array.toArray()) {
        field = obj.getClass().getDeclaredField("nameB");
        field.setAccessible(true);

        Object headerKey = field.get(obj);

        if (headerKey.toString().equalsIgnoreCase(headerToFind)) {
            field = obj.getClass().getDeclaredField("valueB");
            field.setAccessible(true);

            MessageBytes messageBytes = MessageBytes.newInstance();
            messageBytes.setString(valueToReplace));

            field.set(obj, messageBytes);

            break;
        }
    }
}
cgyqldqp

cgyqldqp5#

Http头不区分大小写,按照GuySmorodinsky的回答,考虑使用Spring中的LinkedCaseInsensitiveMap而不是HashMap

this.customHeaders = new LinkedCaseInsensitiveMap<>();
roqulrg3

roqulrg36#

示例如何通过Spring中的过滤器将用户名从OAuth2 Authentication的值设置为自定义头X-Login,并在控制器中用作参数@RequestHeader(X_LOGIN) String login之一

import static java.util.Collections.enumeration;
import static java.util.Collections.singleton;

@Component
public class HeaderLoginFilter extends GenericFilterBean {

    public static final String X_LOGIN = "X-Login";

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        OAuth2Authentication auth = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication();
        if (auth.getUserAuthentication() == null) {
            //ignore, header value is set in my case 
            chain.doFilter(request, response);
            return;
        }

        //filling custom header with value from auth
        HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper((HttpServletRequest) request) {
            @Override
            public Enumeration<String> getHeaders(String name) {
                if (X_CLIENT_LOGIN.equals(name))
                    return enumeration(singleton(auth
                            .getUserAuthentication()
                            .getName()));
                return super.getHeaders(name);
            }
        };
        chain.doFilter(wrapper, response);
    }

}
s1ag04yj

s1ag04yj7#

我在实现REST Client with Quarkus时遇到过类似的问题,使用ClientRequestFilter解决了这个问题

@Provider
public class AddAuthHeadersRequestFilter implements ClientRequestFilter {
    private static final Logger LOG = Logger.getLogger(AddAuthHeadersRequestFilter.class);

    @Override
    public void filter(ClientRequestContext context) throws IOException {
        context.getHeaders().add("Authorization", "Bearer XXXXXXXXXXX");
    }
}
vmdwslir

vmdwslir8#

我知道这是一个很老的主题,但是我需要一个这样的类。从这个讨论中吸取一些好的部分,这里有一个非常全面的类,当每个人想要定制传入的HttpServletRequest的头时,它应该有每个人所需要的东西:

import org.springframework.util.LinkedCaseInsensitiveMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

public class MutableHttpServletRequest extends HttpServletRequestWrapper {

    private final Map<String, String> customHeaders;

    public MutableHttpServletRequest(HttpServletRequest request) {
        super(request);
        this.customHeaders = new LinkedCaseInsensitiveMap<>();
    }

    public void putHeader(String name, String value) {
        this.customHeaders.put(name, value);
    }

    private HttpServletRequest getServletRequest() {
        return (HttpServletRequest) getRequest();
    }

    @Override
    public String getHeader(String name) {
        return Optional.ofNullable(customHeaders.get(name))
                .orElseGet(() -> getServletRequest().getHeader(name));
    }

    @Override
    public Enumeration<String> getHeaders(String name) {
        return Optional.ofNullable(customHeaders.get(name))
                .map(v -> Collections.enumeration(List.of(v)))
                .orElseGet(() -> getServletRequest().getHeaders(name));
    }

    @Override
    public Enumeration<String> getHeaderNames() {
        return Collections.enumeration(
                Stream.concat(
                        customHeaders.keySet().stream(),
                        StreamSupport.stream(
                                Spliterators.spliteratorUnknownSize(
                                        getServletRequest()
                                                .getHeaderNames()
                                                .asIterator(),
                                        Spliterator.ORDERED), false))
                        .collect(Collectors.toSet()));
    }
}

相关问题