我需要从我的定制实现ReactiveAuthorizationManager<AuthorizationContext>ServerHttpRequest中提取请求正文.主体有效载荷包含实体的id.我需要判断请求的授权,以查看请求者是否可以访问该实体.

为了实现它,我创建了一个定制的ServerHttpRequestDecorator:

public class BodyInterceptingRequest extends ServerHttpRequestDecorator {

    private StringBuilder body = null;

    public BodyInterceptingRequest(ServerHttpRequest delegate) {
        super(delegate);
    }

    public Flux<DataBuffer> getBody() {
        if (body == null) {
            return super.getBody();
        }

        DataBufferFactory bufferFactory = new DefaultDataBufferFactory();
        byte[] bytes = getRequestBody().getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = bufferFactory.allocateBuffer(bytes.length);
        return Flux.just(buffer.write(bytes));
    }

    public Mono<String> readBody() {
        // @formatter:off
        return DataBufferUtils.join(super.getBody())
            .map((DataBuffer dataBuffer) -> {
                byte[] bytes = new byte[dataBuffer.readableByteCount()];
                dataBuffer.read(bytes);
                DataBufferUtils.release(dataBuffer);
                return bytes;
            })
            .defaultIfEmpty(new byte[0])
            .flatMap(bytes -> {
                body = new StringBuilder();
                body.append(new String(bytes, StandardCharsets.UTF_8));
                return Mono.just(body.toString());
            });
        // @formatter:on
    }

    public String getRequestBody() {
        return this.body.toString();
    }
}

并在定制的ServerWebExchangeDecorator:

public class RequestBodyInterceptingExchange extends ServerWebExchangeDecorator {

    private final BodyInterceptingRequest request;

    public RequestBodyInterceptingExchange(ServerWebExchange exchange) {
        super(exchange);
        this.request = new BodyInterceptingRequest(exchange.getRequest());
    }

    @Override
    public BodyInterceptingRequest getRequest() {
        return request;
    }
}

我还创建了一个定制的WebFilter:

public class RequestBodyInterceptingFilter implements WebFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        return chain.filter(new RequestBodyInterceptingExchange(exchange));
    }
}

并在ServerHttpSecurity年注册为:

http
    // other stuff
    .addFilterBefore(new RequestBodyInterceptingFilter(), SecurityWebFiltersOrder.SECURITY_CONTEXT_SERVER_WEB_EXCHANGE)
    .build();

现在,上面提到的ReactiveAuthorizationManager<AuthorizationContext>的自定义实现如下所示:

public class MyAuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {

    public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext context) {
        // @formatter:off
        return Mono.zip(
                authentication,
                getId(context)
            )
            .flatMap((Tuple2<Authentication, String> tuple) -> {
                Authentication auth = tuple.getT1();
                String id = tuple.getT2();
                Jwt principal = (Jwt) auth.getPrincipal();
                String userId = principal.getSubject();

                return // execute some logic and return a Mono boolean
            })
            .filter(BooleanUtils::isTrue)
            .map(AuthorizationDecision::new);
        // @formatter:on
    }

    protected Mono<String> getId(AuthorizationContext context) {
        SecurityContextServerWebExchange securityContextExchange = (SecurityContextServerWebExchange) context.getExchange();
        RequestBodyInterceptingExchange exchange = (RequestBodyInterceptingExchange) securityContextExchange.getDelegate();

        // @formatter:off
        return exchange.getRequest()
            .readBody()
            .flatMap((String requestBody) -> Mono.just(convert(requestBody).getId())); // convert is a method for deserialize JSON to an Object
        // @formatter:on
    }
}

我可以实现我所需要的.但我有几个问题:

  • 这是我想做的事情的正确方式吗?
  • 有没有可能BodyInterceptingRequest类是错误的,在并发命中的情况下,它提供了错误的信息?我的意思是,如果有两个调用方,Caller1和Caller2,并且他们分别使用请求1和请求2调用API,那么将为Caller1处理请求2,并为Caller2处理请求1.
  • 在实现BodyInterceptingRequest的过程中是否存在内存泄漏的可能性?

我很感谢你对我的方法提出的专家意见.

推荐答案

这是我想做的事情的正确方式吗?

实际上,这在react 式应用程序中并不是最好的主意.您的情况有点棘手,因为ReactiveAuthorizationManager通常用于基于用户角色、声明等数据执行授权,而不是基于请求正文.这里的棘手之处在于,读取请求正文以获取授权意味着您正在使用Flux<DataBuffer>.由于该通量只能使用一次,因此您需要对其进行缓存并将缓存转发给控制器.这正是您在示例中已经做过的事情.

这里的问题是,这样的缓存可能会抵消WebFlux的一些好处.例如,在这种情况下,它使缓存涉及到将整个请求正文存储在内存中,以便可以多次读取.然而,使用像WebFlux这样的基于流的非阻塞I/O模型的好处之一是能够通过在数据到达时按块处理数据(这就是为什么您有Flux<DataBuffer>个而不是Mono<DataBuffer>个).缓存它可能会影响应用程序的性能,特别是在请求正文较大的情况下.此外,如果请求正文是数据流,则不能 Select 缓存.流可能不会一次全部可用,因此无法对其进行缓存.

这就是为什么不建议你做你想做的事情.最好将所有需要的授权上下文移动到Header和Check Header.

有没有可能BodyInterceptingRequest类有错误,在并发命中的情况下,它会提供错误信息?我的意思是,如果有两个调用方,Caller1和Caller2,并且他们分别使用请求1和请求2调用API,那么将为Caller1处理请求2,并为Caller2处理请求1.

但是,如果您了解所有的缺点,但又想继续这样的解决方案,那么总体实现是正确的.您不太可能遇到您所描述的情况,因为您 for each 请求创建了一个BodyInterceptingRequest的新实例,并且您对每个请求都有自己的"交换".此外,WebFilter是按顺序执行的,因此没有争用条件应该在BodyInterceptingRequest内.

在实现BodyInterceptingRequest时是否存在内存泄漏的可能性?

总体而言,我没有看到可能的内存泄漏.让我们仔细看看重要的地方:

  1. ReadBody方法

根据这DataBufferUtils.join份文件:

the given data buffers do not have to be released. They will be released as part of the returned composite.

这就是你在DataBufferUtils.release(dataBuffer)年里所做的.

  1. GetBody方法

由于您使用的是DefaultDataBufferFactory,因此不需要释放已分配的数据缓冲区.他们将被垃圾收集.但实际上,我看不出有什么理由在这里分配新内存.我想你可以用bufferFactory.wrap(bytes)代替bufferFactory.allocateBuffer.

Java相关问答推荐

Spring bootstrap @ Asmat注释与@ Routed

try Dockerize Maven应用程序,但发布版本21不支持"

如何转换Tue Feb 27 2024 16:35:30 GMT +0800 String至ZonedDateTime类型""

Quarkus keycloat配置不工作.quarkus. keycloak. policy—enforcer. enable = true在. yaml表示中不工作

滚动视图&不能在alert 对话框中工作(&Q;&Q;)

使用意向过滤器从另一个应用程序启动服务

这是什么Java构造`(InputStream Is)->;()->;{}`

对于亚洲/香港,使用ResolverStyle.STRICT的LocalDate.parse返回意外结果

如何在Java记录中设置BigDecimal类型属性的精度?

为什么Spring Boot项目无法为基于MySQL的CRUD应用程序找到从JPARepository接口扩展的ProductRepository?

对字符串长度进行排序,但颠倒了顺序(最长字符串在前)

如果按钮符合某些期望,如何修改它的文本?

如何生成指定范围内的11位序列号?

如果第一位数字和最后一位数字相差超过一位,您将如何获得随机数?

JFree Chart从图表中删除边框

如何在不作为类出现的表上执行原生查询?

Java 21中泛型的不兼容更改

如何在运行docker的应用程序中获取指定的配置文件

如何在Maven Central上部署?

java 11上出现DateTimeParseException,但java 8上没有