Я перехожу с Spring Boot 1.4.9 на Spring Boot 2.0, а также на Spring Security 5, и я пытаюсь выполнить аутентификацию через OAuth 2. Но я получаю эту ошибку:
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null
Из документации Весенняя безопасность 5 я узнал, что изменен формат хранения пароля.
В моем текущем коде я создал bean-компонент кодировщика паролей как:
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
Однако это давало мне ошибку ниже:
Encoded password does not look like BCrypt
Поэтому я обновляю кодировщик в соответствии с документом Весенняя безопасность 5, чтобы:
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
Теперь, если я вижу пароль в базе данных, он хранится как
{bcrypt}$2a$10$LoV/3z36G86x6Gn101aekuz3q9d7yfBp3jFn7dzNN/AL5630FyUQ
Эта первая ошибка исчезла, и теперь, когда я пытаюсь выполнить аутентификацию, я получаю ошибку ниже:
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null
Чтобы решить эту проблему, я попробовал все следующие вопросы из Stackoverflow:
Вот вопрос, похожий на мой, но без ответа:
ПРИМЕЧАНИЕ. Я уже храню зашифрованный пароль в базе данных, поэтому нет необходимости повторно кодировать в UserDetailsService.
В документации Весенняя безопасность 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);
}
}
Пожалуйста, помогите мне решить эту проблему. У меня есть часы, чтобы исправить это, но я не могу исправить.
Я считаю, что проблема связана с клиентом (а не с отдельными учетными записями пользователей). Я лично уже кодировал данные о пользователе, но не о клиенте. Теперь ожидается, что пользователи OAuth2 будут кодировать секрет клиент (а также пароли пользователей). В частности, либо установите passwordEncoder на ClientDetailsServiceConfigurer, либо префикс секрета с помощью {noop}. Надеюсь, что это имеет смысл и кому-то поможет.
Эта проблема была решена для меня, запустив mvn clean package. Должно быть какая-то проблема с кешированием.




Когда вы настраиваете ClientDetailsServiceConfigurer, вы также должны применить новый формат хранения паролей к секрету клиента.
.secret("{noop}secret")
см. также Соответствие пароля и Формат хранения паролей в Spring Security 5.0.0.RC1 Выпущенные примечания
также можно добавить secret(passwordEncoder.encode("secret")), используя кодировщик пароля по умолчанию.
что, если я не использую секрет?
также: auth.inMemoryAuthentication () .withUser ("admin"). roles ("ADMIN"). password ("{noop} password") ;
Для всех, кто сталкивается с той же проблемой и не нуждается в безопасном решении - в основном для тестирования и отладки - пользователи по-прежнему могут быть настроены в памяти.
Это просто для игры - никакого сценария из реального мира.
Используемый ниже подход устарел.
Вот откуда я это взял:
В ваш 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));
Привет, на самом деле мы не можем использовать NoOpPasswordEncoder. Поскольку это устарело в новой версии Spring-Security. И согласно документации по безопасности Spring (spring.io/blog/2017/11/01/…), даже если мы используем NoOpPasswordEncoder, мы должны добавить {noop} в качестве идентификатора к паролю и секрету. Я считаю, что это не оправдывает мой вопрос. Основная проблема со всеми решениями заключается в том, что они не упоминают, что вам также нужно добавить идентификатор к вашему секрету.
Да, это не рекомендуется. Так же и мой источник. Если просто использовать NoOpPasswordEncoder - без BCryptPasswordEncoder - работает. Я использую Spring boot 2.0.1.RELEASE.
Но, как я уже упоминал в ответе, это вообще не производственный сценарий.
Я не понимаю, почему мы должны использовать устаревший класс даже для целей тестирования?
Хорошо, вы совершенно правы. Я добавил этот ответ для всех, кто просто играет и начинает погружаться в этот вопрос, возможно, используя устаревшее руководство. Вот почему я также упомянул BCryptPasswordEncoder. Я обновил свой ответ.
Ответ помог мне со сценарием тестирования, спасибо
Вот почему я так сказал в начале ответа.
Добавьте .password("{noop}password") в файл конфигурации безопасности.
Например :
auth.inMemoryAuthentication()
.withUser("admin").roles("ADMIN").password("{noop}password");
скажите, почему вы добавили {noop}?
Просто хочу использовать простой пароль ...! Как будто с этим не нужно проводить никаких операций! Я думаю так! ;)
Касательно
Encoded password does not look like BCrypt
В моем случае было несоответствие в силе BCryptPasswordEncoder, используемой конструктором по умолчанию (10), поскольку хэш pwd был сгенерирован с силой 4. Итак, я установил силу явным образом.
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(4);
}
также моя версия Spring Security - 5.1.6, и она отлично работает с BCryptPasswordEncoder
Всякий раз, когда 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;
}
Не знаю, поможет ли это кому-нибудь. Мой рабочий код WebSecurityConfigurer и OAuth2Config, как показано ниже:
Файл OAuth2Config:
package com.crown.AuthenticationServer.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
@Configuration
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("crown")
.secret("{noop}thisissecret")
.authorizedGrantTypes("refresh_token", "password", "client_credentials")
.scopes("webclient", "mobileclient");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
}
WebSecurityConfigurer:
package com.crown.AuthenticationServer.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
@Override
public UserDetailsService userDetailsService() {
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
final User.UserBuilder userBuilder = User.builder().passwordEncoder(encoder::encode);
UserDetails user = userBuilder
.username("john.carnell")
.password("password")
.roles("USER")
.build();
UserDetails admin = userBuilder
.username("william.woodward")
.password("password")
.roles("USER","ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
}
Вот ссылка на проект: Springboot-авторизация-сервер-oauth2
Вы можете прочитать в официальной документации по безопасности Spring, что для DelegatingPasswordEncoder общий формат пароля: {id} encodedPassword
Such that id is an identifier used to look up which PasswordEncoder should be used and encodedPassword is the original encoded password for the selected PasswordEncoder. The id must be at the beginning of the password, start with { and end with }. If the id cannot be found, the id will be null. For example, the following might be a list of passwords encoded using different id. All of the original passwords are "password".
Примеры идентификаторов:
{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG {noop}password {pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc {scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=
{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0
Эта проблема связана с тем, что нам также нужен тип шифрования в секрете клиента. На данный момент к зашифрованному паролю уже добавлен тип шифрования.
Если вы извлекаете имя пользователя и пароль из базы данных, вы можете использовать приведенный ниже код, чтобы добавить экземпляр NoOpPassword.
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(adm).passwordEncoder(NoOpPasswordEncoder.getInstance());
}
Где адм - это настраиваемый пользовательский объект для моего проекта, в котором есть методы getPassword () и getUsername ().
Также помните, что для создания пользовательского POJO пользователя вам необходимо реализовать интерфейс UserDetails и реализовать все его методы.
Надеюсь это поможет.
Я немного описал проблему. Когда я перешел с Spring security 4 на 5. Я получал первую ошибку, а затем решил ее, изменив свой генератор паролей. Он начал выдавать мне вторую ошибку. И сообщения об ошибках разные. 1) Закодированный пароль не похож на BCrypt и 2) java.lang.IllegalArgumentException: нет PasswordEncoder, сопоставленного с идентификатором "null. Вторая проблема, которая у меня есть в настоящее время.