【问题标题】:ReactiveSecurityContextHolder is empty in Spring WebFluxSpring WebFlux 中的 ReactiveSecurityContextHolder 为空
【发布时间】:2018-11-27 01:34:43
【问题描述】:

我正在尝试将 ReactiveSecurityContextHolder 与 Spring WebFlux 一起使用。不幸的是,SecurityContext 是空的:

@Configuration
public class Router {

    @Bean
    public RouterFunction<ServerResponse> routes(Handler handler) {
        return nest(
                path("/bill"),
                route(
                        GET("/").and(accept(APPLICATION_JSON)), handler::all));
    }

    @Component
    class Handler {

        Mono<ServerResponse> all(ServerRequest request) {

            ReactiveSecurityContextHolder.getContext()
                .switchIfEmpty(Mono.error(new IllegalStateException("ReactiveSecurityContext is empty")))
                .map(SecurityContext::getAuthentication)
                .map(Authentication::getName)
                .flatMap(s -> Mono.just("Hi " + s))
                .subscribe(
                        System.out::println,
                        Throwable::printStackTrace,
                        () -> System.out.println("completed without a value")
                );

            return ok().build();
        }

    }

}

此代码总是抛出 IllegalStateException。

如果我添加一个像 here 所示的subscriberContext :

Authentication authentication = new TestingAuthenticationToken("admin", "password", "ROLE_ADMIN");

ReactiveSecurityContextHolder.getContext()
        .switchIfEmpty(Mono.error(new IllegalStateException("ReactiveSecurityContext is empty")))
        .map(SecurityContext::getAuthentication)
        .map(Authentication::getName)
        .flatMap(s -> Mono.just("Hi " + s))
        .subscriberContext(ReactiveSecurityContextHolder.withAuthentication(authentication))
        .subscribe(
                System.out::println,
                Throwable::printStackTrace,
                () -> System.out.println("completed without a value")
        );

它工作正常并打印“Hi admin”。但这不是重点,文章说“在 WebFlux 应用程序中,subscriberContext 是使用 ReactorContextWebFilter 自动设置的”。所以我应该能够获取登录的用户。

我有这个配置:

@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class SecurityConfig {

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        return http.authorizeExchange()
            .anyExchange().authenticated()
            .and().formLogin()
            .and().build();
    }

    @Bean
    public MapReactiveUserDetailsService userDetailsService() {
        UserDetails user = User.withDefaultPasswordEncoder()
            .username("user")
            .password("password")
            .roles("USER")
            .build();

        UserDetails admin = User.withDefaultPasswordEncoder()
            .username("admin")
            .password("password")
            .roles("ADMIN")
            .build();

        return new MapReactiveUserDetailsService(user, admin);
    }

}

我在这里遗漏了什么吗?如果我在 ReactorContextWebFilter 中放置断点,我可以看到它在每个请求之前被正确调用。但是我的 ReactiveSecurityContextHolder 总是空的...

【问题讨论】:

  • 您找到解决方案了吗?我最终使用了org.springframework.web.reactive.function.server.ServerRequest#principal,但我也想使用ReactiveSecurityContextHolder
  • 不,我使用带有 @Controller 注释的“经典”表示法,并在我的方法中注入了 Principal 对象。

标签: spring spring-boot spring-security spring-webflux reactive


【解决方案1】:

你必须返回你想要访问的流ReactiveSecurityContextHolder。您不能在另一个流中订阅您必须手动执行 Reactor 上下文切换。

@Component
class Handler {
    Mono<ServerResponse> all(ServerRequest request) {
        return ReactiveSecurityContextHolder.getContext()
                .switchIfEmpty(Mono.error(new IllegalStateException("ReactiveSecurityContext is empty")))
                .map(SecurityContext::getAuthentication)
                .map(Authentication::getName)
                .flatMap(s -> Mono.just("Hi " + s))
                .doOnNext(System.out::println)
                .doOnError(Throwable::printStackTrace)
                .doOnSuccess(s -> System.out.println("completed without value: " + s))
                .flatMap(s -> ServerResponse.ok().build());
    }
}

【讨论】:

  • 如何在 WebFluxTagsContributor.httpRequestTags 中访问 ReactiveSecurityContextHolder?该方法未返回发布者
  • 您能否解释一下您在答案标题中提到的理论描述。
  • 好的,在传统的基于 Servlet 的 WebApp 中,SecurityContext 保存在 ThreadLoacal 中。如果您不创建新线程,则可以访问安全上下文,但如果您想从另一个线程访问 SecurityContext,则必须手动“移动”/将安全上下文从当前线程复制到新线程。这同样适用于响应式 WebApp,但有点不同。当您启动/订阅一个新 Stream 时,它与在 Servlet WebApp 中创建一个新线程相同。然后您必须手动“移动”/将 SecurityContext 复制到新的 Stream/Subscription。
猜你喜欢
  • 2019-03-11
  • 2021-03-03
  • 2020-12-11
  • 2020-01-20
  • 2020-08-09
  • 1970-01-01
  • 2020-09-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多