如果授权标头不存在,则无法在 Webfilter 中发送自定义正文

Posted

技术标签:

【中文标题】如果授权标头不存在,则无法在 Webfilter 中发送自定义正文【英文标题】:Unable to send custom body in Webfilter if Authorization header doesn't exist 【发布时间】:2018-07-05 09:22:37 【问题描述】:

我正在尝试使用 Webfilter 拦截我的 Spring Boot Webflux 应用程序(Spring boot 2.0.0.M7)中的所有请求,并检查是否存在“授权”标头。如果不存在,我想停止请求处理并发送自定义 HttpStatus 和自定义正文。自定义 HttpStatus 正在工作,但我无法将自定义消息写入 HTTP 正文。下面

import java.time.LocalDateTime;

import org.apache.commons.lang.SerializationUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
import reactor.util.context.Context;

public class RequestContextFilter implements WebFilter

    Logger LOG = LoggerFactory.getLogger(RequestContextFilter.class);

    @Autowired
    private WebClient.Builder webclientBuilder;


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) 
        LOG.debug("Inside RequestContextFilter.."+ exchange);
        String authorizationHeader = exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        if(authorizationHeader == null) 
            exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
            ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST);
            apiError.setMessage("Missing Authorization Header");
            apiError.setTimestamp(LocalDateTime.now());
   // The below code of writing to body is NOT WORKING
            exchange.getResponse().writeWith(Mono.just(new DefaultDataBufferFactory().wrap(SerializationUtils.serialize(apiError))));
            return Mono.empty();
        
        return chain.filter(exchange);

    



ApiError.java 类不过是我想要包含在响应中的自定义对象。

public class ApiError  implements Serializable

       private HttpStatus status;
       @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss")
       private LocalDateTime timestamp;
       private String message;
       private String debugMessage;
       private List<ApiSubError> subErrors;


       public ApiError() 
           timestamp = LocalDateTime.now();
       

       public ApiError(HttpStatus status) 
           this();
           this.status = status;
       

       public ApiError(HttpStatus status, Throwable ex) 
           this();
           this.status = status;
           this.message = "Unexpected error";
           this.debugMessage = ex.getLocalizedMessage();
       

       public ApiError(HttpStatus status, String message, Throwable ex) 
           this();
           this.status = status;
           this.message = message;
           this.debugMessage = ex.getLocalizedMessage();
       






我 curl 端点,这个 Webfilter 确实工作,它发送正确的 HttpStatus 代码 400,但没有 ApiError。

请求(无授权标头):

curl -X GET "http://localhost:8080/accounts"--verbose

回复:

HTTP/1.1 400 Bad Request
content-length: 0

状态有效并且正在调用过滤器但没有对象响应。我确实尝试使用将原始字节转换为 JSON 后将字节写入 DataBufferFactory,但它不起作用。

【问题讨论】:

【参考方案1】:

+1 @bsamartins 所说的。

现在关于您的特定解决方案:writeWith 方法返回 Publisher。如果没有人订阅它,那么什么也不会发生。你应该替换

exchange.getResponse().writeWith(Mono.just(new DefaultDataBufferFactory().wrap(SerializationUtils.serialize(apiError))));
return Mono.empty();

return exchange.getResponse()
               .writeWith(Mono.just(new DefaultDataBufferFactory().wrap(SerializationUtils.serialize(apiError))));

通过该更改,Spring WebFlux 将订阅返回的 Publisher,并且您的代码将写入响应。

【讨论】:

感谢您的精彩回答! 实际上它返回的响应正文,但不是 JSON,而是一些带有 ?? 的奇怪字符串标记在里面。你能帮我看看可能是什么原因造成的吗?我的类实现了 Serializable 并被序列化为字节数组。 这可能值得一个单独的问题,提供更多细节。 嗨@Brian,很抱歉打扰您,但我刚刚发布了一个关于此的问题:***.com/questions/49384626/…【参考方案2】:

您可以使用 Spring AuthenticationWebFilter 而不是创建一个新的。 看看这个question如何使用它。

设置您自己的authenticationConverter 以从标头中提取凭据,然后您可以实现自己的AuthenticationEntryPoint 并将其设置在过滤器上以向客户端发送自定义响应。

您可以查看 http 基本身份验证的默认实现,了解如何实现。

【讨论】:

以上是关于如果授权标头不存在,则无法在 Webfilter 中发送自定义正文的主要内容,如果未能解决你的问题,请参考以下文章

如果 VTL / API 网关模板中不存在密钥,则返回 null

AWS API Gateway:如果响应未授权,则添加标头

AlamofireImage addAuthentication 不添加授权标头

页面刷新时不存在授权标头

如果标头中不存在 JWT 令牌,如何执行一些逻辑?

当请求中存在授权标头时,它总是一个缓存未命中