ReactiveSecurityContextHolder.getContext()为空,但@AuthenticationPrincipal有效

Dee*_*pak 1 java spring spring-security project-reactor spring-webflux

我一直在Spring Security + Webflux中使用ReactiveAuthenticationManager。它是经过自定义的,以返回一个实例UsernamePasswordAuthenticationToken,从该实例中我可以看出是调用时应接收的内容ReactiveSecurityContextHolder.getContext().map(ctx -> ctx.getAuthentication()).block()。据我所知,我无法通过两种方式访问​​身份验证上下文:

SecurityContextHolder.getContext().getAuthentication();
Run Code Online (Sandbox Code Playgroud)

要么

ReactiveSecurityContextHolder.getContext().map(ctx -> ctx.getAuthentication()).block()
Run Code Online (Sandbox Code Playgroud)

尝试从控制器或组件访问这些文件将解决null。对于我是否真的要Authentication在自定义管理器中返回实例,我有些怀疑,好像我是:

@Override
public Mono<Authentication> authenticate(final Authentication authentication) {
    if (authentication instanceof PreAuthentication) {
        return Mono.just(authentication)
            .publishOn(Schedulers.parallel())
            .switchIfEmpty(Mono.defer(this::throwCredentialError))
            .cast(PreAuthentication.class)
            .flatMap(this::authenticatePayload)
            .publishOn(Schedulers.parallel())
            .onErrorResume(e -> throwCredentialError())
            .map(userDetails -> new AuthenticationToken(userDetails, userDetails.getAuthorities()));
    }

    return Mono.empty();
}
Run Code Online (Sandbox Code Playgroud)

PreAuthentication的实例在哪里扩展AbstractAuthenticationTokenAuthenticationTokenUsernamePasswordAuthenticationToken

有趣的是,尽管ReactiveSecurityContextHolder.getContext().map(ctx -> ctx.getAuthentication()).block()在控制器中不起作用,但我可以@AuthenticationPrincipal在控制器中成功地将带有注释的身份验证主体作为方法参数注入。

这似乎是一个配置问题,但我不知道在哪里。有人知道为什么我不能返回身份验证吗?

小智 8

由于您正在使用WebFlux,因此您将使用事件循环来处理请求。您不再像Tomcat那样使用每个请求线程模型。

身份验证按上下文存储。

使用Tomcat,当请求到达时,Spring将身份验证存储在中SecurityContextHolderSecurityContextHolder使用ThreadLocal变量存储身份验证。仅当您尝试从设置了身份验证对象ThreadLocal的同一线程中的对象中获取特定身份验证对象时,该身份验证对象才对您可见。这就是为什么您可以通过静态调用在控制器中获得身份验证的原因。ThreadLocal对象知道返回给您什么,因为它知道您的上下文-您的线程。

使用WebFlux,您可以仅使用1个线程来处理所有请求。像这样的静态调用将不再返回预期结果:

SecurityContextHolder.getContext().getAuthentication();

因为不再有使用ThreadLocal对象的方法。为您获取身份验证的唯一方法是在控制器的方法签名中要求身份验证,或者...

从正在ReactiveSecurityContextHolder.getContext()调用的方法返回反应链。

由于您要返回一连串的反应式运算符,因此Spring会对您的链进行订阅以便执行它。当Spring执行此操作时,它将为整个链提供安全上下文。因此,ReactiveSecurityContextHolder.getContext()该链中的每个调用都将返回预期的数据。

您可以在此处阅读有关Mono / Flux上下文的更多信息,因为它是Reactor特有的功能。

  • 您能否显示有关如何以被动方式设置安全上下文的示例? (3认同)

ahn*_*suk 5

我也有类似的问题。因此,我使用 JWT 令牌示例上传了 ReactiveSecurityContext 的 github。

https://github.com/heesuk-ahn/spring-webflux-jwt-auth-example

我希望它对你有帮助。

我们需要创建一个自定义的authentication filter. 如果该过滤器中的身份验证成功,则会将principal信息存储在ReactiveSecurityHolder. ReactiveSecurityHolder我们可以从控制器访问。