Аутентификация LDAP через репозиторий LDAP

У меня возникают проблемы с разрешением различных типов кодирования с моего сервера LDAP (который находится в SSHA) и значением аутентификации простого текста.

Я использую репозиторий LDAP для получения своего сервера LDAP и использовал AuthenticationManager и свою собственную реализацию `UserDetailService.

Проблема всегда заключается в том, что я получаю исключение Bad Credentials, когда пытаюсь пройти аутентификацию.

Как видно из моих кодов ниже;

когда я запрашиваю /auth, у меня есть простой текст username и password. Однако, когда я пытаюсь аутентифицировать его через AuthenticationManager, он дает мне неверные учетные данные. Теперь я знаю, что это связано с UserDetailService.getPassword() и password, которые я предоставил в UsernamePasswordAuthenticationToken. Речь идет о другом типе шифрования. У моего LDAP есть {SSHA}, также переведенный в двоичный (байты), а кодировщик — Bcrypt и String.

Теперь мой вопрос: как правильно аутентифицировать пароль по паролю, возвращаемому LdapRespository?

ОБНОВЛЕНИЯ

2020-04-12 17:09:46.655  INFO 6672 --- [nio-8080-exec-1] s.w.s.SantaRestApiSecurityConfiguration  : Raw Password:  jdoe123 | Encoded Password: {SSHA}kMgk3gD4prAa/9m4wsPbuAoGhO7UvH2v6+W0Dg==
2020-04-12 17:09:58.110  INFO 6672 --- [nio-8080-exec-1] s.w.s.SantaRestApiSecurityConfiguration  : Raw Password: {SSHA}kMgk3gD4prAa/9m4wsPbuAoGhO7UvH2v6+W0Dg== matches Encoded Password: {SSHA}k1Pp3NICHbwuxFFdT7zno/iG/NTILZGL = false

Вышеупомянутые журналы во время сопоставления пароля в PasswordEncoder. Исходя из него. он показывает, что Необработанный пароль (введенный пользователем) имеет хэш, отличный от Закодированного пароля (с сервера ldap).

Я использовал LdapShaPasswordEncoder.encode для хеширования введенного пользователем пароля.

Кроме того, я заметил, что каждый раз, когда я хочу пройти аутентификацию (вызвать http://locahost:xxxx/auth), хэш пароля, введенного пользователем, меняется каждый раз. я думаю это нормально при хешировании

Конфигурация безопасности ПРИМЕЧАНИЕ. Сейчас я разрешаю все конечные точки REST-API, позже я ограничу их

@Configuration
@EnableWebSecurity
public class SantaRestApiSecurityConfiguration extends WebSecurityConfigurerAdapter   {

    @Autowired
    AuthService authService;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        // configure AuthenticationManager so that it knows from where to load
        // user for matching credentials
        // Use BCryptPasswordEncoder
        auth.userDetailsService(authService).passwordEncoder(passwordEncoder());
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // TODO Auto-generated method stub
        http.csrf()
            .disable()
            .authorizeRequests().antMatchers("/*").permitAll();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/*");
    }

    /*
     * i am not sure about this since my LDAP server has 
     * a SSHA password encrpytion
     * 
     * */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

Контроллер входа в систему

@RestController
@RequestMapping("/auth")
public class LoginController {

    public static Logger logger = LoggerFactory.getLogger(LoginController.class);

    @Autowired
    private AuthenticationManager authManager;

    @PostMapping
    public ResponseEntity<String> login(@RequestBody Map<String, String> credentials) {
        String username = credentials.get("username");
        String password = credentials.get("password");

        authManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));

        return new ResponseEntity<String>("", HttpStatus.OK);
    }
}

Реализация UserDetailService

@Service
public class AuthService implements UserDetailsService {

    public static Logger logger = LoggerFactory.getLogger(AuthService.class);

    @Autowired
    UserLdapRepository ldapRepo;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        UserLdap e = ldapRepo.findByUid(username);
        logger.info("{}",e.toString());

        return new AuthDetails(e.getCn(), 
                               e.getPassword(),
                               e.getUid(), 
                               e.getMail(), 
                               e.getSn(), 
                               Long.toString( e.getUidNumber()));
    }
}

Реализация UserDetails

public class AuthDetails implements UserDetails {

    public static Logger logger = LoggerFactory.getLogger(AuthDetails.class);

    private String username;
    private String password;
    private String email;
    private String uidNumber;
    private String sn;
    private String uid;

    public AuthDetails(String username, String password, String uid, String email,String sn, String uidNumber) {
        this.uid = uid;
        this.password = password;
        this.username = username;
        this.email = email;
        this.sn = sn;
        this.uidNumber = uidNumber;
    }

    /*
     * Here i am not sure how to implement this one to satisfy AuthenticationManager
     *
     *
     **/
    @Override
    public String getPassword() {
        return this.password;
    }

    @Override
    public String getUsername() {
        // TODO Auto-generated method stub
        return this.username;
    }

    // some method to implement

}

person lemoncodes    schedule 12.04.2020    source источник
comment
Сохраняете ли вы пароль пользователя LDAP в BCrypt endcoing?   -  person Mukhtiar Ahmed    schedule 12.04.2020
comment
нет, я только читаю существующий сервер LDAP. Нет записи или изменения существующих данных. Прямо сейчас сервер LDAP, который я читаю, использует шифрование SSHA.   -  person lemoncodes    schedule 12.04.2020


Ответы (1)


Вы используете BCryptPasswordEncoder, но в LDAP используется шифрование SSHA.

Вам нужно изменить метод passwordEncoder в классе SantaRestApiSecurityConfiguration

  @Bean
public PasswordEncoder passwordEncoder() {
   return  new LdapShaPasswordEncoder();

}



public Class UserLdap {

    @Attribute(name = "userPassword", type = Type.BINARY)
    private byte[] password;
}

и измените метод loadUserByUsername

@Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        UserLdap e = ldapRepo.findByUid(username);
        logger.info("{}",e.toString());
        String password = "{SSHA}" + new String(e.getPassword());
        return new AuthDetails(e.getCn(), 
                               password,
                               e.getUid(), 
                               e.getMail(), 
                               e.getSn(), 
                               Long.toString( e.getUidNumber()));
    } 
person Mukhtiar Ahmed    schedule 12.04.2020
comment
Какой LdapShaPasswordEncoder мне использовать? Здесь написано, что он устарел, также encodePassword не существует, а также isPasswordValid. я использовал encode и matches соответственно как альтернативу - person lemoncodes; 12.04.2020
comment
Как насчет получения пароля из ldaprepository. я читал, что значение на сервере ldap для userPassword хранится в двоичном виде. Как я должен преобразовать его? - person lemoncodes; 12.04.2020
comment
Я сделал что-то похожее на это. Моя проблема в том, что значение, которое я извлекаю из поля пароля на сервере ldap, похоже на 112,12,3,2,21, в основном его двоичный код, тогда тот, который я использую для аутентификации, представляет собой читаемую строку. - person lemoncodes; 12.04.2020
comment
Возможно проблема в UserLdapRepository. он неправильно получает пароль от сервера LDAP. - person Mukhtiar Ahmed; 12.04.2020
comment
Нет, возвращает правильно, просто серверы LDAP хранят пароль в двоичном формате. см. эту статью stackoverflow .com/questions/25536341/ - person lemoncodes; 12.04.2020
comment
затем измените тип пароля даты на массив байтов в UserLdap и преобразуйте в строку - person Mukhtiar Ahmed; 12.04.2020
comment
тогда я не могу получить фактический хэш из байта. так как это SSHA, я не могу его расшифровать. Я думаю, что это односторонняя вещь. Кроме того, хеш SSHA генерирует разные хэши с одним и тем же строковым паролем. - person lemoncodes; 12.04.2020
comment
Я внес изменения в loadUserByUsername. - person Mukhtiar Ahmed; 12.04.2020
comment
да, сделал то же самое, но все еще не может правильно выполнить сопоставление, поскольку хэш пароля, введенный пользователем, не является тем же самым ldap (хотя обычный текст на самом деле тот же). Посмотреть обновления - person lemoncodes; 12.04.2020
comment
примените base64 к массиву байтов, а затем попробуйте - person Mukhtiar Ahmed; 12.04.2020
comment
на самом деле new String(e.getPassword()); эта строка уже производит {SSHA}.... проблема в том, что ввод пользователя имеет другой хеш (используя тот же текстовый пароль) - person lemoncodes; 12.04.2020
comment
Возможно, сервер LDAP хранит пароль пользователя в другом шифровании. - person Mukhtiar Ahmed; 12.04.2020
comment
на самом деле я проверил свое определение ldif на своем сервере и тот, который я получил в журналах spring, оба одинаковы. проблема в том, что он хешируется из пользовательского ввода - person lemoncodes; 12.04.2020