问题
In non reactive applications we could do SecurityContextHolder.getContext().setAuthentication(authentication);
to authenticate a request programmatically.
What would be the equivalent with 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);
}
回答1:
You can do this, but not sure it will work or not:
@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));
}
But the best way I think this. you need extra some class to do this.
SecurityConfig class to create a Bean
@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 class
@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();
}
}
And now in your ReactiveServiceAuthenticationFilter class
@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);
});
}
}
回答2:
Here is my solution. The key was to use the subscriberContext to invoke 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);
});
}
来源:https://stackoverflow.com/questions/61397201/how-do-you-set-programmatically-the-authentication-object-in-spring-security-rea