为 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
; 将身份验证添加到安全上下文中;这将它绑定到线程;同一个线程用于调用侦听器。
我找到了两个可能的解决方案
使用 springs RunAs
支持 (baeldung) 为特定方法添加安全上下文权限。如果我这样做,我需要将 ROLE_RUN_AS_USER
添加到我的安全方法中。在规模上,这会使注释变得非常复杂。
在执行处理程序方法之前手动更改安全上下文,然后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);
}
}
}
我通过 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
; 将身份验证添加到安全上下文中;这将它绑定到线程;同一个线程用于调用侦听器。
我找到了两个可能的解决方案
使用 springs
RunAs
支持 (baeldung) 为特定方法添加安全上下文权限。如果我这样做,我需要将ROLE_RUN_AS_USER
添加到我的安全方法中。在规模上,这会使注释变得非常复杂。在执行处理程序方法之前手动更改安全上下文,然后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);
}
}
}