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?
Comment From: wilkinsona
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)
Comment From: hantsy
@wilkinsona Thanks for your explanation.
Comment From: hantsy
@wilkinsona I checked my initial workable version( built with Spring Boot 2.0.0.M2), it worked without exposing AuthenticationManager.
Comment From: wilkinsona
That’s to be expected. It’s due to the simplification of Security auto-configuration that was made in this commit
Comment From: hantsy
@wilkinsona I understood the purpose of this change is simplification of security auto-configuration internally.
But if there is a customized UserDetailsSerivce existed, I have to expose the AuthenticationManager bean manually as above SecurityConfig. Why the authentication manager auto-configuration can not detect the my custom UserDetailsSerivce and declare the AuthenticationManager bean for me. I think it is more reasonable, as the expected behavior in 2.0.0.M2.
Comment From: mbhave
@hantsy Before M4 because the auto-config had some somewhat complex logic to create the AuthenticationManager bean. The Boot auto-configuration was updated in 2.0.0.M4 to do the minimum thing that is required to get a user for a secure application.
When you provide a UserDetailsService bean, Spring Security will automatically create an AuthenticationManager using that. So, all the auto-config does is create a UserDetailsService bean. Even if you didn't provide a custom UserDetailsService, to expose the AuthenticationManager as a bean, you need to override the authenticationManagerBean method as of 2.0.0.M4.
Comment From: hantsy
@mbhave Thanks for your explanation.
But obviously, in the former version(before 2.0.0.M4), it exposed a AuthenticationManager to user automatically if it is not existed, I think it was more reasonable.
Now(in Spring Boot 2.0.0.M6) I provided a custom UserDetailsService bean, and I have to expose the AuthenticationManager bean by overriding authenticationManagerBean myself.
Ok, this is not a big problem, if this is the purpose of this change, I accept it.
Comment From: FreezeSoul
+1 thanks
Comment From: l0co
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.
Comment From: kakawait
My 2 cents tips will be to inject AuthenticationManagerBuilder instead of AuthenticationManager and .getOrBuild() but I'm not totally sure that could resolve your issue. Security stuff are really special
Comment From: jgrandja
@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?
Comment From: l0co
@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.
Comment From: edwardzjl
@l0co Yes,
AuthenticationManageris required for thepasswordgrant type in Spring Security OAuth2. It's required as a constructor arg inResourceOwnerPasswordTokenGranter.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
AuthenticationManagerORAuthenticationProviderOR configuring viaAuthenticationManagerBuilder. This needs to happen in yourWebSecurityConfigurerAdapter. Spring Security OAuth2 simply uses theAuthenticationManagerthat is configured by yourWebSecurityConfigurerAdapter.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
AuthenticationManageras a@Beanvia theauthenticationManagerBean()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
AuthorizationServerConfigurerinstead ofResourceServerConfigurer? TheAuthorizationServerConfigurerneeds to be wired with theAuthenticationManagerin order to validate the user during thepasswordgrant 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?
How should I expose the AuthenticationManager Bean if I'm configuring SpringSecurity by exposing SecurityFilterChain Bean introduced in 5.4, in which way I cannot extend WebSecurityConfigurerAdapter?
Comment From: hantsy
@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
Comment From: edwardzjl
@edwardzjl Declare an
AuthenticationManagerbean 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.
Comment From: liuenqi
@edwardzjl Declare an
AuthenticationManagerbean yourself. Check my example: https://github.com/hantsy/spring-webmvc-jwt-sample/blob/master/src/main/java/com/example/demo/config/SecurityConfig.java#L54-L71Thank you for the example, however my situation seems a bit different. I have multiple
AuthenticationProviders, and I need to add them toAuthenticationManagerBuilderI can get the
AuthenticationManagerBuilderbean, but after I add my customizeAuthenticationProviders to it, I still cannot get theAuthenticationManagerbean. If I manually call thebuild()method ofAuthenticationManagerBuilder, 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 noAuthenticationManagerbean in my spring contextAfter some struggle I managed to expose the
AuthenticationManagerbean by```java @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
Comment From: Mehdi-HAFID
@edwardzjl Declare an
AuthenticationManagerbean yourself. Check my example: https://github.com/hantsy/spring-webmvc-jwt-sample/blob/master/src/main/java/com/example/demo/config/SecurityConfig.java#L54-L71Thank you for the example, however my situation seems a bit different. I have multiple
AuthenticationProviders, and I need to add them toAuthenticationManagerBuilderI can get the
AuthenticationManagerBuilderbean, but after I add my customizeAuthenticationProviders to it, I still cannot get theAuthenticationManagerbean. If I manually call thebuild()method ofAuthenticationManagerBuilder, 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 noAuthenticationManagerbean in my spring contextAfter some struggle I managed to expose the
AuthenticationManagerbean by```java @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.
same problem as you, did you found out why this works?