如何在Spring Security反应式应用程序中以编程方式设置Authentication对象



在非响应式应用程序中,我们可以使用SecurityContextHolder.getContext().setAuthentication(authentication);以编程方式验证请求。

Webflux的等价物是什么?

public class ReactiveServiceAuthenticationFilter implements WebFilter {
private final ReactiveAuthenticationManager authenticationManager;
public ReactiveServiceAuthenticationFilter(ReactiveAuthenticationManager authenticationManager){
this.authenticationManager = authenticationManager;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
Mono<Authentication> authentication = authenticationManager.authenticate(new ReactiveServiceAuthentication(principal, authorization));
...
//Replacement for SecurityContextHolder.getContext().setAuthentication(authentication);
...
return chain.filter(exchange);
}

这是我的解决方案。关键是使用subscriberContext来调用ReactiveSecurityContextHolder.withAuthentication(authentication)

public class ReactiveServiceAuthenticationFilter implements WebFilter {
private final ReactiveAuthenticationManager authenticationManager;
public ReactiveServiceAuthenticationFilter(ReactiveAuthenticationManager authenticationManager){
this.authenticationManager = authenticationManager;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
//... In my case I retrieve principal / authorization from headers
return authenticationManager.authenticate(new ReactiveServiceAuthentication(principal, authorization))
.flatMap(authentication -> chain.filter(exchange)
.subscriberContext(c -> ReactiveSecurityContextHolder.withAuthentication(authentication)))
.onErrorResume(AuthenticationException.class, e -> {
log.error("Authentication Exception", e);
return chain.filter(exchange);
});
}

您可以这样做,但不确定它是否有效:

@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return ReactiveSecurityContextHolder.getContext()
.flatMap(securityContext -> {
return authenticationManager.authenticate(new ReactiveServiceAuthentication(principal, authorization))
.map(authentication -> {
securityContext.setAuthentication(authentication);
return securityContext;
}).thenReturn("")
})
.defaultIfEmpty("")
.flatMap(string -> chain.filter(exchange));
}

但我认为这是最好的方式。你需要额外的一些课程来做这件事。

创建Bean 的SecurityConfig类

@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class SecurityConfig {
private final ReactiveAuthenticationManager authenticationManager;
private final CustomSecurityContext customSecurityContext;
public SecurityConfig(ReactiveAuthenticationManager authenticationManager,
CustomSecurityContext customSecurityContext) {
this.authenticationManager = authenticationManager;
this.customSecurityContext = customSecurityContext;
}
@Bean
SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) {
return http
.csrf().disable()
.cors().disable()
.formLogin().disable()
.httpBasic().disable()
.exceptionHandling()
.and()
.authenticationManager(authenticationManager)
.securityContextRepository(customSecurityContext)
.authorizeExchange()
.anyExchange()
.authenticated()
.and()
.build();
}
}

CustomSecurityCOntext类

@Component
public class CustomSecurityContext implements ServerSecurityContextRepository {
private final AuthenticationManager authenticationManager;
public CustomSecurityContext(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Override
public Mono<Void> save(ServerWebExchange swe, SecurityContext sc) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Mono<SecurityContext> load(ServerWebExchange swe) {
if (CHECK SOMETHING IF YOU WANT TO OTHERWISE NO NEED OF THIS IF) {
return authenticationManager.authenticate(new ReactiveServiceAuthentication(principal, authorization))
.map(SecurityContextImpl::new);
}
return Mono.empty();
}
}

现在在您的ReactiveServiceAuthenticationFilter类中

@Component
public class ReactiveServiceAuthenticationFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return ReactiveSecurityContextHolder.getContext()
.map(securityContext -> (String) securityContext.getAuthentication().getPrincipal())
.defaultIfEmpty("")
.flatMap(principal -> {
if (!principal.isEmpty()) 
return chain.filter(decorate(exchange, principal)); 
// In decorate method check your authentication. 
// If not valid then return mono error. 
// Otherwise return ServerWebExchange object.
else 
return chain.filter(exchange);
});
}
}

最新更新