11

I am trying to setup a REST based web application, where the frontend is using Reactjs and the backend is using Spring Boot. I am also trying to setup a custom authentication provider, and this is where my problems start. When trying to test the login API call, the CustomAuthenticationProvider is never called, and instead the default DaoAuthenticationProvider is used. This causes the login to report "Bad credentials".

I have upload a small sample application to github: spring-boot-auth-demo

To test the login API I use the following curl:

curl -H "Content-Type: application/json" -X POST -d '{"username":"admin","password":"admin"}' http://localhost:8080/api/users/login

The CustomAuthenticationProvider does a simple username/password check and returns an UsernamePasswordAuthenicationToken object.

package no.bluebit.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

private static final Logger logger =     LoggerFactory.getLogger(CustomAuthenticationProvider.class);

public CustomAuthenticationProvider() {
    logger.info("*** CustomAuthenticationProvider created");
}

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {

    if(authentication.getName().equals("admin")  && authentication.getCredentials().equals("admin")) {
        List<GrantedAuthority> grantedAuths = new ArrayList<>();
        grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
        grantedAuths.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
        return new UsernamePasswordAuthenticationToken(authentication.getName(), authentication.getCredentials(), grantedAuths);
    } else {
        return null;
    }

}

@Override
public boolean supports(Class<?> authentication) {
    return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}

}

The CustomAuthenticationProvider is wired up using the SecurityConfiguration class. When stepping through the code, I can see that the CustomAuthenicationProvider is not in the list of providers used to authenticate the incoming request.

package no.bluebit.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    private CustomAuthenticationProvider customAuthenticationProvider;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .authenticationProvider(this.customAuthenticationProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/api/users/login").permitAll()    // Permit access for all to login REST service
                .antMatchers("/").permitAll()                   // Neccessary to permit access to default document
            .anyRequest().authenticated().and()                 // All other requests require authentication
            .httpBasic().and()
            .logout().and()
            .csrf().disable();
    }
}

Why is this not working?

user207421
  • 305,947
  • 44
  • 307
  • 483
Håvard Bakke
  • 259
  • 1
  • 4
  • 10
  • 1
    Take a look at this: http://stackoverflow.com/questions/22453550/custom-authentication-provider-not-being-called/22457561#22457561 – franDayz Apr 20 '16 at 13:13
  • 1
    Thank you! The missing @Autowired annotation was the issue. Problem solved! – Håvard Bakke Apr 21 '16 at 02:18
  • @franDayz maybe add your comment as an answer, so that Håvard Bakke can accept as an answer? – demaniak Dec 13 '16 at 09:30
  • @demaniak I just tried but the system converts simple answers to comments... – franDayz Dec 13 '16 at 11:19
  • @franDayz just mention, why '@autowired' was required and follow up with the URL then you can add it as a answer :) – rohit thomas May 20 '18 at 02:53
  • Duplicate of http://stackoverflow.com/questions/22453550/custom-authentication-provider-not-being-called/22457561#22457561 – user207421 May 20 '18 at 03:27
  • Does this answer your question? [Custom Authentication provider with Spring Security and Java Config](https://stackoverflow.com/questions/22606751/custom-authentication-provider-with-spring-security-and-java-config) – Vikash Gupta Apr 16 '20 at 16:16

3 Answers3

0

Try to add on header http this thinks:

Example:

const headers = new HttpHeaders();
headers.set('Access-Control-Allow-Origin', '*');
headers.set('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, DELETE, PUT');
headers.set('Access-Control-Allow-Headers', 'Authorization, Content-Type, Accept, x- 
requested-with, Cache-Control');
headers.set('Content-Type', 'application/json');


this.http.post('http://localhost:8081/loginAngular',
   JSON.stringify({user: 'sdasd', password: 'dasdasd', estado: 'dasdasd', idUsuario: 1, resultado: 'asdasd'}) ,
  {headers: new HttpHeaders().set('Content-Type', 'application/json')}).subscribe(data => {
  console.log(' Data: ' + data);

});

I made this app with spring security and angular! Front: https://github.com/nicobassart/angularforHidra Back: https://github.com/nicobassart/hidra_server

Nico
  • 1
  • 1
-2

Look at the AuthenticationProvider class (respectively it's java doc)

The method authenticate expects to :

 * Performs authentication with the same contract as
 * {@link org.springframework.security.authentication.AuthenticationManager#authenticate(Authentication)}
 * @return a fully authenticated object including credentials. May return
 * <code>null</code> if the <code>AuthenticationProvider</code> is unable to support
 * authentication of the passed <code>Authentication</code> object. In such a case,
 * the next <code>AuthenticationProvider</code> that supports the presented
 * <code>Authentication</code> class will be tried.

If you return null, then the next AuthenticationProvider will be called, which is the defaut one.

I am not sure this is the problem, but this might be something. Try to throw BadCredentialsException, as the AuthenticationManager class tells you to do :

 * <li>A {@link BadCredentialsException} must be thrown if incorrect credentials are
 * presented. Whilst the above exceptions are optional, an
 * <code>AuthenticationManager</code> must <B>always</B> test credentials.</li>
-2

You have to set the credentials in some other way. Try to see a working example of Username Password token. But your "authenticate" function needs to be the one that sets the credentials.

Dexter
  • 6,170
  • 18
  • 74
  • 101