如果授权 header 不存在,则无法在 Webfilter 中发送自定义 body

Unable to send custom body in Webfilter if Authorization header doesn't exist

我正在尝试使用 Webfilter 拦截我的 Spring Boot Webflux 应用程序(Spring boot 2.0.0.M7)中的所有请求并检查是否存在 "Authorization" header。如果不存在,我想停止请求处理并发送自定义 HttpStatus 和自定义 body。自定义 HttpStatus 正在运行,但我无法将自定义消息写入 HTTP body。以下

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 class 只是我的习惯 object 我想包含在回复中。

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





}

我卷曲端点,这个 Webfilter 确实有效,它发送正确的 HttpStatus 代码 400,但没有 ApiError。

请求(无授权header):

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

回复:

HTTP/1.1 400 Bad Request
content-length: 0

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

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

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

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

+1 @bsamartins 所说的。

现在介绍您的特定解决方案:writeWith 方法 returns a 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))));

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