web-dev-qa-db-fra.com

Custom Authentication Manager avec Spring Security et configuration Java

J'utilise Spring Security avec SpringMVC pour créer une application Web (je l'appellerai WebApp par souci de clarté) qui concerne une application existante (je l'appellerai BackendApp).

Je souhaite déléguer les responsabilités d'authentification à BackendApp (afin que je n'ai pas besoin de synchroniser les deux applications). 

Pour implémenter cela, je voudrais que la WebApp (running spring security) communique avec BackendApp via REST avec le nom d'utilisateur et le mot de passe fournis par l'utilisateur dans un formulaire et s'authentifie selon que la réponse de BackendApp est 200 OK ou 401. Non autorisé.

Je comprends que j’aurai besoin d’écrire un gestionnaire d’authentification personnalisé pour le faire, mais je suis très nouveau au printemps et je ne trouve aucune information sur la façon de le mettre en œuvre.

Je crois que je devrai faire quelque chose comme ceci:

public class CustomAuthenticationManager implements AuthenticationManager{

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

        String username = authentication.getName();
        String pw       = authentication.getCredentials().toString();

        // Code to make rest call here and check for OK or Unauthorised.
        // What do I return?

    }

}

Dois-je définir authentication.setAuthenticated (true) en cas de succès et false en cas contraire et c'est tout?

Une fois cela écrit, comment configurer la sécurité de ressort pour utiliser ce gestionnaire d'authentification à l'aide d'un fichier de configuration Java?

Merci d'avance pour toute aide.

20
Jeff I

Jetez un oeil à mon échantillon ci-dessous. Vous devez renvoyer un UsernamePasswordAuthenticationToken. Il contient le principal et les GrantedAuthorities. J'espère pouvoir aider :)

public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    String username = authentication.getPrincipal() + "";
    String password = authentication.getCredentials() + "";

    User user = userRepo.findOne(username);
    if (user == null) {
        throw new BadCredentialsException("1000");
    }
    if (user.isDisabled()) {
        throw new DisabledException("1001");
    }
    if (!encoder.matches(password, user.getPassword())) {
        throw new BadCredentialsException("1000");
    }
    List<Right> userRights = rightRepo.getUserRights(username);
    return new UsernamePasswordAuthenticationToken(username, password, userRights.stream().map(x -> new SimpleGrantedAuthority(x.getName())).collect(Collectors.toList()));
}

PS: userRepo et rightRepo sont des référentiels Spring-Data-JPA qui accèdent à ma base de données utilisateur personnalisée.

SpringSecurity JavaConfig:

@Configuration
@EnableWebMvcSecurity
public class MySecurityConfiguration extends WebSecurityConfigurerAdapter {

public MySecurityConfiguration() {
    super(false);
}

@Override
protected AuthenticationManager authenticationManager() throws Exception {
    return new ProviderManager(Arrays.asList((AuthenticationProvider) new AuthProvider()));
}

}
30

Dans sa plus simple:

@Override
    public Authentication authenticate(Authentication auth) throws AuthenticationException {
        String username = auth.getName();
        String password = auth.getCredentials().toString();
        // to add more logic
        List<GrantedAuthority> grantedAuths = new ArrayList<>();
        grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
        return new UsernamePasswordAuthenticationToken(username, password, grantedAuths);
    }
5
mel3kings

Vous devez d’abord configurer la sécurité de Spring pour utiliser votre AuthenticationProvider personnalisé . Ainsi, dans votre fichier spring-security.xml (ou un fichier de configuration équivalent), vous devez définir quelle classe implémentera cette fonctionnalité. Par exemple:

<authentication-manager alias="authenticationManager">
    <authentication-provider ref="myAuthenticationProvider" />
</authentication-manager>

<!-- Bean implementing AuthenticationProvider of Spring Security -->
<beans:bean id="myAuthenticationProvider" class="com.teimas.MyAutenticationProvider">
</beans:bean>

Deuxièmement, vous devez implémenter AuthenticationProvider comme dans votre exemple. Spécialement la méthode authenticate (Authentication Authentication) dans laquelle votre appel restant doit être. Par exemple:

public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    User user = null;
    try {
        //use a rest service to find the user. 
        //Spring security provides user login name in authentication.getPrincipal()
            user = userRestService.loadUserByUsername(authentication.getPrincipal().toString());
    } catch (Exception e) {
        log.error("Error loading user, not found: " + e.getMessage(), e);
    }

    if (user == null) {
        throw new UsernameNotFoundException(String.format("Invalid credentials", authentication.getPrincipal()));
    } else if (!user.isEnabled()) {
        throw new UsernameNotFoundException(String.format("Not found enabled user for username ", user.getUsername()));
    }
    //check user password stored in authentication.getCredentials() against stored password hash
    if (StringUtils.isBlank(authentication.getCredentials().toString())
        || !passwordEncoder.isPasswordValid(user.getPasswordHash(), authentication.getCredentials().toString()) {
        throw new BadCredentialsException("Invalid credentials");
    }

    //doLogin makes whatever is necesary when login is made (put info in session, load other data etc..)
    return doLogin(user);
} 
3
Ricardo Vila

Ma solution est presque la même que la première réponse:

1) Vous avez besoin d'une classe qui implémente le fournisseur d'authentification

@Service
@Configurable
public class CustomAuthenticationProvider implements AuthenticationProvider    {
      @Override
      public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    // Your code of custom Authentication
}
}

2) Contrairement à la première réponse, vous n'avez pas devez disposer du code suivant dans votre WebSecurityConfiguration si vous ne disposez que de ce fournisseur personnalisé.

@Override
protected AuthenticationManager authenticationManager() throws Exception {
     return new ProviderManager(Arrays.asList((AuthenticationProvider) new  AuthProvider()));
}

Le problème est que Spring recherche les fournisseurs disponibles et utilise la valeur par défaut si rien d’autre n’est trouvé. Mais comme vous avez l'implémentation de AuthenticationProvider, votre implémentation sera utilisée. 

0
Andrew Gans