web-dev-qa-db-fra.com

Spring Security 5: il n'y a pas de PasswordEncoder mappé pour l'id "null"

Je migre de Spring Boot 1.4.9 vers Spring Boot 2.0 et également vers Spring Security 5 et j'essaie de m'authentifier via OAuth 2. Mais je reçois le message d'erreur suivant:

Java.lang.IllegalArgumentException: il n'y a pas de PasswordEncoder mappé pour l'id "null

De la documentation de Spring Security 5 , je découvre que le format de stockage du mot de passe est modifié.

Dans mon code actuel, j'ai créé mon bean encoder de mot de passe en tant que:

@Bean
public BCryptPasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

Cependant il me donnait l'erreur ci-dessous:

Le mot de passe codé ne ressemble pas à BCrypt

Je mets donc à jour le codeur comme indiqué dans le document Spring Security 5 :

@Bean
public PasswordEncoder passwordEncoder() {
    return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

Maintenant, si je peux voir le mot de passe dans la base de données, il le stocke comme

{bcrypt}$2a$10$LoV/3z36G86x6Gn101aekuz3q9d7yfBp3jFn7dzNN/AL5630FyUQ

Avec cette première erreur disparue et maintenant, lorsque j'essaie de faire de l'authentification, j'obtiens une erreur:

Java.lang.IllegalArgumentException: il n'y a pas de PasswordEncoder mappé pour l'id "null

Pour résoudre ce problème, j'ai essayé toutes les questions ci-dessous de Stackoverflow:

Voici une question similaire à la mienne mais pas répondue:

REMARQUE: Je suis déjà en train de stocker un mot de passe crypté dans la base de données, il n'est donc pas nécessaire de recoder dans UserDetailsService.

Dans la documentation Spring security 5 , ils ont suggéré que vous puissiez gérer cette exception en utilisant:

DelegatingPasswordEncoder.setDefaultPasswordEncoderForMatches (PasswordEncoder)

Si tel est le correctif alors où devrais-je le mettre? J'ai essayé de le mettre dans PasswordEncoder haricot comme ci-dessous mais cela ne fonctionnait pas:

DelegatingPasswordEncoder def = new DelegatingPasswordEncoder(idForEncode, encoders);
def.setDefaultPasswordEncoderForMatches(passwordEncoder);

MyWebSecurity class

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    public void configure(WebSecurity web) throws Exception {

        web
                .ignoring()
                .antMatchers(HttpMethod.OPTIONS)
                .antMatchers("/api/user/add");
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

Configuration MyOauth2

@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Bean
    public TokenStore tokenStore() {
        return new InMemoryTokenStore();
    }

    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;


    @Bean
    public TokenEnhancer tokenEnhancer() {
        return new CustomTokenEnhancer();
    }

    @Bean
    public DefaultAccessTokenConverter accessTokenConverter() {
        return new DefaultAccessTokenConverter();
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints)
            throws Exception {
        endpoints
                .tokenStore(tokenStore())
                .tokenEnhancer(tokenEnhancer())
                .accessTokenConverter(accessTokenConverter())
                .authenticationManager(authenticationManager);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients
                .inMemory()
                .withClient("test")
                .scopes("read", "write")
                .authorities(Roles.ADMIN.name(), Roles.USER.name())
                .authorizedGrantTypes("password", "refresh_token")
                .secret("secret")
                .accessTokenValiditySeconds(1800);
    }
}

Veuillez me guider avec ce problème. J'ai passé des heures à résoudre ce problème, mais pas en mesure de le réparer.

32
Jimmy

Lorsque vous configurez le ClientDetailsServiceConfigurer, vous devez également appliquer le nouveau format de stockage du mot de passe au secret client.

.secret("{noop}secret")
53

Pour les personnes confrontées au même problème et n'ayant pas besoin d'une solution sécurisée - pour les tests et le débogage principalement -, les utilisateurs en mémoire peuvent toujours être configurés.

Ceci est juste pour jouer - pas de scénario du monde réel.

L'approche utilisée ci-dessous est obsolète.

C'est de là que je l'ai eu:


Dans votre WebSecurityConfigurerAdapter ajoutez ce qui suit:

@SuppressWarnings("deprecation")
@Bean
public static NoOpPasswordEncoder passwordEncoder() {
return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
}

Ici, évidemment, les mots de passe sont hachés, mais restent disponibles en mémoire.


Bien sûr, vous pouvez aussi utiliser une vraie PasswordEncoder comme BCryptPasswordEncoder et préfixer le mot de passe avec le bon identifiant:

// Create an encoder with strength 16
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);
String result = encoder.encode("myPassword");
assertTrue(encoder.matches("myPassword", result));
7
rocksteady

Ajoutez .password("{noop}password") au fichier de configuration de la sécurité.

Par exemple :

auth.inMemoryAuthentication()
        .withUser("admin").roles("ADMIN").password("{noop}password");
4

Chaque fois que Spring stocke le mot de passe, il insère un préfixe d'encodeur dans les mots de passe encodés tels que bcrypt, scrypt, pbkdf2, etc. s'il n'y a pas de préfixe dans le mot de passe codé, il utilise defaultPasswordEncoderForMatches. Vous pouvez afficher la méthode des correspondances de DelegatingPasswordEncoder.class pour voir comment elle fonctionne. Nous avons donc besoin de définir defaultPasswordEncoderForMatches avec les lignes suivantes.

@Bean(name="myPasswordEncoder")
public PasswordEncoder getPasswordEncoder() {
        DelegatingPasswordEncoder delPasswordEncoder=  (DelegatingPasswordEncoder)PasswordEncoderFactories.createDelegatingPasswordEncoder();
        BCryptPasswordEncoder bcryptPasswordEncoder =new BCryptPasswordEncoder();
    delPasswordEncoder.setDefaultPasswordEncoderForMatches(bcryptPasswordEncoder);
    return delPasswordEncoder;      
}

Maintenant, vous devrez peut-être également fournir à votre fournisseur d'authentification cet encodeur avec DefaultPasswordEncoderForMatches. Je l'ai fait avec les lignes ci-dessous dans mes classes de configuration.

@Bean
    @Autowired  
    public DaoAuthenticationProvider getDaoAuthenticationProvider(@Qualifier("myPasswordEncoder") PasswordEncoder passwordEncoder, UserDetailsService userDetailsServiceJDBC) {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
        daoAuthenticationProvider.setUserDetailsService(userDetailsServiceJDBC);
        return daoAuthenticationProvider;
    }
1
Vikky

En ce qui concerne

Le mot de passe encodé ne ressemble pas à BCrypt

Dans mon cas, il y avait une incompatibilité dans BCryptPasswordEncoder utilisé par le constructeur par défaut (10), car pwd hash a été généré avec la force 4. J'ai donc défini la force explicitement.

@Bean
public BCryptPasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder(4);
}

ma version de Spring Security est également 5.1.6 et fonctionne parfaitement avec BCryptPasswordEncoder

0
Bender