spring-boot: AuthenticationManager bean is missing when upgraded to Spring Boot 2.0.0.M6

Today I have upgraded one of my sample from Spring Boot 2.0.0.M4 to 2.0.0.M6.

https://github.com/hantsy/spring-microservice-sample

When starting up auth-service, it complains AuthentionManager bean is not existed in my AuthenticationController, I have to expose it manually in my security config.

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

Is there something changed in Spring Boot 2.0.0.M6?

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 17 (4 by maintainers)

Most upvoted comments

AuthenticationManager bean is required for password grant type in Spring Security OAuth2. The whole design of AuthorizationServerConfigurer + ResourceServerConfigurer assumes that you never use WebSecurityConfigurerAdapter in oauth2-based app. However now the only way to get the AuthenticationManager seems to be this:

@Configuration
public static class AuthenticationMananagerProvider extends WebSecurityConfigurerAdapter {

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

}

This is really confusing, because now, along with ResourceServerConfigurer, you have two beans exposing configure(HttpSecurity http). Which one should I use, then? They are not compatible.

This is the most obscure thing I’ve met so far in Spring Security OAuth2 and this is purely caused by not exposing AuthenticationManager automatically.

@l0co Yes, AuthenticationManager is required for the password grant type in Spring Security OAuth2. It’s required as a constructor arg in ResourceOwnerPasswordTokenGranter.

The whole design of AuthorizationServerConfigurer + ResourceServerConfigurer assumes that you never use WebSecurityConfigurerAdapter in oauth2-based app

This is not correct. You still need to configure your user’s either by providing an AuthenticationManager OR AuthenticationProvider OR configuring via AuthenticationManagerBuilder. This needs to happen in your WebSecurityConfigurerAdapter. Spring Security OAuth2 simply uses the AuthenticationManager that is configured by your WebSecurityConfigurerAdapter.

However now the only way to get the AuthenticationManager seems to be this:

@Configuration
public static class AuthenticationMananagerProvider extends WebSecurityConfigurerAdapter {

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

}

Yes, you do need to expose the AuthenticationManager as a @Bean via the authenticationManagerBean() override. However, I don’t see this being an overhead. It’s one simple override.

This is really confusing, because now, along with ResourceServerConfigurer, you have two beans exposing configure(HttpSecurity http)

I think you meant to say AuthorizationServerConfigurer instead of ResourceServerConfigurer? The AuthorizationServerConfigurer needs to be wired with the AuthenticationManager in order to validate the user during the password grant flow. An example configuration would be:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

	@Qualifier("authenticationManagerBean")
	@Autowired
	private AuthenticationManager authenticationManager;

	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
		endpoints.authenticationManager(this.authenticationManager);
	}
}

@l0co Does this clarify things?

@jgrandja Thanks for the clarification. It does clarify things, however I still think the design would be better if you didn’t have to create your own AuthenticationManager and have this bean ready to be used in the container.

@wilkinsona I checked my initial workable version( built with Spring Boot 2.0.0.M2), it worked without exposing AuthenticationManager.

To my knowledge, nothing’s changed in this area between 2.0 M4 and 2.0 M6.

The auto-configured AuthenticationManager bean is backing off because of your AuthUserDetailsService. This happens because Boot’s AuthenticationMangerConfiguration is conditional on missing AuthenticationManager, AuthenticationProvider, and UserDetailsService beans.

If I modify your sample to be compatible with Boot 2.0 M4, it has the same failure at startup. You can use the condition evaluation report to diagnose this sort of problem (start with --debug). It shows the auto-configured authentication manager backing off:

   AuthenticationManagerConfiguration:
      Did not match:
         - @ConditionalOnMissingBean (types: org.springframework.security.authentication.AuthenticationManager,org.springframework.security.authentication.AuthenticationProvider,org.springframework.security.core.userdetails.UserDetailsService; SearchStrategy: all) found beans of type'org.springframework.security.core.userdetails.UserDetailsService'authUserDetailsService (OnBeanCondition)

@edwardzjl Declare an AuthenticationManager bean yourself. Check my example: https://github.com/hantsy/spring-webmvc-jwt-sample/blob/master/src/main/java/com/example/demo/config/SecurityConfig.java#L54-L71

Thank you for the example, however my situation seems a bit different. I have multiple AuthenticationProviders, and I need to add them to AuthenticationManagerBuilder

I can get the AuthenticationManagerBuilder bean, but after I add my customize AuthenticationProviders to it, I still cannot get the AuthenticationManager bean. If I manually call the build() method of AuthenticationManagerBuilder, I get an “Already built” exception:

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.authentication.AuthenticationManager]: Factory method 'configProviders' threw exception; nested exception is java.lang.IllegalStateException: Cannot apply org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration$EnableGlobalAuthenticationAutowiredConfigurer@639c5ab4 to already built object
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.3.2.jar:5.3.2]
...

But if I don’t call the build() there’s still no AuthenticationManager bean in my spring context

After some struggle I managed to expose the AuthenticationManager bean by

@Autowired
private AuthenticationConfiguration authenticationConfiguration;

@Bean
AuthenticationManager configProviders(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
    authenticationManagerBuilder.authenticationProvider(usernamePasswordAuthenticationProvider());
    authenticationManagerBuilder.authenticationProvider(smsAuthenticationProvider());
    return authenticationConfiguration.getAuthenticationManager();
}

But I’m not sure whether this is the correct way to do it.

I have the same problem as you,according to the document ,it looks like build a global AuthenticationManager, it will apply for all SecurityFilterChain. i don’t know how to get local AuthenticationManager in my service