How To Inject AuthenticationManager using Java Configuration in a Custom Filter

126,041

Solution 1

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

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

Share:
126,041
rince
Author by

rince

SOreadytohelp

Updated on February 03, 2022

Comments

  • rince
    rince about 2 years

    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);
        }
    }
    
  • Roger
    Roger about 9 years
    @qxixp "to expose the AuthenticationManager built using configure(AuthenticationManagerBuilder) as a Spring bean"
  • qxixp
    qxixp about 9 years
    @Roger, why do we need to manually expose the AuthenticationManager?
  • Roger
    Roger about 9 years
    @qxixp you can only Autowire a spring managed bean. If its not exposed as a bean, you cannot Autowire it.
  • searching9x
    searching9x about 8 years
    The super method is not a Bean, then Override it and add Bean annotation.
  • Isthar
    Isthar about 7 years
    What really helped me of this answer is the "name = BeanIds.AUTHENTICATION_MANAGER". Without it, it doesn't work at least in my environment.
  • Dimitri Kopriwa
    Dimitri Kopriwa over 6 years
    Using this I can't login it create 401. But the same error I have in my test environment is gone. I also have 401 in my test environment.
  • Rick Slinkman
    Rick Slinkman over 5 years
    Thanks, this worked for me when I had a StackOverflowException in this configuration today..
  • phxism
    phxism almost 4 years
    this way does not work for customized filter in spring security oauth2 ,@Bean super.authenticationManagerBean() like this will not build the ClientDetailsUserDetailsService and DaoAuthenticationProvider it's different from the way http.getSharedObject(AuthenticationManager.class)