Базовая аутентификация с помощью хэша пароля?

Суть такова. Клиент android по запросу get получает данные с сервера spring security в виде JSON. Так как обращения клиента к серверу происходят крайне редко, для этого используется Base Authentication. Пароли на сервере хранятся в виде хэша bcrypt. На стороне клиента его пароль так же должен хранится в виде хэша bcrypt (либо в любом другом закрытом виде), и здесь из-за этого возникает проблема передачи пароля для аутентификации на сервере, так как Base Authentication требует связки user:password закодированной Base64.

Для себя вижу только два выхода:

  1. Заставить Spring Security "распознавать" хэши bcrypt, но как это сделать - не знаю, так как в конфигурации уже объявлен и используется passwordEncoder();
  2. Изменить систему аутентификации с базовой. Сессии здесь явно не подходят, других вариантов я не знаю (пока опыта мало). Единственное, на данный момент усложнять сильно не хочется (писать отдельно сервер авторизации, как и положено в таких случаях), это будет в дальнейшем.

При необходимости дополню пост необходимой информацией.

Буду рад любым идеям и советам,

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 шт):

Автор решения: Oberon

Получилось заставить 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);
                }
            };
        }
    }
→ Ссылка