Хорошо, примерно через полтора дня работы над этим я понял это.
Мой первоначальный подход заключался в том, чтобы расширить класс Spring ActiveDirectoryLdapAuthenticationProvider
и переопределить его метод loadUserAuthorities()
, чтобы настроить способ создания разрешений аутентифицированного пользователя. По неочевидным причинам класс ActiveDirectoryLdapAuthenticationProvider
обозначен как final
, поэтому, конечно, я не могу его расширить.
К счастью, открытый исходный код обеспечивает возможность взлома (а суперклассы этого класса не final
), поэтому я просто скопировал все его содержимое, удалил обозначение final
и соответствующим образом скорректировал ссылки на пакеты и классы. Я не редактировал никакой код в этом классе, за исключением добавления заметного комментария, в котором говорится, что его нельзя редактировать. Затем я расширил этот класс в OverrideActiveDirectoryLdapAuthenticationProvider
, на который я также ссылался в своем файле ldap.xml
, и добавил в него метод переопределения для loadUserAuthorities
. Все это отлично работало с простой привязкой LDAP через незашифрованный сеанс (на изолированном виртуальном сервере).
Однако реальная сетевая среда требует, чтобы все запросы LDAP начинались с подтверждения TLS, а запрашиваемый сервер не является PDC - его имя - 'sub.domain.tld', но пользователь правильно аутентифицирован по 'domain.tld . ' Кроме того, для привязки к имени пользователя должно быть добавлено "NT_DOMAIN \". Все это требовало работы по настройке, и, к сожалению, я нигде почти не нашел помощи.
Итак, вот абсурдно простые изменения, все из которых включают дополнительные переопределения в OverrideActiveDirectoryLdapAuthenticationProvider
:
@Override
protected DirContext bindAsUser(String username, String password) {
final String bindUrl = url; //super reference
Hashtable<String,String> env = new Hashtable<String,String>();
env.put(Context.SECURITY_AUTHENTICATION, "simple");
//String bindPrincipal = createBindPrincipal(username);
String bindPrincipal = "NT_DOMAIN\\" + username; //the bindPrincipal() method builds the principal name incorrectly
env.put(Context.SECURITY_PRINCIPAL, bindPrincipal);
env.put(Context.PROVIDER_URL, bindUrl);
env.put(Context.SECURITY_CREDENTIALS, password);
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxtFactory");
//and finally, this simple addition
env.put(Context.SECURITY_PROTOCOL, "tls");
//. . . try/catch portion left alone
}
То есть все, что я сделал с этим методом, - это изменил способ форматирования строки bindPrincipal
и добавил ключ / значение в хеш-таблицу.
Мне не нужно было удалять субдомен из параметра domain
, переданного моему классу, потому что он передавался ldap.xml
; Я просто изменил параметр там на <constructor-arg value="domain.tld"/>
Затем я изменил метод searchForUser()
в OverrideActiveDirectoryLdapAuthenticationProvider
:
@Override
protected DirContextOperations searchForUser(DirContext ctx, String username) throws NamingException {
SearchControls searchCtls = new SearchControls();
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
//this doesn't work, and I'm not sure exactly what the value of the parameter {0} is
//String searchFilter = "(&(objectClass=user)(userPrincipalName={0}))";
String searchFilter = "(&(objectClass=user)(userPrincipalName=" + username + "@domain.tld))";
final String bindPrincipal = createBindPrincipal(username);
String searchRoot = rootDn != null ? rootDn : searchRootFromPrincipal(bindPrincipal);
return SpringSecurityLdapTemplate.searchForSingleEntryInternal(ctx, searchCtls, searchRoot, searchFilter, new Object[]{bindPrincipal});
Последнее изменение касалось метода createBindPrincipal()
для правильного построения String (для моих целей):
@Override
String createBindPrincipal(String username) {
if (domain == null || username.toLowerCase().endsWith(domain)) {
return username;
}
return "NT_DOMAIN\\" + username;
}
И с вышеупомянутыми изменениями, которые все еще нуждаются в очистке от всего моего тестирования и работы с головным офисом, я смог привязать и аутентифицироваться от имени себя в Active Directory в самой сети, захватывать любые поля пользовательских объектов, которые я хотел, идентифицировать членство в группе , и т.д.
Да, и очевидно, что TLS не требует 'ldaps: //', поэтому мой ldap.xml
просто имеет ldap://192.168.0.3:389
.
tl; dr:
Чтобы включить TLS, скопируйте класс Spring ActiveDirectoryLdapAuthenticationProvider
, удалите обозначение final
, расширите его до настраиваемого класса и переопределите bindAsUser()
, добавив env.put(Context.SECURITY_PROTOCOL, "tls");
в хэш-таблицу среды. Вот и все.
Чтобы более точно контролировать имя пользователя привязки, домен и строку запроса LDAP, при необходимости переопределите применимые методы. В моем случае я не мог точно определить значение {0}
, поэтому я полностью удалил его и вместо этого вставил переданную строку username
.
Надеюсь, кто-то найдет это полезным.
person
cabbagery
schedule
16.05.2013