【问题标题】:Adding custom header to Request via Filter通过过滤器将自定义标头添加到请求
【发布时间】:2018-06-22 17:51:32
【问题描述】:

我有一个扩展 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);
        }
    }

问题是我可以将自定义header 添加到request 吗?我浏览了requestreq 中可用的所有方法,但找不到任何方法。但是在debug 模式下,我发现CoyoteRequest 里面是headers 的列表。如何在其中添加我的自定义 header

【问题讨论】:

    标签: spring servlets filter header request


    【解决方案1】:

    您不能在HttpServletRequestobject 中设置标题,但可以使用包装类。

    看看这个指南: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);
    }
    

    【讨论】:

    • 这对我有用,但我需要将 public Enumeration getHeaders(String name) 添加到 MutableHttpServletRequest。我会把代码放在一个新的答案中。
    【解决方案2】:

    Guy Smorodinsky 的回答是可以的,但例如对于 Spring,您可能想要覆盖一些额外的方法,例如 Enumeration&lt;String&gt; 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);
      }
    

    【讨论】:

    • 小心,如果您的自定义标头不包含指定标头name,上述代码片段将在标头集合中的索引0处引入null项。
    • 确保在添加到headerValues 集之前检查null,这样就可以了。
    【解决方案3】:

    示例如何通过 spring 中的过滤器将用户名从 OAuth2Authentication 的值设置为自定义标头 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);
        }
    
    }
    

    【讨论】:

      【解决方案4】:

      我在实现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");
          }
      }
      

      【讨论】:

        【解决方案5】:

        我遵循 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);
            }
        

        【讨论】:

          【解决方案6】:

          如果出于任何原因添加新的过滤器或拦截器不是一个选项,反射和丑陋的代码是可以的。您可以执行以下操作以在控制器方法的主体中以编程方式修改现有标头:

          警告!这很丑陋,绝对不应该在生产中使用!

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

          【讨论】:

            【解决方案7】:

            Http 标头不区分大小写,遵循 Guy Smorodinsky 的回答,考虑使用 Spring 中的 LinkedCaseInsensitiveMap 而不是 HashMap

            this.customHeaders = new LinkedCaseInsensitiveMap<>();
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2016-11-14
              • 1970-01-01
              • 2017-12-14
              • 1970-01-01
              • 1970-01-01
              • 2013-11-13
              • 2018-04-12
              • 1970-01-01
              相关资源
              最近更新 更多