ReactiveSecurityContextHolder 在 Spring WebFlux 中为空

Jul*_*3nT 6 spring spring-security spring-boot reactive spring-webflux

我正在尝试将 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();
        }

    }

}
Run Code Online (Sandbox Code Playgroud)

此代码始终抛出 IllegalStateException。

如果我添加一个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")
        );
Run Code Online (Sandbox Code Playgroud)

它工作正常并打印“嗨管理员”。但这不是重点,文章说“在 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);
    }

}
Run Code Online (Sandbox Code Playgroud)

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

xte*_*mi2 9

You have to return the stream in which you want to access ReactiveSecurityContextHolder. You're not allowed to subscribe within another stream OR you have to do the Reactor context switch manually.

@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());
    }
}
Run Code Online (Sandbox Code Playgroud)