Я работаю над инструментом, чтобы получить данные пользователя из AD и импортировать их в другую систему. Мы планировали использовать objectSid как уникальный идентификатор, но я обнаружил, что по какой-то причине объект-объект в LDAP-результате не соответствует тому, что в Active Directory. Большинство байтов одно и то же, но есть некоторые из них, и иногда результаты LDAP имеют меньше байтов, чем в AD.
objectSid от пользователя в AD:
decimal: [ 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 35, 106, 222, 96, 236, 251, 239, 68, 32, 255, 234, 203, 122, 4, 0, 0]
hex: [01, 05, 00, 00, 00, 00, 00, 05, 15, 00, 00, 00, 23, 6A, DE, 60, EC, FB, EF, 44, 20, FF, EA, CB, 7A, 04, 00, 00]
objectSid для одного пользователя через результат LDAP:
decimal: [ 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 35, 106, 63, 96, 63, 63, 63, 68, 32, 63, 63, 63, 122, 4, 0, 0]
hex: [01, 05, 00, 00, 00, 00, 00, 05, 15, 00, 00, 00, 23, 6A, 3F, 60, 3F, 3F, 3F, 44, 20, 3F, 3F, 3F, 7A, 04, 00, 00]
Кажется, что любое значение более 128 возвращается как результат 63/3F в результате LDAP. Для другого пользователя результат LDAP отсутствует 1 байт (вопросительные знаки):
hex from AD: [01 05 00 00 00 00 00 05 15 00 00 00 23 6A DE 60 EC FB EF 44 20 FF EA CB 88 04 00 00]
hex from LDAP: [01 05 00 00 00 00 00 05 15 00 00 00 23 6A 3F 60 3F 3F 3F 44 20 3F 3F 3F ?? 04 00 00]
Вот основная часть кода, который я использую для выполнения этих тестов.
final String ldapADServer = "ldap://" + cmdLine.getOptionValue("ldap");
final String bindDN = cmdLine.getOptionValue("u");
final String bindCredential = cmdLine.getOptionValue("p");
final String baseCtxDN = cmdLine.getOptionValue("d");
final Hashtable<String, Object> env = new Hashtable<String, Object>();
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, bindDN);
env.put(Context.SECURITY_CREDENTIALS, bindCredential);
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldapADServer);
env.put("com.sun.jndi.ldap.trace.ber", System.err);
final LdapContext ctx = new InitialLdapContext(env, null);
final String searchFilter = "(&(objectClass=user) (sAMAccountName=" + accountName + "))";
final SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
final StringBuilder builder = new StringBuilder();
final NamingEnumeration<SearchResult> results = ctx.search(baseCtxDN, searchFilter, searchControls);
while (results != null && results.hasMoreElements()) {
final SearchResult result = results.nextElement();
builder.append(LdapHelper.getSearchResultDetails(result, ""));
}
logger.info("Search results: {}{}", StringUtils.NEW_LINE, builder.toString());
LdapHelper просто перебирает все атрибуты и возвращает их в красиво отформатированной строке. ОбъектGUID и objectSid печатаются в шестнадцатеричном формате.
Я запускал тест, используя JRE 6, а также JRE 7 с тем же результатом. Наш сервер AD - это Window Server 2008 RC2, и я попытался использовать оба порта AD, 389 и 3268.
Теперь я собираюсь изучить другие библиотеки LDAP Java, но мне хотелось узнать, сталкивался ли кто-либо с этими проблемами или кто-нибудь знает, почему это так и как обойти это? То есть есть ли способ получить правильные значения из AD?
Спасибо.
UPDATE:
Теперь я сделал то же самое с помощью UnboundID LDAP SDK, и это работает правильно и возвращает полный и правильный objectSid, а также objectGUID. Значит, это похоже на ошибку в стандартной библиотеке J2SE?
Код для этого, если кто-то заинтересован:
private static void unboundIdLdapSearch(final String ldapADServer, final String bindDN, final String bindCredential, final String baseCtxDN, final String userName) throws LDAPException, Exception {
final LDAPConnection connection = new LDAPConnection(ldapADServer.substring(0, ldapADServer.indexOf(':')),
Integer.parseInt(ldapADServer.substring(ldapADServer.indexOf(':') + 1)), bindDN, bindCredential);
findAccountByAccountName(connection, baseCtxDN, userName);
connection.close();
}
private static void findAccountByAccountName(final LDAPConnection connection, final String baseCtxDN, final String accountName) throws Exception {
final String searchFilter = "(&(objectClass=user)(sAMAccountName=" + accountName + "))";
logger.info("LDAP search filter: {}", searchFilter);
final SearchRequest request = new SearchRequest(baseCtxDN, SearchScope.SUB, searchFilter);
final com.unboundid.ldap.sdk.SearchResult result = connection.search(request);
final int numOfResults = result.getEntryCount();
final StringBuilder builder = new StringBuilder();
builder.append("Search returned with ").append(numOfResults).append(" results: ").append(StringUtils.NEW_LINE);
for (final SearchResultEntry entry : result.getSearchEntries()) {
builder.append(LdapHelper.getSearchResultDetails(entry, ""));
}
logger.info("Search results: {}{}", StringUtils.NEW_LINE, builder.toString());
}
2nd UPDATE
Случалось, что споткнулся о том, почему метод JNDI LDAP не работал должным образом для objectSid и objectGUID и заработал его в дополнение к моему решению UnboundID.
Прежде всего, я понял, что когда я использовал метод UnboundID 'getValue', который возвращает строку, он также возвращал те же значения, что и версия J2SE JNDI, которая была, когда я выяснил, что это преобразование строки в UTF -8 импортированного значения.
Затем мне попалось другое сообщение в блоге (http://www.jroller.com/eyallupu/entry/java_jndi_how_to_convert), а также эту страницу: http://docs.oracle.com/javase/jndi/tutorial/ldap/misc/attrs.html. Таким образом, все, что необходимо для того, чтобы правильно получить objectSid и objectGUID, - это добавить их в список двоичных атрибутов, добавив в список контекста LDAP список имен атрибутов, разделенных пробелами:
env.put("java.naming.ldap.attributes.binary", "objectSid objectGUID");