76 votes

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

Je suis en train de migrer de Spring Boot 1.4.9 vers Spring Boot 2.0 et aussi vers Spring Security 5 et j'essaie de m'authentifier via OAuth 2. Mais j'obtiens cette erreur :

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

D'après la documentation de Sécurité du printemps 5 j'ai appris que le format de stockage du mot de passe a été modifié.

Dans mon code actuel, j'ai créé mon bean d'encodage de mot de passe comme :

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

Cependant, il m'a donné l'erreur suivante :

Le mot de passe codé ne ressemble pas à BCrypt

J'ai donc mis à jour l'encodeur selon le Sécurité du printemps 5 document à :

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

Maintenant, si je peux voir le mot de passe dans la base de données, il est stocké en tant que

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

Avec cette 1ère erreur disparue et maintenant quand j'essaie de faire l'authentification j'obtiens l'erreur suivante :

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 sans réponse :

REMARQUE : Je stocke déjà le mot de passe crypté dans la base de données, il n'est donc pas nécessaire de le coder à nouveau dans la base de données. UserDetailsService .

Dans le Sécurité du printemps 5 Ils ont suggéré que vous pouvez gérer cette exception en utilisant la documentation :

DelegatingPasswordEncoder.setDefaultPasswordEncoderForMatches(PasswordEncoder)

Si c'est le remède, où dois-je le mettre ? J'ai essayé de le mettre dans PasswordEncoder haricot comme ci-dessous mais ça ne marchait pas :

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

Classe MyWebSecurity

@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 de 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 m'aider à résoudre ce problème. J'ai passé des heures à le résoudre mais je n'y suis pas parvenu.

0 votes

J'ai été un peu plus descriptif du problème. Lorsque je suis passé de Spring security 4 à 5, j'ai eu une première erreur, puis je l'ai résolue en changeant mon générateur de mot de passe, et j'ai commencé à avoir une deuxième erreur. Et les messages d'erreur sont différents. 1) Le mot de passe codé ne ressemble pas à BCrypt et 2) java.lang.IllegalArgumentException : Il n'y a pas de PasswordEncoder mapped pour l'id "null". Deuxième problème que je rencontre actuellement.

3 votes

Je pense que le problème se situe au niveau du client (et non des comptes utilisateurs individuels). Personnellement, je codais déjà les détails de l'utilisateur, mais pas le client. Maintenant, les utilisateurs d'OAuth2 sont censés encoder les données de l'utilisateur. client secret (ainsi que les mots de passe des utilisateurs). Plus précisément, il faut soit définir l'option passwordEncoder sur le ClientDetailsServiceConfigurer ou préfixe le secret avec {noop}. J'espère que cela aura un sens et aidera quelqu'un.

0 votes

Ce problème a été résolu pour moi en exécutant mvn clean package . Il doit y avoir un problème avec la mise en cache.

99voto

Edwin Diaz-Mendez Points 868

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

.secret("{noop}secret")

0 votes

Voir aussi Correspondance des mots de passe y Format de stockage des mots de passe dans Remarques sur la publication de Spring Security 5.0.0.RC1

1 votes

Peut également ajouter le secret(passwordEncoder.encode("secret")) en utilisant le codeur de mot de passe par défaut.

3 votes

Que faire si je n'utilise pas de secret ?

38voto

Lokesh Goud Points 32

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

Par exemple :

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

1 votes

Pouvez-vous me dire pourquoi vous avez ajouté {noop} ?

3 votes

Je veux juste utiliser un mot de passe simple... ! Comme si aucune opération ne devait être effectuée sur cela ! Je pense que oui ! ;)

20voto

rocksteady Points 1035

Pour tous ceux qui sont confrontés au même problème et qui n'ont 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.

C'est juste pour jouer - pas de scénario réel.

L'approche utilisée ci-dessous est dépréciée.

C'est de là que je tiens ça :


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 ils sont toujours disponibles en mémoire.


Bien sûr, vous pouvez aussi utiliser un véritable PasswordEncoder comme BCryptPasswordEncoder et préfixez le mot de passe avec l'identifiant correct :

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

3 votes

Bonjour, en fait nous ne pouvons pas utiliser NoOpPasswordEncoder. Comme il est déprécié dans la nouvelle version de spring-security. Et selon la documentation de spring security ( spring.io/blog/2017/11/01/… ) même si nous utilisons NoOpPasswordEncoder nous devons ajouter {noop} comme id au mot de passe et au secret. Je pense que cela ne justifie pas ma question. Le problème principal avec toutes les solutions est qu'elles ne mentionnent pas que vous devez ajouter un ID à votre secret aussi.

0 votes

Oui, il est déprécié. Ma source le dit aussi. Si vous utilisez simplement NoOpPasswordEncoder - sans BCryptPasswordEncoder - cela fonctionne. J'utilise Spring Boot 2.0.1.RELEASE .

0 votes

Mais, comme je l'ai mentionné dans la réponse, ce n'est pas du tout un scénario de production.

2voto

Vikky Points 667

Chaque fois que Spring stocke le mot de passe, il met un préfixe d'encodeur dans les mots de passe encodés comme bcrypt, scrypt, pbkdf2 etc. de sorte que lorsqu'il est temps de décoder le mot de passe, il peut utiliser l'encodeur approprié pour décoder. S'il n'y a pas de préfixe dans le mot de passe encodé, il utilise defaultPasswordEncoderForMatches. Vous pouvez consulter la méthode matches de DelegatingPasswordEncoder.class pour voir comment cela fonctionne. Donc, fondamentalement, nous devons définir defaultPasswordEncoderForMatches par 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 aussi fournir cet encodeur avec DefaultPasswordEncoderForMatches à votre fournisseur d'authentification. 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;
    }

0voto

Bender Points 323

Concernant

Le mot de passe codé ne ressemble pas à BCrypt

Dans mon cas, il y avait un décalage dans la force de BCryptPasswordEncoder utilisé par le constructeur par défaut (10) comme pwd hash a été généré avec la force 4. Donc, j'ai mis la force explicite.

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

De plus, ma version de Spring Security est 5.1.6 et elle fonctionne parfaitement avec BCryptPasswordEncoder.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X