Issue
So I am using the code below to connect to our LDAP server in my spring boot app, I can authorized successfully using the userPrincipalName -> ([email protected]), but I want to use the assigned sAMAccountName instead (ID00001).
I messed around with the setSearchFilter by doing: provider.setSearchFilter("(sAMAccountName ={0})")
but it's not working. (I'm getting bad credentials)
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/myapplication")
.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and()
.httpBasic();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
}
@Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
}
@Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider("", "ldap://test.enterprise.com","dc=ORG1, dc=ORG2");
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
provider.setSearchFilter("(sAMAccountName={0})"); // THIS DOES NOT WORK
provider.setSearchFilter("(userPrincipalName={0})"); // THIS WORKS
return provider;
}
}
EDIT: OK turns out I have been using the wrong field, there is another field: sAMAccountName that has the same value that I should be using, updated the title and question contents.
EDIT:
Ok I tried:
provider.setSearchFilter("(&(objectClass=user)(cn={0}))");
provider.setSearchFilter("(&(objectClass=user)(sAMAccountName={0}))");
provider.setSearchFilter("(&(objectClass=user)(cn={1}))");
provider.setSearchFilter("(&(objectClass=user)(sAMAccountName={1}))");
and it's still the same error,
bad credentials, AcceptSecurityContextError, 52e v2580
Solution
I managed to solve my issue but instead of using spring ldap dependencies, I created a custom authentication provider that implements AuthenticationProvider and and used the following code to connect to ldap and validate the credentials:
autowire it to WebSecurityConfig class.
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserDetails userDetails;
@Override
public Authentication authenticate(Authentication auth) throws
AuthenticationException {
String user = authentication.getName();
String pass = authentication.getCredentials.ToString();
try {
if (isRegistrered(user,pass) {
return new UsernamePasswordAuthenticationToken(user,pass, new
ArrayList<>());
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
private isRegisterd(String user, String pass) {
boolean result = false;
try {
// Set up the environment for creating the initial context
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://ldap_server:389");
//
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "DOMAIN\\USER");
env.put(Context.SECURITY_CREDENTIALS, "PASSWORD");
// Create the initial context
DirContext ctx = new InitialDirContext(env);
if(ctx != null) {
ctx.close();
}
return result;
} catch (Exception e) {
return result;
}
}
private void retrieveUserDetails(DirContext ctx, String username) throws NamingException {
String userSearchBase = "dc=TEST,dc=SAMPLE";
String userSearchFilter = String.format("sAMAccountName=%s", username);
//any attributes that you want
String[] attributes = { "sAMAccountName", "department" };
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
controls.setReturningAttributes(attributes);
SearchResult result = null;
NamingEnumeration<?> users = ctx.search(userSearchBase, userSearchFilter, controls);
while(users.hasmore()) {
result = (SearchResult) users.next();
Attributes attr = result.getAttribtutes();
String sAMAccountName = attr.get("sAMAccountName").get(0).toString();
String department = attr.get("department").get(0).toString();
//assign to autowired object to be accessed anywhere
this.userDetails.setAccountName(sAMAccountName);
this.userDetails.setDepartment(department);
}
}
}
Answered By - askwer
Answer Checked By - Timothy Miller (JavaFixing Admin)