Reputation: 1547
I have configured spring security using:
@Configuration
@Import(SecurityProblemSupport.class)
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private RestAuthenticationProvider authenticationProvider;
@Autowired
private CustomUserDetailService customUserDetailService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
auth.userDetailsService(customUserDetailService);
super.configure(auth);
}
Everything work as expected. The Rest authentication provider is a Component which implements AuthenticationProvider:
@Component
public class RestAuthenticationProvider implements AuthenticationProvider {
The problem is that, if I ADD in the classpath another @Component which extends AbstractUserDetailAuthenticationProvider, the RestAuthenticationProvider is not used anymore; instead spring uses one default provider, using the UserDetailService specified in configuration. There are no other links to this CustomAuthenticationProvider. It's just in the same package. The same thing happens if I use in configuration the CustomProvider but the Rest one is annotated with component and not used anywere else.
@Component // <- if I remove this, everything works fine.
public class CustomUserAuthenticationProvider extends
AbstractUserDetailsAuthenticationProvider {
I would like to switch between AuthenticationProvider(S) based on some configuration. Any help is appreciated.
Upvotes: 1
Views: 613
Reputation: 1547
I've solved this issue after several tries. I had to use
@Component
@ConditionalOnProperty(name = "authprovider", havingValue = "custom")
public class CustomUserAuthenticationProvider extends
AbstractUserDetailsAuthenticationProvider {
and
@Component
@ConditionalOnProperty(name = "authentication.type", havingValue = "restCall")
public class RestAuthenticationProvider implements AuthenticationProvider {
On the two components.
Having one of the two used directly in the "configure" method of the WebSecurityConfigurerAdapter was causing troubles, as the default "provider" was automatically used by spring.
So I had to read the environment and use a manual inject of bean:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private Environment env;
// .....
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
if (env.getProperty("authentication.type").equals("standard")) {
auth.authenticationProvider(applicationContext.getBean(
CustomUserAuthenticationProvider.class));
}
else {
auth.authenticationProvider(applicationContext.getBean(
RestAuthenticationProvider.class));
}
auth.userDetailsService(customUserDetailService);
super.configure(auth);
}
Upvotes: 0
Reputation: 4818
You can add ConditionalOnProperty
annotation to bean.
Spring will include that bean in context only if the condition is enabled or has a specific value.
For your code, it will look like:
@ConditionalOnProperty(name = "authprovider", havingValue = "custom")
public class CustomUserAuthenticationProvider extends
AbstractUserDetailsAuthenticationProvider {
And in properties file (or relevant PropertySource
) add authprovider
property:
authprovider=custom
Another option is to add @Profile
annotation to bean. It maps bean mapping that particular application profile. I use this option to load certain configuration for a specific development environment. I'd suggest to use the first option in your case.
Upvotes: 1
Reputation: 3288
I would suggest a single AuthenticationProvider
in your WebSecurityConfigurerAdapter
with delegate approach, i.e. implement just one authentication provider:
@Component
public class DelegatingAuthenticationProvider implements AuthenticationProvider {
private final AuthenticationProvider authenticationProviderFirstDelegate;
private final AuthenticationProvider authenticationProvideSecondDelegate; // This uses your UserDetailsService, e.g.
private final SomeProperty someProperty;
public Authentication authenticate(Authentication authentication) {
// Here based on someProperty, return authenticationProviderFirstDelegate.authenticate(authentication) or authenticationProviderSecondDelegate.authenticate(authentication)
}
}
and lose auth.userDetailsService(customUserDetailService);
Upvotes: 1