在我的 Spring Boot 应用程序中,我使用 spring-security 并实现了
WebFilter
以在 Authentication
中设置 ReactiveSecurityContextHolder
。然而,当我尝试检索它时,Authentication
对象变为空。
这是重现我面临的问题的代码示例片段。
Spring-boot应用程序启动类如下:
@EnableWebFlux
@SpringBootApplication
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
@Configuration
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.csrf(csrfSpec -> csrfSpec.disable())
.authorizeExchange(auth -> auth.pathMatchers(HttpMethod.GET, "/**")
.authenticated())
.httpBasic(httpBasicSpec -> httpBasicSpec.disable())
.addFilterBefore(customWebFilter(), SecurityWebFiltersOrder.AUTHENTICATION)
.build();
}
public WebFilter customWebFilter() {
return (exchange, chain) -> {
Authentication authenticatedToken =
new PreAuthenticatedAuthenticationToken("User_PreAuthenticated", "");
authenticatedToken.setAuthenticated(true);
return chain.filter(exchange)
.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authenticatedToken));
};
}
}
这是我尝试从
Authentication
检索 ReactiveSecurityContextHolder
的代码,但是身份验证对象将为空。
// This authentication object is coming null.
Authentication authentication = ReactiveSecurityContextHolder.getContext()
.map(SecurityContext::getAuthentication)
.toFuture().getNow(null);
我已经参考了多个 StackOverflow 问题和文档,但是我无法解决这个特定问题。我错过了什么吗?
当你处于反应范式时,你应该始终保持反应。 在反应式应用程序中,
Authentication
存储在订阅者上下文中,而不是存储在ThreadLocal
中,因为它通常在 Spring MVC 中实现(不是反应式应用程序)。
因此,当您尝试以这种方式检索
Authentication
时:
// This authentication object is coming null.
Authentication authentication = ReactiveSecurityContextHolder.getContext()
.map(SecurityContext::getAuthentication)
.toFuture().getNow(null);
您将获得 null,因为
Authentication
仅限于 subscriber 上下文。
您可能会注意到
ReactiveSecurityContextHolder.getContext()
返回 Mono
,因此您应该在反应式链中调用它,而不用阻塞调用或其他 subscribe()
方法来破坏它
例如:
@GetMapping
public Mono<ResponseEntity<String>> endpoint() {
return ReactiveSecurityContextHolder.getContext()
.map(securityContext -> securityContext.getAuthentication())
.map(authentication -> authentication.getName())
.map(userName -> ResponseEntity.ok(userName));
}