Reputation: 23502
I'm trying to wrap my head around Spring Security framework, and implement authentication using custom AuthenticationProvider
When I navigate to secured url and form login with correct credentials, two login attempts are made. First succeeds and second fails, and browser stays at login page without error message.
Here is my security configuration.
package training2;
{ imports... }
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
@Qualifier("myProvider")
private AuthenticationProvider provider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(provider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll()
.and()
.logout()
.permitAll();
}
@Override
public UserDetailsService userDetailsServiceBean() throws Exception {
// TODO Auto-generated method stub
return super.userDetailsServiceBean();
}
@Override
@Bean
protected UserDetailsService userDetailsService() {
return new UserDetailsService() {
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
System.out.println("loadUserByUsername");
return new UserDetails() {
private static final long serialVersionUID = -1044116648365271684L;
public boolean isEnabled() {
// TODO Auto-generated method stub
return true;
}
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return true;
}
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return true;
}
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return true;
}
public String getUsername() {
// TODO Auto-generated method stub
return username;
}
public String getPassword() {
// TODO Auto-generated method stub
return "asdf";
}
public Collection<? extends GrantedAuthority> getAuthorities() {
// TODO Auto-generated method stub
return null;
}
};
}
};
}
@Bean
@Qualifier("myProvider")
public AuthenticationProvider myProvider(final UserDetailsService userDetailsService) {
return new AuthenticationProvider() {
public boolean supports(Class<?> authentication) {
return true;
}
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
System.out.println("authenticate");
UserDetails user = userDetailsService.loadUserByUsername(authentication.getName());
if (user != null && user.getPassword().equals(authentication.getCredentials())) {
System.out.println("authentication ok");
return authentication;
} else {
System.out.println("authentication failed");
throw new AuthenticationException(null) {
private static final long serialVersionUID = -1022654748424786317L;
};
}
}
};
}
}
And following is printed out in the console, when attempting to login with valid credentials
authenticate
loadUserByUsername
authentication ok
authenticate
loadUserByUsername
authentication failed
When attempting to login with invalid credentials (password anything other than asdf), authentication is performed only once, and fails as it should.
Why is the authentication performed twice on form login, first succeeding and then failing? Is something misconfigured, or am I missing some bean? Also, should I manage the SecurityContext
manually, when using custom AuthenticationProvider
implementation, or does Spring still manages it and where?
I'm also confused, why I have to explicitly annotate userDetailsService
method as a @Bean
to make is Spring managed, even though WebSecurityConfigurer
declares this method.
Upvotes: 4
Views: 3891
Reputation: 2696
.anyRequest().authenticated()
So it will check whether the authentication is authenticated in FilterSecurityInterceptor
, but the authentication returned in your provider is not authenticated, FilterSecurityInterceptor
will do authenticate
again.
if (user != null && user.getPassword().equals(authentication.getCredentials())) {
System.out.println("authentication ok");
// should authenticated authenticaiton
//return new UsernamePasswordAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities)
}
Upvotes: 3