Estoy tratando de configurar Spring Security dentro de una aplicación Spring Boot reactiva e incluí el proyecto principal que se basa en la nueva versión de Spring-Security 5.0.0.RC1 :

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.0.M6</version>
</parent>

Configuré mi Spring WebSecurity para usar un AuthenticationFilter personalizado para recuperar mi información de autenticación, así como un AuthenticationManager personalizado con mis propios proveedores.

Config:

@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class WebSecurityConfig {

    private ServerAuthenticationEntryPoint entryPoint = new CustomServerAuthenticationEntryPoint();

    @Bean
    SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) throws Exception {
        return http
            // we rely on method security
            .authorizeExchange()
                .anyExchange().permitAll()
            .and()
                .logout().disable()
                .authenticationManager(authenticationManager())
                //Override BasicAuthenticationEntryPoint
                .exceptionHandling().serverAuthenticationEntryPoint(this.entryPoint)
            .and()
                .addFilterAt(upmAuthenticationWebFilter(), SecurityWebFiltersOrder.AUTHENTICATION)
            .build();
    }


    private ReactiveAuthenticationManager authenticationManager() {
        return new CustomReactiveAuthenticationManager()
            .authenticationProvider(new CustomAuthenticationProvider());
    }

    private AuthenticationWebFilter upmAuthenticationWebFilter() {
        try {
            AuthenticationWebFilter authenticationFilter = new AuthenticationWebFilter(authenticationManager());
            authenticationFilter.setServerAuthenticationFailureHandler(new ServerAuthenticationEntryPointFailureHandler(this.entryPoint));
            authenticationFilter.setAuthenticationConverter(new CustomAuthenticationConverter(););

            return authenticationFilter;
        } catch (Exception e) {
            throw new BeanInitializationException("Could not initialize AuthenticationWebFilter.", e);
        }
    }
}

ReactiveAuthenticationManager:

public class CustomReactiveAuthenticationManager implements ReactiveAuthenticationManager {

    private List<AuthenticationProvider> authenticationProvider = new ArrayList<>();

    public CustomReactiveAuthenticationManager customAuthenticationProvider(
        final AuthenticationProvider customAuthenticationProvider) {
        this.authenticationProvider.add(customAuthenticationProvider);
        return this;
    }

    @Override
    public Mono<Authentication> authenticate(final Authentication authentication) {
        Class<? extends Authentication> toTest = authentication.getClass();
        for(AuthenticationProvider provider: authenticationProvider) {
            if (provider.supports(toTest)) {
                try {

                    //The provider returns a full authenticated Authentication object.
                    //With authentication.setAuthenticated(true);
                    return Mono.just(provider.authenticate(authentication));
                } catch (Exception e) {
                    return Mono.error(new BadCredentialsException("Invalid Credentials"));
                }
            }
        }
        return Mono.error(new ProviderNotFoundException("Unsupported Credentials"));
    }
}

Cuando ahora realizo una llamada en un Endpoint que requiere un usuario autenticado. Como:

public interface HelloService {
    /**
     * This method returns just 'Hello, world!'.
     *
     * @return Return 'Hello, world!'.
     */
    @PreAuthorize("isAuthenticated()")
    Mono<String> getHelloWorld();
}

Siempre obtengo una excepción de acceso denegado 401.

Después de depurarlo en la línea 75 de PrePostAdviceReactiveMethodInterceptor

 auth -> this.preInvocationAdvice.before(auth, invocation, preAttr)

Vi que el objeto de autenticación pasado es un usuario anónimo.

Ahora mi pregunta: ¿Olvidé configurar algo para el uso con ReactiveSecurityContext?

La semana pasada todavía usé el MilestoneRelease M5 anterior, donde el paquete reactivo Spring-Security todavía se encontraba adicionalmente, en Spring-Security-Reactive. En este caso, se recuperó el objeto de autenticación y se pudo completar mi solicitud.

1
Marvin 13 nov. 2017 a las 20:37

2 respuestas

La mejor respuesta

Si alguien está interesado en el problema.

No configuré un serverSecurityContextRepository en AuthenticationWebFilter.

Así que establecer esto resolvió el problema.

private AuthenticationWebFilter upmAuthenticationWebFilter() {
    try {
        AuthenticationWebFilter authenticationFilter = new AuthenticationWebFilter(authenticationManager());
        authenticationFilter.setServerAuthenticationFailureHandler(new ServerAuthenticationEntryPointFailureHandler(this.entryPoint));
        authenticationFilter.setAuthenticationConverter(new CustomAuthenticationConverter(););

        //Setting the Context Repo helped
        authenticationFilter.setServerSecurityContextRepository(new WebSessionServerSecurityContextRepository());

        return authenticationFilter;
    } catch (Exception e) {
        throw new BeanInitializationException("Could not initialize AuthenticationWebFilter.", e);
    }
}
1
Marvin 20 nov. 2017 a las 11:28

Como configurar el repositorio no funcionó para mí, acabo de agregar el Principal a mi Función REST:

@GetMapping("/select")
public Mono<String> select(java.security.Principal principal) {
     log.debug("Authorities: {}", ((Authentication) principal).getAuthorities());
}
0
Frischling 6 feb. 2018 a las 15:00