Базовая аутентификация с помощью хэша пароля?
Суть такова. Клиент android по запросу get получает данные с сервера spring security в виде JSON. Так как обращения клиента к серверу происходят крайне редко, для этого используется Base Authentication.
Пароли на сервере хранятся в виде хэша bcrypt. На стороне клиента его пароль так же должен хранится в виде хэша bcrypt (либо в любом другом закрытом виде), и здесь из-за этого возникает проблема передачи пароля для аутентификации на сервере, так как Base Authentication требует связки user:password закодированной Base64.
Для себя вижу только два выхода:
- Заставить Spring Security "распознавать" хэши
bcrypt, но как это сделать - не знаю, так как в конфигурации уже объявлен и используетсяpasswordEncoder(); - Изменить систему аутентификации с базовой. Сессии здесь явно не подходят, других вариантов я не знаю (пока опыта мало). Единственное, на данный момент усложнять сильно не хочется (писать отдельно сервер авторизации, как и положено в таких случаях), это будет в дальнейшем.
При необходимости дополню пост необходимой информацией.
Буду рад любым идеям и советам,
UPD: конфиг Spring Security:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MultiHttpSecurityConfig {
@Autowired
UserService userService;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userService)
.passwordEncoder(passwordEncoder());
}
@Bean ///сделал его static дабы не было цикличности с UserService,
protected static PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12);
}
@Configuration
@Order(1)
public static class RestSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
;
}
@Configuration
// @Order(1)
public static class WebFormSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
CustomLogoutSuccessHandler customLogoutSuccessHandler;
@Autowired
CustomSuccessHandler customSuccessHandler;
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/css/**")
.antMatchers("/image/**")
.antMatchers("/js/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/auth/registration").not().fullyAuthenticated()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/auth/login")
.defaultSuccessUrl("/")
.permitAll()
.successHandler(customSuccessHandler)
.and()
.rememberMe()
.and()
.logout()
.permitAll()
.logoutSuccessHandler(customLogoutSuccessHandler)
.invalidateHttpSession(true) ///сделать объект сеанса недействительным,
.clearAuthentication(true) ///очистка всей аутентификации,
.deleteCookies("JSESSIONID") ///удаление куки,
.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET")) //иначе только post-запросами logout,
.and()
.sessionManagement().maximumSessions(1).sessionRegistry(sessionRegistry()) //для httpSessionEventPublisher и sessionRegistry.getAllPrincipals()
;
}
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
@Bean
SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
}
}
Ответы (1 шт):
Получилось заставить Spring Security "распознавать" хэши bcrypt.
Решением было банальное отключение BCryptPasswordEncoder в конфигурации Spring Security сервера (отдельное спасибо @Wlad, за то что натолкнул на эту идею):
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService)
.passwordEncoder(passwordEncoder())
.passwordEncoder(NoOpPasswordEncoder.getInstance());
}
Более того, получилось сконфигурировать Spring Security таким образом, что бы сервер отличал чистый пароль от хешированного, и соответственно этому проводил аутентификацию.
Конфигурацию с короткими комментариями прилагаю, может кому то пригодится:
@Configuration
@Order(1)
public static class RestSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserService userService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
;
}
/////////////////////////Конфигурация аутентификации
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService)
.passwordEncoder(passwordEncoderApi());
}
////////////////////////Конфигурация энкодера
@Bean
public PasswordEncoder passwordEncoderApi() {
return new PasswordEncoder() {
@Override
public String encode(CharSequence charSequence) {
return new BCryptPasswordEncoder().encode(charSequence.toString());
}
/*
так как длина хешированного пароля BCrypt около 50 символов,
а так же присутствует ограничение на чистый пароль при регистрации (не более 20 символов):
*/
@Override
public boolean matches(CharSequence charSequence, String s) {
String rawPassword = charSequence.toString();
/// если прилетел хэш,
if (rawPassword.length() > 20) {
return charSequence.toString().equals(s);
}
/// и если прилетел чистый,
return new BCryptPasswordEncoder().matches(rawPassword, s);
}
};
}
}