How To Inject AuthenticationManager using Java Configuration in a Custom Filter

JavaSpringSpring SecuritySpring Java-Config

Java Problem Overview


I'm using Spring Security 3.2 and Spring 4.0.1

I'm working on converting an xml config into a Java config. When I annotate AuthenticationManager with @Autowired in my Filter, I'm getting an exception

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: {}

I've tried injecting AuthenticationManagerFactoryBean but that also fails with a similar exception.

Here is the XML configuration I'm working from

<?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>

Here is the Java Config I'm attempting

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

And this is the Custom Filter class. The line giving me trouble is the setter for 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);
    }
}

Java Solutions


Solution 1 - Java

Override method authenticationManagerBean in WebSecurityConfigurerAdapter to expose the AuthenticationManager built using configure(AuthenticationManagerBuilder) as a Spring bean:

For example:

   @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
   @Override
   public AuthenticationManager authenticationManagerBean() throws Exception {
       return super.authenticationManagerBean();
   }

Solution 2 - Java

In addition to what Angular University said above you may want to use @Import to aggregate @Configuration classes to the other class (AuthenticationController in my case) :

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

Spring doc about Aggregating @Configuration classes with @Import: link

Solution 3 - Java

When I @Bean'ed AuthenticationManager and @Autowired it in same class then needed to activate circular references but that is rather as for CDI.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionrinceView Question on Stackoverflow
Solution 1 - JavaAngular UniversityView Answer on Stackoverflow
Solution 2 - JavaAbdEllah AbrilView Answer on Stackoverflow
Solution 3 - JavaOleksii KyslytsynView Answer on Stackoverflow