Как программно установить объект аутентификации в реактивных приложениях Spring Security? - PullRequest
0 голосов
/ 24 апреля 2020

В нереактивных приложениях мы могли бы сделать 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);
    }

Ответы [ 2 ]

0 голосов
/ 25 апреля 2020

Вот мое решение. Ключ должен был использовать 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);
            });
    }
0 голосов
/ 24 апреля 2020

Вы можете сделать это, но не уверены, что это будет работать или нет:

@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));
}

Но лучший способ, которым я думаю, это. для этого вам понадобится дополнительный класс.

Класс SecurityConfig для создания класса 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

@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);
                });
    }
}
...