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

时间:2018-07-12 22:35:51

标签: java spring spring-security spring-webflux project-reactor

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

SecurityContextHolder.getContext().getAuthentication();

ReactiveSecurityContextHolder.getContext().map(ctx -> ctx.getAuthentication()).block()

并尝试从控制器或组件访问这些内容将解析为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();
}

其中PreAuthenticationAbstractAuthenticationToken的实例,而AuthenticationToken扩展了UsernamePasswordAuthenticationToken

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

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

2 个答案:

答案 0 :(得分:3)

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

  

身份验证是按上下文存储的。

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

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

SecurityContextHolder.getContext().getAuthentication();

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

  

从方法中返回反应式链,该方法正在进行ReactiveSecurityContextHolder.getContext()调用。

由于您要返回一连串的反应式运算符,因此Spring会对您的链进行订阅以便执行它。 当Spring完成时,it provides a security context to whole chain。因此,此链中的每个调用ReactiveSecurityContextHolder.getContext()都将返回预期的数据。

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

答案 1 :(得分:2)

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

  

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

希望对您有帮助。

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