Cómo inyectar AuthenticationManager usando la configuración de Java en un filtro personalizado

81

Estoy usando Spring Security 3.2 y Spring 4.0.1

Estoy trabajando para convertir una configuración xml en una configuración de Java. Cuando anoto AuthenticationManagercon @Autowireden mi filtro, estoy recibiendo una excepción

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.authentication.AuthenticationManager] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}

Intenté inyectar AuthenticationManagerFactoryBeanpero eso también falla con una excepción similar.

Aquí está la configuración XML con la que estoy trabajando

<?xml version="1.0" encoding="UTF-8"?> <beans ...>
    <security:authentication-manager id="authenticationManager">
        <security:authentication-provider user-service-ref="userDao">
            <security:password-encoder ref="passwordEncoder"/>
        </security:authentication-provider>
    </security:authentication-manager>

    <security:http
            realm="Protected API"
            use-expressions="true"
            auto-config="false"
            create-session="stateless"
            entry-point-ref="unauthorizedEntryPoint"
            authentication-manager-ref="authenticationManager">
        <security:access-denied-handler ref="accessDeniedHandler"/>
        <security:custom-filter ref="tokenAuthenticationProcessingFilter" position="FORM_LOGIN_FILTER"/>
        <security:custom-filter ref="tokenFilter" position="REMEMBER_ME_FILTER"/>
        <security:intercept-url method="GET" pattern="/rest/news/**" access="hasRole('user')"/>
        <security:intercept-url method="PUT" pattern="/rest/news/**" access="hasRole('admin')"/>
        <security:intercept-url method="POST" pattern="/rest/news/**" access="hasRole('admin')"/>
        <security:intercept-url method="DELETE" pattern="/rest/news/**" access="hasRole('admin')"/>
    </security:http>

    <bean class="com.unsubcentral.security.TokenAuthenticationProcessingFilter"
          id="tokenAuthenticationProcessingFilter">
        <constructor-arg value="/rest/user/authenticate"/>
        <property name="authenticationManager" ref="authenticationManager"/>
        <property name="authenticationSuccessHandler" ref="authenticationSuccessHandler"/>
        <property name="authenticationFailureHandler" ref="authenticationFailureHandler"/>
    </bean>

</beans>

Aquí está la configuración de Java que estoy intentando

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    private AccessDeniedHandler accessDeniedHandler;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                .exceptionHandling()
                    .authenticationEntryPoint(authenticationEntryPoint)
                    .accessDeniedHandler(accessDeniedHandler)
                    .and();
        //TODO: Custom Filters
    }
}

Y esta es la clase Filtro personalizado. La línea que me está dando problemas es el configurador de AuthenticationManager

@Component
public class TokenAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {


    @Autowired
    public TokenAuthenticationProcessingFilter(@Value("/rest/useAuthenticationManagerr/authenticate") String defaultFilterProcessesUrl) {
        super(defaultFilterProcessesUrl);
    }


    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
      ...
    }

    private String obtainPassword(HttpServletRequest request) {
        return request.getParameter("password");
    }

    private String obtainUsername(HttpServletRequest request) {
        return request.getParameter("username");
    }

    @Autowired
    @Override
    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
        super.setAuthenticationManager(authenticationManager);
    }

    @Autowired
    @Override
    public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler successHandler) {
        super.setAuthenticationSuccessHandler(successHandler);
    }

    @Autowired
    @Override
    public void setAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) {
        super.setAuthenticationFailureHandler(failureHandler);
    }
}
Rince
fuente
¿Puedo preguntar qué hacen los Autowired justo encima de un Override? Nunca he visto esto antes. ¿Qué está conectado con esto?
Stephane
¿Cómo agregaste tu filtro personalizado? Hice mi propio filtro y proveedor de autenticación. Pero no sé cómo configurarlos para que funcionen juntos. Aquí está mi pregunta stackoverflow.com/questions/30502589/…
PaintedRed

Respuestas:

187

Reemplazar el método authenticationManagerBeande WebSecurityConfigurerAdapterexponer la AuthenticationManager construido usando configure(AuthenticationManagerBuilder)como un grano de primavera:

Por ejemplo:

   @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
   @Override
   public AuthenticationManager authenticationManagerBean() throws Exception {
       return super.authenticationManagerBean();
   }
Universidad Angular
fuente
1
@qxixp "para exponer el AuthenticationManager creado con configure (AuthenticationManagerBuilder) como un bean Spring"
Roger
1
@Roger, ¿por qué necesitamos exponer manualmente el AuthenticationManager?
qxixp
11
@qxixp solo puede Autowire un bean administrado por Spring. Si no está expuesto como un bean, no puede conectarlo automáticamente.
Roger
El súper método no es un Bean, luego anótelo y agregue la anotación Bean.
buscando
2
Lo que realmente me ayudó con esta respuesta es el "nombre = BeanIds.AUTHENTICATION_MANAGER". Sin él, no funciona al menos en mi entorno.
Isthar
1

Además de lo que dijo Angular University anteriormente, es posible que desee usar @Import para agregar clases de @Configuration a la otra clase (AuthenticationController en mi caso):

@Import(SecurityConfig.class)
@RestController
public class AuthenticationController {
@Autowired
private AuthenticationManager authenticationManager;
//some logic
}

Spring doc sobre la agregación de clases de @Configuration con @Import: enlace

AbdEllah Abril
fuente