【问题标题】:How to add a custom header in Spring WebFilter?如何在 Spring WebFilter 中添加自定义标头?
【发布时间】:2018-11-28 08:58:04
【问题描述】:

我正在尝试在调用 REST 服务之前添加自定义过滤器。在下面的这个类中,我试图在 HttpRequest 中添加自定义过滤器,但出现错误:-

java.lang.UnsupportedOperationException: null 在 java.util.Collections$UnmodifiableMap.computeIfAbsent(Collections.java:1535) ~[na:1.8.0_171] 在 org.springframework.util.CollectionUtils$MultiValueMapAdapter.add(CollectionUtils.java:459) ~[spring-core-5.0.7.RELEASE.jar:5.0.7.RELEASE]

public class AuthenticationWebFilter implements WebFilter {
    private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationWebFilter.class);

    @Autowired
    private TokenServiceRequest tokenServiceRequest;

    @Autowired
    private AuthenticationProvider authenticationProvider;

    public AuthenticationWebFilter(TokenServiceRequest tokenServiceRequest, AuthenticationProvider authenticationProvider) {
        super();
        this.tokenServiceRequest = tokenServiceRequest;
        this.authenticationProvider = authenticationProvider;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
        HttpHeaders requestHeaders = serverWebExchange.getRequest().getHeaders();
        HttpHeaders responseHeaders = serverWebExchange.getResponse().getHeaders();
        LOGGER.info("Response HEADERS: "+responseHeaders);
        LOGGER.info("Request HEADERS: "+serverWebExchange.getRequest().getHeaders());

        tokenServiceRequest.setUsername(serverWebExchange.getRequest().getHeaders().getFirst(CommerceConnectorConstants.USERNAME));
        tokenServiceRequest.setPassword(serverWebExchange.getRequest().getHeaders().getFirst(CommerceConnectorConstants.PASSWORD));
        tokenServiceRequest.setClientId(serverWebExchange.getRequest().getHeaders().getFirst(CommerceConnectorConstants.CLIENT_ID));
        tokenServiceRequest.setSecretClient(serverWebExchange.getRequest().getHeaders().getFirst(CommerceConnectorConstants.SECRET_CLIENT));
        LOGGER.info("Token Received: " + authenticationProvider.getUserAccessToken(tokenServiceRequest).getTokenId());

        //responseHeaders.set(CommerceConnectorConstants.X_AUTH_TOKEN, authenticationProvider.getUserAccessToken(tokenServiceRequest).getTokenId());
        //responseHeaders.add(CommerceConnectorConstants.X_AUTH_TOKEN, authenticationProvider.getUserAccessToken(tokenServiceRequest).getTokenId());

        //This below code is not working
        serverWebExchange.getRequest().getQueryParams().add("test", "value");

        //This below code is not working
        //serverWebExchange.getRequest().getHeaders().add(CommerceConnectorConstants.X_AUTH_TOKEN, authenticationProvider.getUserAccessToken(tokenServiceRequest).getTokenId());
        LOGGER.info("Exiting filter@AuthenticationWebFilter");
        return webFilterChain.filter(serverWebExchange);
    }
    }

在 HTTPResponse 中,我可以设置自定义标头,但我的要求是在 HTTPRequest 中添加自定义标头。请指教。

【问题讨论】:

    标签: spring httprequest spring-filter


    【解决方案1】:

    我遇到了同样的问题,因为标题已经有相同的键;我的解决方法是在header中设置key,先检查key是否存在;

    @Configuration
    public class AuthGatewayFilter implements GlobalFilter, Ordered {
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            Consumer<HttpHeaders> httpHeaders = httpHeader -> {
                // check exists
                if(StringUtils.isBlank(httpHeader.getFirst("xxx"))){
                    httpHeader.add("xxx", "xxx");
                }
            };
            ServerHttpRequest serverHttpRequest = exchange.getRequest().mutate().headers(httpHeaders).build();
            exchange = exchange.mutate().request(serverHttpRequest).build();
    
            return chain.filter(exchange);
        }
    
    }
    

    【讨论】:

      【解决方案2】:
          public class CustomTokenFilter implements WebFilter {
      
          @Override
          public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
              ServerHttpRequest mutateRequest = serverWebExchange.getRequest().mutate()
                      .header("token", "test")
                      .build();
              ServerWebExchange mutateServerWebExchange = serverWebExchange.mutate().request(mutateRequest).build();
              return webFilterChain.filter(mutateServerWebExchange);
          }
      }
      

      【讨论】:

      • 您能否为您的答案提供一些上下文而不仅仅是代码?
      • Sorry,表示复制原始请求并添加标头“token”。我希望我能帮助你。我的英语很差。
      【解决方案3】:

      如果你在spring cloud gateway,请求头可以通过implements GlobalFilter或GatewayFilter来修改。

          @Component 
          public class LogFilter implements GlobalFilter, Ordered {
      
          private Logger LOG = LoggerFactory.getLogger(LogFilter.class);
      
          @Override
          public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
      
              return chain.filter(
                      exchange.mutate().request(
                              exchange.getRequest().mutate()
                                      .header("customer-header", "customer-header-value")
                                      .build())
                              .build());
          }
      
          @Override
          public int getOrder() {
              return 0;
          } }
      

      如果你在 ZuulFilter 中,addZuulRequestHeader 可以修改请求头。

          RequestContext.getCurrentContext().addZuulRequestHeader("customer-header", "customer-header-value");
      

      希望对你有帮助。

      【讨论】:

      • 通过实现WebFilter接口,你可以做与实现GatewayFilter一样的事情。
      • 如何在spring cloud gateway中改变响应?
      • 哦,太好了,节省了我几个小时来发现你可以改变交换和请求。
      【解决方案4】:

      我认为抛出异常是出于安全原因。如果过滤器可以添加/修改 HTTP 请求标头,那将是令人讨厌的。当然,您可以通过创建一系列装饰器来完成此操作:

      import org.springframework.http.HttpHeaders;
      import org.springframework.http.server.reactive.ServerHttpRequest;
      import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
      import org.springframework.util.MultiValueMap;
      import org.springframework.web.server.ServerWebExchange;
      import org.springframework.web.server.ServerWebExchangeDecorator;
      import org.springframework.web.server.WebFilter;
      import org.springframework.web.server.WebFilterChain;
      import reactor.core.publisher.Mono;
      
          public class CustomFilter implements WebFilter {
      
          public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
      
                  ServerWebExchangeDecorator decorator = new ServerWebExchangeDecoratorImpl(serverWebExchange);
      
                  //do your stuff using decorator 
      
                  return webFilterChain.filter(decorator);
              }
          }
      
      
          class ServerWebExchangeDecoratorImpl extends ServerWebExchangeDecorator {
      
              private ServerHttpRequestDecorator requestDecorator;
      
              public ServerWebExchangeDecoratorImpl(ServerWebExchange delegate) {
                  super(delegate);
                  this.requestDecorator = new ServerHttpRequestDecoratorImpl(delegate.getRequest());
              }
      
              @Override
              public ServerHttpRequest getRequest() {
                  return requestDecorator;
              }
      
          }
      
          class ServerHttpRequestDecoratorImpl extends  ServerHttpRequestDecorator {
      
              // your own query params implementation
              private MultiValueMap queryParams;
      
              public ServerHttpRequestDecoratorImpl(ServerHttpRequest request) {
                  super(request);
                  this.queryParams = new HttpHeaders();
                  this.queryParams.addAll(request.getQueryParams());
              }
      
              @Override
              public MultiValueMap<String, String> getQueryParams() {
                  return queryParams;
              }
      
              //override other methods if you want to modify the behavior
          }
      

      【讨论】:

      • 好的,知道我们无法添加/修改 HTTP 请求,但我们可以在其中添加身份验证 Jwt 令牌吗?
      • 是的。 JWT Token 可以作为标准模板添加:Authorization: Bearer &lt;token&gt;。其中Authorization 是标头名称和Bearer &lt;token&gt; 标头值。
      猜你喜欢
      • 1970-01-01
      • 2014-05-23
      • 1970-01-01
      • 2018-07-25
      • 2019-12-15
      • 2013-05-15
      • 1970-01-01
      • 1970-01-01
      • 2012-01-21
      相关资源
      最近更新 更多