如何使用 Spring WebFlux WebFilter 结束请求并发送正确的响应?

Posted

技术标签:

【中文标题】如何使用 Spring WebFlux WebFilter 结束请求并发送正确的响应?【英文标题】:How to end request and send proper response using Spring WebFlux WebFilter? 【发布时间】:2018-07-05 17:54:34 【问题描述】:

我在标头中使用 JWT 来验证用户请求。

@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) 
    String token = exchange.getRequest().getHeaders().getFirst("token");
    // Verify a Token
    try 
        Algorithm algorithm = Algorithm.HMAC256("secret");
        JWTVerifier verifier = JWT.require(algorithm)
                .withIssuer("auth0")
                .build(); //Reusable verifier instance
        DecodedJWT jwt = verifier.verify(token);
     catch (UnsupportedEncodingException exception) 
        // send internal server error in response
     catch (JWTVerificationException exception) 
        // send invalid token
    
    return chain.filter(exchange);

当我使用时

return Mono.empty();

它结束请求,但是如何设置正确的响应?例如响应为“无效令牌”或“内部服务器错误”。

【问题讨论】:

【参考方案1】:

在 catch 块中,您可以执行以下操作或重新抛出异常,这样您就可以通过实现“org.springframework.web.server.WebExceptionHandler”来获得一些 GlobalExceptionHandler,并且您也可以拥有这个逻辑。这里的关键是使用 DataBufferFactory,然后使用 Jackson 的 ObjectMapper 将您的自定义错误对象序列化为字符串,然后写入响应并使用 exchange.getResponse().setStatusCode 设置 HTTP 状态

import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

@Autowired
    ObjectMapper objMapper;

        ApiError apiError = new ApiError(HttpStatus.UNAUTHORIZED);
        apiError.setTimestamp(LocalDateTime.now());
        apiError.setMessage("Invalid token" + exception.getMessage() + ":"+exception.getLocalizedMessage());
        DataBuffer buf = null;
        try 
            buf = dataBufferFactory.wrap(objMapper.writeValueAsBytes(apiError));
         catch (JsonProcessingException e) 
            LOG.debug("Exception during processing JSON", e);
            apiError.setMessage(e.getMessage());
        
        if(buf == null) buf = dataBufferFactory.wrap("".getBytes());
        exchange.getResponse().setStatusCode(apiError.getStatus());
        return exchange.getResponse().writeWith(Flux.just(buf));

ApiError 是一个自定义类,它具有 HTTP 状态和时间戳以及类似下面的内容或您的自定义数据结构。

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;

import org.springframework.http.HttpStatus;

import com.fasterxml.jackson.annotation.JsonFormat;

import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class ApiError  implements Serializable

       private HttpStatus status;
       @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "MM/dd/yyyy hh:mm:ss a")
       private LocalDateTime timestamp;
       private String message;
       private String debugMessage;


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






【讨论】:

【参考方案2】:

也许这会有所帮助,这适用于 x509 身份验证,但适用于 JWT。

查看Authentication by certificate for WebFlux?

重点是:

    使用身份验证转换器提取凭据(身份验证过滤器将负责调用 ReactiveAuthenticationManager 以对提取的凭据进行身份验证) 如果需要,使用 AuthenticationEntryEndpoint 自定义在身份验证失败时返回给客户端的响应

希望对你有帮助

【讨论】:

【参考方案3】:

您可以修改响应的标题:

@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) 
    String token = exchange.getRequest().getHeaders().getFirst("token");
    // Verify a Token
    try 
        Algorithm algorithm = Algorithm.HMAC256("secret");
        JWTVerifier verifier = JWT.require(algorithm)
                .withIssuer("auth0")
                .build(); //Reusable verifier instance
        DecodedJWT jwt = verifier.verify(token);
     catch (UnsupportedEncodingException exception) 
        // send internal server error in response
        exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
        exchange.getResponse().getHeaders().add("X-Message", "Unsupported Encoding");
        return Mono.empty();
     catch (JWTVerificationException exception) 
        // send invalid token
        exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
        exchange.getResponse().getHeaders().add("X-Message", "Invalid token");
        return Mono.empty();
    
    return chain.filter(exchange);

我找不到写响应正文的方法

【讨论】:

以上是关于如何使用 Spring WebFlux WebFilter 结束请求并发送正确的响应?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 WebFlux 在 Spring Boot 2 中设置登录页面?

如何在 Spring Boot WebFlux 中使用 GET 请求注销

spring5 webflux,如何返回自定义json数据?

如何使用 Spring WebFlux 获取“身份验证”对象?

如何使用WebFlux在Spring Boot 2中设置登录页面?

如何在 Spring webflux 应用程序中使用 Spring WebSessionIdResolver 和 Spring Security 5?