Spring LDAP returns same and incorrect objectSid

2019-07-27 09:06发布

问题:

In my Spring web application I am unable to retrive the correct objectId from currently logged in user with the Active Directory account. All of attributes seems to have the right value, but the objectId value is always set to S-1-5-21-1723711471-3183472479-4012130053-3220159935 and I don't know where it comes from.

WebSecurityConfig

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/", "/home").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
                .logout()
                .permitAll();
    }

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

    private ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
        ActiveDirectoryLdapAuthenticationProvider provider =
                new ActiveDirectoryLdapAuthenticationProvider(LdapConfig.AD_DOMAIN, LdapConfig.AD_SERVER);
        provider.setUserDetailsContextMapper(new LdapUserDetailsContextMapper());
        return provider;
    }
}

LdapUserDetailsContextMapper

@Slf4j
public class LdapUserDetailsContextMapper implements UserDetailsContextMapper {
    @Override
    public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> collection) {
        log.info("username: " + username); //username is correct
        log.info("DN from ctx: " + ctx.getDn()); // returns correct DN
        byte[] byteSid = ctx.getStringAttribute("objectSid").getBytes();
        String sid = LdapUtils.convertBinarySidToString(byteSid);
        log.info("SID: " + sid); // S-1-5-21-1723711471-3183472479-4012130053-3220159935 everytime

        return new User(username, "notUsed", true, true, true, true,
                AuthorityUtils.createAuthorityList("ROLE_USER"));
    }

    @Override
    public void mapUserToContext(UserDetails userDetails, DirContextAdapter dirContextAdapter) {

    }
}

How to get the correct SID from Active Directory?

回答1:

I think the answer is here: http://forum.spring.io/forum/spring-projects/data/ldap/66894-objectsid-and-ldaptemplate

In the second last post, he describes the same issue you're having. In the last post, he describes a fix, which is to add this to the Bean config file:

<bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
    <property name="url" value="ldap://ldapserver.domain.com:389" />
    <property name="base" value="dc=domain,dc=com" />
    <property name="userDn" value="cn=binduser,cn=Users,dc=domain,dc=com" />
    <property name="password" value="bindpwd"/>
    <property name="baseEnvironmentProperties">
        <map>
        <entry key="java.naming.ldap.attributes.binary">
            <value>objectSid</value>
        </entry>
        </map>
    </property>
</bean>

You'll have to modify the values for your domain, but I think the important part is the baseEnvironmentProperties.

This thread also describes a programmatic way to set that (although, for objectGuid, but you can just swap the attribute).

AbstractContextSource contextSource = (AbstractContextSource) ldapTemplate.getContextSource();
Map<String,String> baseEnvironmentProperties = new HashMap<String, String>();
baseEnvironmentProperties.put("java.naming.ldap.attributes.binary", "objectSid");
contextSource.setBaseEnvironmentProperties(baseEnvironmentProperties);
contextSource.afterPropertiesSet();


回答2:

I got this to work by adding the environment properties in the configure method:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
            .antMatchers("/", "/home").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .loginPage("/login")
            .permitAll()
            .and()
            .logout()
            .permitAll();
}

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

private ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
    ActiveDirectoryLdapAuthenticationProvider provider =
            new ActiveDirectoryLdapAuthenticationProvider(LdapConfig.AD_DOMAIN, LdapConfig.AD_SERVER);

// ************** NEW ENVIRONMENT PROPERTIES **********************************
    Map<String, Object> environmentProperties = new HashMap<>();
    environmentProperties.put("java.naming.ldap.attributes.binary", "objectsid");
    provider.setContextEnvironmentProperties(environmentProperties);
// ************** END OF NEW ENVIRONMENT PROPERTIES ***************************

    provider.setUserDetailsContextMapper(new LdapUserDetailsContextMapper());
    return provider;
    }
}

And then reading it like this in the UserDetailContextMapper:

public class CustomUserDetailsContextMapper implements UserDetailsContextMapper {

private final static Logger logger = LoggerFactory.getLogger(CustomUserDetailsContextMapper.class);

@Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
    logger.info(ctx.getDn().toString());
    byte[] byteSid = null;
    try {
        byteSid = (byte[]) ctx.getAttributes().get("objectsid").get();
    } catch (NamingException e) {
        e.printStackTrace();
    }
    String sid = LdapUtils.convertBinarySidToString(byteSid);
    logger.info("SID: {}", sid);

    return new User(username, "notUsed", true, true, true, true,
            AuthorityUtils.createAuthorityList("ROLE_USER"));    
}

I hope this is helpful!