Spring Security Как получить username из PasswordEncoder?
Во время добавления нового юзера, я сохраняю его пароль в базе, в виде SHA256(cleartext_password + salt), значение salt сохраняю рядом в отдельную колонку для каждого юзера. Соответственно, проверка пароля должна выполняться следующим образом: вытаскиваем клеартекст пароля из BasicAuth заголовка, достаем из БД значение salt для этого логина, применяем SHA256(cleartext_password + salt), проверяем, что получившаяся строка и строка, хранящаяся в БД совпали. Для того чтобы это реализовать, я хочу создать костомный PasswordEncoder, в котором буду выполнять проверку, но в него Не приходит username, по которому я мог бы вытащить salt для этого юзера, которая нужна для проверки. Как можно выйти из этой ситуации?
Вот текущая реализации Security:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsService customUserDetailsService;
public SecurityConfig(UserDetailsService customUserDetailsService) {
this.customUserDetailsService = customUserDetailsService;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
.anyRequest().authenticated()
.and().httpBasic();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService)
.passwordEncoder(new CustomPasswordEncoder());
}
}
@Component
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUserName(username);
if (user != null) {
return org.springframework.security.core.userdetails.
User.withUsername(username)
.password(new CustomPasswordEncoder().encode(user.getPassword()))
.roles("USER")
.build();
}
throw new IllegalArgumentException();
}
}
public class CustomPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
return rawPassword.toString();
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
String sha256hex = DigestUtils.sha256Hex(rawPassword.toString()); // Здеь должно быть DigestUtils.sha256Hex(rawPassword.toString() + salt)
return sha256hex.equals(encodedPassword);
}
}
Ответы (1 шт):
Можно написать свой AuthenticationProvider
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
private final UserDetailsService customUserDetailsService;
public AuthenticationProvider(UserDetailsService customUserDetailsService) {
this.customUserDetailsService = customUserDetailsService;
}
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
// тут идет вся логика с обращением в customUserDetailsService,
// проверка пароля и т.д.
...
// Для примера, если авторизация не удалась - вернуть null
// name и password тут не обязательно передавать.
// Токен нужен для дальнейших проверок, если они конечно есть.
// Смотри javadoc этого конструктора.
return new UsernamePasswordAuthenticationToken(
name, password, new ArrayList<>());
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
Далее нужно передать его в AuthenticationManagerBuilder:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final CustomAuthenticationProvider authProvider;
public SecurityConfig(CustomAuthenticationProvider authProvider) {
this.authProvider = authProvider;
}
...
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
}