Tuomas Toivonen
Tuomas Toivonen

Reputation: 23502

Spring authentication performed twice, first succeeds and then fails

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

Answers (1)

Chao Luo
Chao Luo

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

Related Questions