15

I have a class which extend Filter class and it looks like:

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

The question is can I add a custom header to request? I went through all method which are available inside request and req and couldn't find any way. But in debug mode I found that inside CoyoteRequest is list of headers. How can I add there my custom header?

enter image description here

9 Answers 9

30

You can not set the headers in HttpServletRequestobject, but you can use a wrapper class.

Look at this guide: http://wilddiary.com/adding-custom-headers-java-httpservletrequest/


Just in case for future the link becomes invalid:

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);
    }
}

usage:

@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);
}
Sign up to request clarification or add additional context in comments.

1 Comment

This works for me, but I needed to add public Enumeration<String> getHeaders(String name) to the MutableHttpServletRequest. I'll put the code in a new answer.
9

Guy Smorodinsky's answer is ok but for example for Spring you might want to override some additional methods, like Enumeration<String> getHeaders(String name), as Spring uses this while getting header value with @RequestHeader annotation.

Example code may look something like this:

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

2 Comments

Careful, the above code snipped will introduce a null item at index 0 in the headers collection if your custom headers don't include the specified header name.
Make sure to check for null before adding to the headerValues set, and you're fine.
4

I followed Guy Smorodinsky's answer and it worked for me but I had to add another method to the 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);
    }

Comments

1

Example how you can set username from OAuth2Authentication's value to custom header X-Login via filter in spring and use in controller as one of arguments @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);
    }

}

Comments

1

If for any reason adding new filter or interceptor is not an option, and reflection and ugly code is OK. You can do the following to modify existing header programmatically in the controller method's body:

Warning! This is ugly and definitely shouldn't be used in production!

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

Comments

1

Http headers are case insensitive, followed Guy Smorodinsky's answer, consider using LinkedCaseInsensitiveMap from Spring instead of HashMap

this.customHeaders = new LinkedCaseInsensitiveMap<>();

Comments

0

I had similar issue implementing a REST Client with Quarkus, it was solved with 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");
    }
}

Comments

0

I know that this is a pretty old thread, but I needed a class like this. Taking the good parts from this discussion, here's a pretty comprehensive class that should have what everyone needs when they want to customize the headers of an incoming 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()));
    }
}

Comments

0

Guy Smorodinsky answer is great and also Roger Parkinson completement but I've had also to override getRequest() method returning "this" and to modify all getRequest() calls with super.getRequest() ex:

Enumeration<String> e = ((HttpServletRequest) super.getRequest()).getHeaderNames();

getRequest() override:

  public ServletRequest getRequest() {
    return this;
  }

This because the ServletRequest results rewrapped by another HttpServletRequestWrapper on cxf.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.