it-swarm.com.ru

Spring Security 5: PasswordEncoder не сопоставлен для идентификатора "null"

Я перехожу с Spring Boot 1.4.9 на Spring Boot 2.0, а также на Spring Security 5, и я пытаюсь выполнить аутентификацию через OAuth 2. Но я получаю эту ошибку:

Java.lang.IllegalArgumentException: не существует PasswordEncoder, сопоставленного для идентификатора "null

Из документации Spring Security 5 я узнал, что формат хранения для пароля изменен.

В моем текущем коде я создал мой bean-кодировщик паролей как:

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

Однако это давало мне ошибку ниже:

Кодированный пароль не похож на BCrypt

Поэтому я обновляю кодировщик в соответствии с документом Spring Security 5 :

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

Теперь, если я вижу пароль в базе данных, он сохраняется как

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

С этой 1-й ошибкой, и теперь, когда я пытаюсь сделать аутентификацию, я получаю ошибку ниже:

Java.lang.IllegalArgumentException: не существует PasswordEncoder, сопоставленного для идентификатора "null

Чтобы решить эту проблему, я попробовал все нижеуказанные вопросы от Stackoverflow:

Вот вопрос, похожий на мой, но без ответа:

ПРИМЕЧАНИЕ. Я уже храню зашифрованный пароль в базе данных, поэтому нет необходимости снова кодировать в UserDetailsService.

В документации Spring security 5 они предложили обработать это исключение, используя:

DelegatingPasswordEncoder.setDefaultPasswordEncoderForMatches (PasswordEncoder)

Если это исправление, то где мне его поставить? Я пытался поместить его в бин PasswordEncoder, как показано ниже, но он не работал:

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

Класс 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();
    }
}

Конфигурация 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);
    }
}

Пожалуйста, ведите меня с этим вопросом. Я потратил часы, чтобы исправить это, но не смог исправить.

32
Jimmy

Когда вы конфигурируете ClientDetailsServiceConfigurer, вы также должны применить новый формат хранения пароля к секрету клиента.

.secret("{noop}secret")
53
Edwin Diaz-Mendez

Для тех, кто сталкивается с той же проблемой и не нуждается в безопасном решении - главным образом для тестирования и отладки - в памяти могут быть настроены пользователи.

Это просто для тренировки - никакой сценарий реального мира.

Подход, используемый ниже, не рекомендуется.

Вот откуда я это взял:


В вашем WebSecurityConfigurerAdapter добавьте следующее:

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

Здесь, очевидно, пароли хешируются, но все же доступны в памяти.


Конечно, вы также можете использовать реальный PasswordEncoder наподобие BCryptPasswordEncoder и ​​поставить перед паролем правильный идентификатор:

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

Добавьте .password("{noop}password") в файл настроек безопасности.

Например :

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

Всякий раз, когда Spring сохраняет пароль, он помещает префикс кодера в закодированные пароли, такие как bcrypt, scrypt, pbkdf2 и т.д., Чтобы, когда пришло время декодировать пароль, он мог использовать соответствующий кодер для декодирования. если в закодированном пароле нет префикса, он использует defaultPasswordEncoderForMatches. Вы можете просмотреть метод соответствия DelegatingPasswordEncoder.class, чтобы увидеть, как он работает. поэтому в основном нам нужно установить defaultPasswordEncoderForMatches в следующих строках.

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

Теперь вам также может понадобиться предоставить этому кодировщику DefaultPasswordEncoderForMatches вашему провайдеру аутентификации. Я сделал это с помощью строк ниже в моих классах конфигурации.

@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

Относительно

Закодированный пароль не похож на BCrypt

В моем случае было несоответствие в силе BCryptPasswordEncoder, используемой конструктором по умолчанию (10), поскольку хэш pwd был создан с силой 4. Поэтому я установил силу явно.

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

также моя версия Spring Security 5.1.6, и она отлично работает с BCryptPasswordEncoder

0
Bender