ReactiveSecurityContextHolder#getContext returns 空上下文

ReactiveSecurityContextHolder#getContext returns an empty context

我尝试对用户进行身份验证(它有效)并从上下文中获取用户令牌,但它不起作用。

我有一个简单的微服务应用程序作为我的 pet-project 并使用 WebFlux 作为 web-framework。我尝试调试 ReactiveSecurityContextHolder#getContext 并在 flatMap 内部我看到我的用户令牌在上下文中,但在我的应用程序中我有一个空的上下文。

SecurityConfiguration.java

@EnableWebFluxSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(
            AuthenticationWebFilter authenticationWebFilter,
            ServerHttpSecurity http) {
        return http
                .csrf().disable()
                .httpBasic().disable()
                .formLogin().disable()
                .logout().disable()
                .addFilterAt(authenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION)
                .authorizeExchange()
                .anyExchange().authenticated()
                .and()
                .build();
    }

    @Bean
    public AuthenticationWebFilter authenticationWebFilter(
            SecurityTokenBasedAuthenticationManager authenticationManager,
            TokenAuthenticationConverter tokenAuthenticationConverter) {
        AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(authenticationManager);

        authenticationWebFilter.setServerAuthenticationConverter(tokenAuthenticationConverter);
        authenticationWebFilter.setSecurityContextRepository(new WebSessionServerSecurityContextRepository());

        return authenticationWebFilter;
    }
}

ReactiveAuthenticationManager.java

@Slf4j
@Component
@AllArgsConstructor
public class SecurityTokenBasedAuthenticationManager implements ReactiveAuthenticationManager {

    private final AuthWebClient authWebClient;

    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {

        return Mono.just(authentication)
                .switchIfEmpty(Mono.defer(this::raiseBadCredentials))
                .cast(Authorization.class)
                .flatMap(this::authenticateToken)
                .map(user ->
                        new Authorization(user, (AuthHeaders) authentication.getCredentials()));
    }

    private <T> Mono<T> raiseBadCredentials() {
        return Mono.error(new TokenValidationException("Invalid Credentials"));
    }

    private Mono<User> authenticateToken(Authorization authenticationToken) {
        AuthHeaders authHeaders = (AuthHeaders) authenticationToken.getCredentials();
        return Optional.of(authHeaders)
                .map(headers -> authWebClient.validateUserToken(authHeaders.getAuthToken(), authHeaders.getRequestId())
                        .doOnSuccess(user -> log
                                .info("Authenticated user " + user.getUsername() + ", setting security context")))
                .orElseThrow(() -> new MissingHeaderException("Authorization is missing"));
    }
}

SecurityUtils.java

@Slf4j
public class SecurityUtils {

    public static Mono<AuthHeaders> getAuthHeaders() {
        return getSecurityContext()
                .map(SecurityContext::getAuthentication)
                .map(Authentication::getCredentials)
                .cast(AuthHeaders.class)
                .doOnSuccess(authHeaders -> log.info("Auth headers: {}", authHeaders));
    }

    private static Mono<SecurityContext> getSecurityContext() {
        return ReactiveSecurityContextHolder.getContext();
    }
}

Web客户端建设

(...)
WebClient.builder()
                .baseUrl(url)
                .filter(loggingFilter)
                .defaultHeaders(httpHeaders ->
                        getAuthHeaders()
                                .doOnNext(headers -> httpHeaders.putAll(
                                        Map.of(
                                                REQUEST_ID, singletonList(headers.getRequestId()),
                                                AUTHORIZATION, singletonList(headers.getAuthToken())))))
                .build()
(...)

主要问题出在我的 WebClient 的建筑物内 - 我希望,我将拥有完整的 webclient 与请求 header,但正如我上面所描述的 - 我在 [=29= 中有一个空的上下文]SecurityUtils.java

因此,在调查和调试之后,我找到了将流合并为单个流的原因。恕我直言,这不是这个问题的完全原因,但现在可以了。

(...)
getAuthHeaders()
    .flatMap(authHeaders -> buildWebClient(url, authHeaders)
                  .get()
(...)