I have scenario. If the client sends the username and password then my application should use DaoAuthenticationProvider.Else if my client sent a header with "phrase" the it should use PhraseAuthenticationProvider(custom). I will point out what I did so far.
- I implemented UserDetailsService as CustomSecurityCustomerService and annotated as @Service
- I created a DaoAuthenticationProvider bean configuration as shown in below code snippet in security configuration class
public class ProjectSecurityConfigurer{
@Autowired
private AuthenticationConfiguration config;
@Autowired
PhraseAuthenticationProvider pProvider;
@Bean
ExtractionFilter getExFilter() throws Exception {
return new ExtractionFilter(config.getAuthenticationManager());
}
@Bean
SecurityFilterChain projectSecSpecs(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
.antMatchers("/myaccount").authenticated()
.antMatchers("/contact","/login").permitAll();
http.httpBasic(Customizer.withDefaults());
http.addFilterBefore(getExFilter(), BasicAuthenticationFilter.class);
http.authenticationProvider(pProvider);
return http.build();
}
// @Bean
// JdbcUserDetailsManager usersInMemory(DataSource datasource) {
// return new JdbcUserDetailsManager(datasource);
// }
@Bean
DaoAuthenticationProvider getDaoBean(CustomerSecurityService service,PasswordEncoder encoder) {
DaoAuthenticationProvider daoProvider= new DaoAuthenticationProvider();
daoProvider.setUserDetailsService(service);
daoProvider.setPasswordEncoder(encoder);
return daoProvider;
}
@Bean
PasswordEncoder encoder() {
return NoOpPasswordEncoder.getInstance();
}
}
- Implemented a PhraseAuthenticationToken which extends AbstractAuthenticationToken
- Implemented PhraseAuthenticationProvider as below
@Component
public class PhraseAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Authentication authenticated = new PhraseAuthenticationToken("abc@123", null,null);
return authenticated;
}
@Override
public boolean supports(Class<?> authentication) {
// TODO Auto-generated method stub
return PhraseAuthenticationToken.class.equals(authentication);
}
}
This issue I am facing is if I create the DaoAuthenticationProvider bean then its not registering my PhraseAuthenticationProvider. My PhraseAuthenticationProvider only works if comment out the DaoAuthenticationProvider bean. How can I register both my DaoAuthenticationProvider and PhraseAuthenticationProvider and make it work based on the header passed
CodePudding user response:
You have not registered the your both AuthenticationProvider DaoAuthenticationProvider and PhraseAuthenticationProvider. Follow these steps and make changes accordingly to register both the authentication providers
The custom authentication can be implemented in two-step:
- Creating custom authentication provider
- Registering that custom authentication provider
1. Creating the custom authentication provider
In first step of creating custom authentication provider, decide which kinds of Authentication objects the new AuthenticationProvider supports? Override the supports() method accordingly and
In the second step, provide the implementation of Authentication logic by overriding the authenticate() method.
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) {
// Writh the authentication logic here...
}
@Override
public boolean supports(Class<?> authenticationType) {
// return authenticationType.equals(authenticationToken.class);
}
}
2. Registering the Custom Authentication Provider
To plug in the custom implementation of the AuthenticationProvider, override the configure(AuthenticationManagerBuilder auth) method of the WebSecurityConfigurerAdapter class in the configuration class.
Note : To register the multiple AuthenticationProvider implementations, we can invoke the auth.authenticationProvider() for each provider separately.
@EnableWebSecurity
public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(customAuthenticationProvider);
//We can register as many providers as we may have
//auth.authenticationProvider(customProviderTwo);
//auth.authenticationProvider(customProviderThree);
}
// ...
}
CodePudding user response:
You can create authentication provider, which delegates the operation to another provider, based on some condition. A very simplified example:
@Component
public class DelegatingAuthenticationProvider implements AuthenticationProvider {
private final List<AuthenticationProvider> providers;
@Autowired
public DelegatingAuthenticationProvider(List<AuthenticationProvider> providers) {
this.providers = providers;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
for (AuthenticationProvider provider : this.providers) {
if (provider.supports(authentication.getClass())) {
return provider.authenticate(authentication);
}
}
return null;
}
@Override
public boolean supports(Class<?> authentication) {
return true;
}
}
Basically provide dao and phrase providers to this one, it will delegate the operation to one of them based on supports()
. You can check this question for information how to register your custom provider.
Tbh, registering both dao and phrase providers in the way mentioned in the above question will most probably achieve the same effect as this delegating provider, but i haven't tested it.
Edit: Didn't notice you are using the new way. According to what i see you are registering only the phrase provider. Did you try to register all providers? Like this, for example:
@Configuration
public class ProjectSecurityConfigurer {
@Autowired
private List<AuthenticationProvider> providers;
@Bean
SecurityFilterChain projectSecSpecs(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
.antMatchers("/myaccount").authenticated()
.antMatchers("/contact","/login").permitAll();
http.httpBasic(Customizer.withDefaults());
http.addFilterBefore(getExFilter(), BasicAuthenticationFilter.class);
for (AuthenticationProvider provider : this.providers) {
http.authenticationProvider(provider);
}
return http.build();
}
}
Autowiring a list of AuthenticationProvider
will pick up all beans of this type.