Почему я могу аутентифицироваться с помощью хэша пароля в Java Spring Security
Я использую spring-boot-starter-security
, Я настроил свой WebSecurityConfigation
использовать DaoAuthenticationProvider
поставщик и BCryptPasswordEncoder
для аутентификации. Так же UserDetailsService
реализация возвращает User
объект с password
поле установлено на фактический хеш.
Вроде нормально работает. Однако я заметил, что могу успешно пройти аутентификацию либо с помощью пароля, либо с помощью хэша.
Например, сам пароль является сгенерированным UUID 51a80a6a-8618-4583-98d2-d77d103a62c6
который был закодирован в $2a$10$u4OSZf7B9yJvQ5UYNNpy7O4f3g0gfUMl2Xmm3h282W.3emSN3WqxO
,
Полная настройка веб-безопасности:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DemoUserDetailsService userDetailsService;
@Autowired
private DaoAuthenticationProvider authenticationProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
auth.userDetailsService(userDetailsService);
auth.inMemoryAuthentication().withUser("user").password("password").roles("SUPER", "BASIC");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").hasRole("BASIC").and().httpBasic();
http.csrf().disable();
}
}
@Service
public class DemoUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
UserDAO userDAO = userRepository.findByEmailAndActivated(email);
if (userDAO == null) {
throw new UsernameNotFoundException(String.format("Email %s not found", email));
}
return new User(email, userDAO.getPasswordHash(), getGrantedAuthorities(email));
}
private Collection<? extends GrantedAuthority> getGrantedAuthorities(String email) {
return asList(() -> "ROLE_BASIC");
}
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder);
return authenticationProvider;
}
Почему я могу аутентифицироваться с обеими строками? Я делаю что-то не так, или это ожидается или какая-то конфигурация? Я не смог ничего найти в документах.
2 ответа
Я думаю, это происходит потому, что в соответствии с вашей конфигурацией вы получите два DaoAuthenticationProvider
s. Один явный, настроенный вами, а другой неявный, который настраивается под капотом, когда вы звоните auth.userDetailsService(userDetailsService);
и для этого неявного провайдера вы не устанавливаете кодировщик пароля.
Попробуйте это:
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
auth.inMemoryAuthentication().withUser("user").password("password").roles("SUPER", "BASIC");
}
И удалите своего провайдера, настроенного вручную - кажется, что он вам на самом деле не нужен.
Надеюсь, поможет.
Наилучшим подходом было бы подключить отладчик. Фактическая логика использования PasswordEncoder для сопоставления пароля находится в additionalAuthenticationChecks
метод DaoAuthenticationProvider
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
Object salt = null;
if (this.saltSource != null) {
salt = this.saltSource.getSalt(userDetails);
}
if (authentication.getCredentials() == null) {
logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), userDetails);
}
String presentedPassword = authentication.getCredentials().toString();
if (!passwordEncoder.isPasswordValid(userDetails.getPassword(), presentedPassword, salt)) {
logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), userDetails);
}
}
Это делегаты BCryptPasswordEncoder
public boolean matches(CharSequence rawPassword, String encodedPassword) {
if (encodedPassword == null || encodedPassword.length() == 0) {
logger.warn("Empty encoded password");
return false;
}
if (!BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
logger.warn("Encoded password does not look like BCrypt");
return false;
}
return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
}