webflux、安全和请求体

g2ieeal7  于 2021-07-15  发布在  Java
关注(0)|答案(1)|浏览(513)

我需要使用请求主体的hmac保护用spring boot、webflux和spring security实现的restapi。简单一点,在一个高级请求中,请求的头包含请求主体的散列值,所以我必须读取头,读取主体,计算主体的散列值,并与头的值进行比较。
我想我应该实施 ServerAuthenticationConverter 但是到目前为止,我所能找到的所有示例都只是查看请求头,而不是正文,我不确定是否可以读取正文,或者是否应该使用缓存正文 Package /修改请求,以便底层组件第二次使用它?
可以使用以下内容:

public class HttpHmacAuthenticationConverter implements ServerAuthenticationConverter {

    @Override
    public Mono<Authentication> convert(ServerWebExchange exchange) {
        exchange.getRequest().getBody()
                .next()
                .flatMap(dataBuffer -> {
                    try {
                        return Mono.just(StreamUtils.copyToString(dataBuffer.asInputStream(), StandardCharsets.UTF_8));
                    } catch (IOException e) {
                        return Mono.error(e); 
                    }
                })
                ...

我收到了ide的警告 copyToString 行:不适当的阻塞方法调用
有什么指南或例子吗?
谢谢!
我也尝试过:

@Override
    public Mono<Authentication> convert(ServerWebExchange exchange) {
        return Mono.justOrEmpty(exchange.getRequest().getHeaders().toSingleValueMap())
                .zipWith(exchange.getRequest().getBody().next()
                        .flatMap(dataBuffer -> Mono.just(dataBuffer.asByteBuffer().array()))
                )
                .flatMap(tuple -> create(tuple.getT1(), tuple.getT2()));

但是这不起作用-最后一行的create()方法中的代码永远不会执行。

ftf50wuq

ftf50wuq1#

我让它工作。张贴我的代码作为参考。
要使其正常工作,需要两个组件:webfilter,它将读取和缓存请求主体,以便多次使用它;serverauthenticationconverter,它将计算主体上的哈希值并验证签名。

public class HttpRequestBodyCachingFilter implements WebFilter {
private static final byte[] EMPTY_BODY = new byte[0];

@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
    // GET and DELETE don't have a body
    HttpMethod method = exchange.getRequest().getMethod();
    if (method == null || method.matches(HttpMethod.GET.name()) || method.matches(HttpMethod.DELETE.name())) {
        return chain.filter(exchange);
    }

    return DataBufferUtils.join(exchange.getRequest().getBody())
            .map(dataBuffer -> {
                byte[] bytes = new byte[dataBuffer.readableByteCount()];
                dataBuffer.read(bytes);
                DataBufferUtils.release(dataBuffer);
                return bytes;
            })
            .defaultIfEmpty(EMPTY_BODY)
            .flatMap(bytes -> {
                ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
                    @Nonnull
                    @Override
                    public Flux<DataBuffer> getBody() {
                        if (bytes.length > 0) {
                            DataBufferFactory dataBufferFactory = exchange.getResponse().bufferFactory();
                            return Flux.just(dataBufferFactory.wrap(bytes));
                        }
                        return Flux.empty();
                    }
                };
                return chain.filter(exchange.mutate().request(decorator).build());
            });
}

}

public class HttpJwsAuthenticationConverter implements ServerAuthenticationConverter {
private static final byte[] EMPTY_BODY = new byte[0];

@Override
public Mono<Authentication> convert(ServerWebExchange exchange) {
    return DataBufferUtils.join(exchange.getRequest().getBody())
            .map(dataBuffer -> {
                byte[] bytes = new byte[dataBuffer.readableByteCount()];
                dataBuffer.read(bytes);
                DataBufferUtils.release(dataBuffer);
                return bytes;
            })
            .defaultIfEmpty(EMPTY_BODY)
            .flatMap(body -> create(
                    exchange.getRequest().getMethod(),
                    getFullRequestPath(exchange.getRequest()),
                    exchange.getRequest().getHeaders(),
                    body)
            );
}

...
这个 create 方法实现了基于请求方法、路径、头和主体的签名验证逻辑。它返回 Authentication 如果成功或 Mono.empty() 如果没有。
接线方式如下:

public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http.authorizeExchange().pathMatchers(PATH_API).authenticated()
      ...
      .and()
      .addFilterBefore(new HttpRequestBodyCachingFilter(), SecurityWebFiltersOrder.AUTHENTICATION)
      .addFilterAt(jwtAuthenticationFilter(...), SecurityWebFiltersOrder.AUTHENTICATION);
}

private AuthenticationWebFilter jwtAuthenticationFilter(ReactiveAuthenticationManager authManager) {
    AuthenticationWebFilter authFilter = new AuthenticationWebFilter(authManager);
    authFilter.setServerAuthenticationConverter(new HttpJwsAuthenticationConverter());
    authFilter.setRequiresAuthenticationMatcher(ServerWebExchangeMatchers.pathMatchers(PATH_API));
    return authFilter;
}

@Bean
public ReactiveAuthenticationManager reactiveAuthenticationManager() {
    return Mono::just;
}

}

相关问题