max*_*dim 5 spring spring-security spring-webflux
我需要使用请求正文的 HMAC 来保护使用 Spring Boot、WebFlux 和 Spring Security 实现的 REST API。稍微简化一下,在较高的层面上 - 请求附带具有请求正文的哈希值的标头,因此我必须读取标头,读取正文,计算正文的哈希值并与标头值进行比较。
我认为我应该实现,ServerAuthenticationConverter但到目前为止我能找到的所有示例都只查看请求标头,而不是正文,我不确定是否可以只读取正文,或者应该用缓存的正文包装/改变请求那么它可以被底层组件第二次消耗吗?
可以使用以下内容吗?
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);
}
})
...
Run Code Online (Sandbox Code Playgroud)
我收到来自 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()));
Run Code Online (Sandbox Code Playgroud)
但这不起作用 - 最后一行的 create() 方法中的代码永远不会执行。
我让它发挥作用。发布我的代码以供参考。
需要两个组件才能使其工作: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());
});
}
Run Code Online (Sandbox Code Playgroud)
}
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)
);
}
Run Code Online (Sandbox Code Playgroud)
...
Converter 中的方法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;
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2397 次 |
| 最近记录: |