Java Spring получает атрибуты из Active Directory UserDetails

У меня есть пользователь, который вошел в систему через AD, и теперь я хочу получить некоторую информацию о нем. Вот пример конечной точки теста, с которой я играю:

@RequestMapping(value={"/secure/test"}, method=RequestMethod.GET)
public ResponseEntity<?> getSecureTest(HttpServletRequest request) {
    String str = "Test Response";

    request.getSession().setAttribute("testVar", "SessionVariable");

    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    UserDetails userDetails = (UserDetails) authentication.getPrincipal();
    if (!(authentication instanceof AnonymousAuthenticationToken)) {
        String currentUserName = authentication.getName();
        str = str + "\n -- " + currentUserName + "\n\n";
        str = str + userDetails.getUsername(); // matches authentication.getName()
        return new ResponseEntity<>(str, HttpStatus.OK);
    } else {
        str = str + "failed auth";
        return new ResponseEntity<>(str, HttpStatus.UNAUTHORIZED);
    }
}

Я могу получить аутентификацию и, исходя из этого, UserDetails, но реализация, которая выходит, я считаю, LdapUserDetailsImpl

https://docs.spring.io/spring-security/site/docs/current/apidocs/org/springframework/security/ldap/userdetails/LdapUserDetailsImpl.html

Похоже, у этого нет никаких методов, таких как «getAttribute» или что-то в этом роде. Если бы я хотел получить один из атрибутов AD, например "mail" или "номер телефона", как мне его получить?

Редактировать:

Итак, чтобы попытаться извлечь атрибут title, я расширил LdapUserDetailsImpl:

public class CustomUserDetails extends LdapUserDetailsImpl {

    private String title;

    public void setTitle(String title) {
        this.title = title;
    }
    public String getTitle() {
        return this.title;
    }
}

И я расширил LdapUserDetailsMapper:

public class CustomDetailsContextMapper extends LdapUserDetailsMapper {

    @Override
    public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
        LdapUserDetailsImpl ldapUserDetailsImpl = (LdapUserDetailsImpl) super.mapUserFromContext(ctx, username, authorities);
        CustomUserDetails customUserDetails = new CustomUserDetails();
        customUserDetails.setTitle(ctx.getStringAttribute("title"));
        return customUserDetails;
    }
}

В моем контроллере я пытаюсь получить этот объект:

CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();

но это дает мне ошибку приведения... Что мне не хватает?

В WebSecurityConfigurerAdapter есть это:

@Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
    authManagerBuilder.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
    authManagerBuilder.userDetailsService(userDetailsService());
}

@Bean
public AuthenticationManager authenticationManager() {
    return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProviderES()));
}
@Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
    ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(ldapdomain, ldapurl);
    provider.setConvertSubErrorCodesToExceptions(true);
    provider.setUseAuthenticationRequestCredentials(true);

    return provider;
}

person MichaelB    schedule 21.11.2017    source источник


Ответы (2)


Сначала установите своего провайдера, добавив ниже в свой файл SecurityConfiguration. Если не задано, по умолчанию используется простой LdapUserDetailsMapper, который не имеет всех атрибутов.

provider.setUserDetailsContextMapper(userDetailsContextMapper());

@Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
    ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(ldapdomain, ldapurl);
    provider.setConvertSubErrorCodesToExceptions(true);
    provider.setUseAuthenticationRequestCredentials(true);
    provider.setUserDetailsContextMapper(userDetailsContextMapper());
    return provider;
}

@Bean
public UserDetailsContextMapper userDetailsContextMapper() {
     return new CustomUserMapper();
}

Затем создайте собственный преобразователь, расширяющий LdapUserDetailsMapper.

public class CustomUserMapper extends LdapUserDetailsMapper{

    @Override
    public CustomUserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities){

        // set from userDetails 
        UserDetails details = super.mapUserFromContext(ctx, username, authorities);

        // set directly from ctx 
        CustomUserDetails customUserDetails = new CustomUserDetails();
        customUserDetails.setFirstName(ctx.getStringAttribute("givenName"));
        customUserDetails.setLastName(ctx.getStringAttribute("sn"));

        return customUserDetails;
    }

}
person Pan    schedule 18.01.2019

Вы можете реализовать свой собственный сопоставитель сведений о пользователе, расширив springs ldap one.

package example.active.directory.authentication;

import org.springframework.ldap.core.DirContextOperations;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;

import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Collection;

public class CustomUserMapper extends LdapUserDetailsMapper{

    @Override
    public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities){

        UserDetails details = super.mapUserFromContext(ctx, username, authorities);
        String[] changedValues = ctx.getStringAttributes("whenchanged");
        /// Do something here, like map to your custom UserDetails object.
        return details;        
    }
}

Если вы установите точку останова в этом методе, вы сможете исследовать все различные атрибуты, доступные вам в отладчике.

Это похоже на другой ответ, который я дал: >Обновлять информацию о пользователях во время входа в систему через LDAP AD с помощью Spring

person Pytry    schedule 21.11.2017
comment
В этом случае вам нужно будет создать несколько дополнительных полей в CustomUserMapper, а затем назначить эти поля материалам, которые вы получаете из ctx.getStringAttribute или чего-то еще, верно? Как это используется контроллером позже? Просто приведя к этому конкретному типу? - person MichaelB; 21.11.2017
comment
Вам нужно будет добавить дополнительные поля (при необходимости) в ваш пользовательский объект UserDetails. Затем ваш преобразователь, который расширяет преобразователь LDAP UserDetails, заполнит поля в вашем объекте UserDetails в соответствии с вашими требованиями. Затем вы сможете получить свою версию UserDetails из SecurityContext по мере необходимости из контроллера. - person MathanMV; 21.11.2017
comment
Ты видишь мое обновление? Я расширил все классы UserDetails, чтобы попытаться захватить атрибуты, но это все еще не работает. - person MichaelB; 21.11.2017
comment
Дополнительные поля не нужны. Любые данные, которые вы хотите извлечь из контекста, будут добавлены в объект UserDetails и возвращены. Если вы создадите свои собственные пользовательские сведения о пользователе, вы можете добавить туда поля. Что касается того, куда поместить маппер, он входит в вашу конфигурацию безопасности, а не в ваш контроллер. Затем, когда вы получите принципала, вы переведете его в свой пользовательский объект userdetails., - person Pytry; 21.11.2017
comment
Уточнение, нет дополнительных полей на контроллере. Это все в вашей конфигурации безопасности. - person Pytry; 21.11.2017
comment
@Pytry, как вы можете видеть, сейчас я пытаюсь создать пользовательский UserDetails, но я до сих пор не понимаю, как подключить этот новый класс к ... Я получаю ошибки приведения во время выполнения. - person MichaelB; 21.11.2017
comment
Как выглядит ваша конфигурация безопасности? Вы добавили точку останова в метод сопоставления, чтобы увидеть, вызывается ли он? Если вы удалите приведение, какой класс объекта будет возвращен? - person Pytry; 21.11.2017
comment
Кроме того, вы можете изменить тип возврата преобразователя на CustomUserDetails. - person Pytry; 21.11.2017
comment
@Pytry контроллер возвращает следующее: org.springframework.security.ldap.userdetails.LdapUserDetailsImpl независимо от типа возвращаемого значения в CustomDetailsContextMapper. - Я также обновил материал WebSecurityConfig. - person MichaelB; 22.11.2017