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?
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();
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!