为 spring-cloud-stream 消息消费者设置 permissions/authentication,使其通过 @PreAuthorize 检查

Set permissions/authentication for spring-cloud-stream message consumer so it passes @PreAuthorize checks

我通过 Consumer<MyMessage> 实现使用来自 spring-cloud-stream 的消息。作为消息处理的一部分,我需要访问受 @PreAuthorize 安全检查保护的方法。默认情况下 Consumer 运行 未经身份验证,因此消息处理失败。

消费者:

@Bean
public Consumer<MyMessage> exampleMessageConsumer(MyMessageConsumer consumer) {
    return consumer::handleMessage;
}

安全方法:

@PreAuthorize("hasAuthority('ROLE_ADMIN') or hasAuthority('ROLE_USER')")
public void doSomething() { ... }

我不只是想绕过安全机制,那么验证我的消费者以使其通过检查的最简单方法是什么?

编辑: 我们正在使用 google pubsub 作为活页夹

对于 Kafka 活页夹:

添加一个@EventListener监听ConsumerStartedEvent;然后,您可以通过 SecurityContextHolder; 将身份验证添加到安全上下文中;这将它绑定到线程;同一个线程用于调用侦听器。

我找到了两个可能的解决方案

  1. 使用 springs RunAs 支持 (baeldung) 为特定方法添加安全上下文权限。如果我这样做,我需要将 ROLE_RUN_AS_USER 添加到我的安全方法中。在规模上,这会使注释变得非常复杂。

  2. 在执行处理程序方法之前手动更改安全上下文,然后return将其恢复到原始状态。

我选择了第二个选项。我想要一个透明的解决方案,但似乎没有。

为了完成这项工作,我创建了一个 class,它用不断变化的代码包装了一个功能接口,并 return 实现了它。

public class RunAs {

    @FunctionalInterface
    public interface RunAsMethod {
        void runWithException() throws Throwable;
    }

    public static <T> Consumer<T> createWriteConsumer(Consumer<T> originalConsumer) {
        return message -> runWithWritePermission(() -> originalConsumer.accept(message));
    }

    public static void runWithWritePermission(final RunAsMethod func) {
        final Authentication originalAuthentication = SecurityContextHolder.getContext().getAuthentication();

        final AnonymousAuthenticationToken token = new AnonymousAuthenticationToken(
            "system",
            originalAuthentication != null ? originalAuthentication.getPrincipal() : "system",
            AuthorityUtils.createAuthorityList("ROLE_ADMIN", "SCOPE_write")
        );

        SecurityContextHolder.getContext().setAuthentication(token);
        try {
            func.runWithException();
        } catch (Throwable e) {
            throw new RuntimeException("exception during method with altered permissions", e);
        } finally {
            SecurityContextHolder.getContext().setAuthentication(originalAuthentication);
        }
    }
}