Аутентификация Active Directory с помощью Java на Linux
У меня есть простая задача аутентификации Active Directory с помощью Java. Просто проверка учетных данных и ничего больше. Скажем, мой домен "fun.xyz.tld", путь OU неизвестен, а имя пользователя/пароль - testu/testp.
Я знаю там несколько библиотек Java, которые упрощают эту задачу, но мне не удалось их реализовать. Большинство примеров, которые я нашел, адресовали LDAP в целом, а не Active Directory. Выдача запроса LDAP означает отправку в него пути OU, которого у меня нет. Кроме того, приложение, которое выдает запрос LDAP, должно быть привязано к Active Directory, чтобы получить доступ к нему... Небезопасно, так как учетные данные должны быть сохранены в каком-либо месте для обнаружения. Я бы хотел, чтобы тестовое связывание с тестовыми учетными данными, если возможно, означало бы, что учетная запись действительна.
Последнее, если возможно, есть способ сделать такой механизм аутентификации зашифрованным? Я знаю, что AD использует Kerberos, но не уверен, работают ли методы LDAP Java.
Есть ли у кого-нибудь пример рабочего кода? Спасибо.
Ответы
Ответ 1
Вот код, который я собрал на примере из этого блога: LINK и этот источник: ССЫЛКА.
import com.sun.jndi.ldap.LdapCtxFactory;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Iterator;
import javax.naming.Context;
import javax.naming.AuthenticationException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import static javax.naming.directory.SearchControls.SUBTREE_SCOPE;
//import org.acegisecurity.AuthenticationException;
import org.acegisecurity.BadCredentialsException;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.GrantedAuthorityImpl;
import org.acegisecurity.providers.AuthenticationProvider;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.providers.dao.AbstractUserDetailsAuthenticationProvider;
import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.UserDetailsService;
import org.acegisecurity.userdetails.UsernameNotFoundException;
class App2 {
public static void main(String[] args) {
if (args.length != 4 && args.length != 2) {
System.out.println("Purpose: authenticate user against Active Directory and list group membership.");
System.out.println("Usage: App2 <username> <password> <domain> <server>");
System.out.println("Short usage: App2 <username> <password>");
System.out.println("(short usage assumes 'xyz.tld' as domain and 'abc' as server)");
System.exit(1);
}
String domainName;
String serverName;
if (args.length == 4) {
domainName = args[2];
serverName = args[3];
} else {
domainName = "xyz.tld";
serverName = "abc";
}
String username = args[0];
String password = args[1];
System.out
.println("Authenticating " + username + "@" + domainName + " through " + serverName + "." + domainName);
// bind by using the specified username/password
Hashtable props = new Hashtable();
String principalName = username + "@" + domainName;
props.put(Context.SECURITY_PRINCIPAL, principalName);
props.put(Context.SECURITY_CREDENTIALS, password);
DirContext context;
try {
context = LdapCtxFactory.getLdapCtxInstance("ldap://" + serverName + "." + domainName + '/', props);
System.out.println("Authentication succeeded!");
// locate this user record
SearchControls controls = new SearchControls();
controls.setSearchScope(SUBTREE_SCOPE);
NamingEnumeration<SearchResult> renum = context.search(toDC(domainName),
"(& (userPrincipalName=" + principalName + ")(objectClass=user))", controls);
if (!renum.hasMore()) {
System.out.println("Cannot locate user information for " + username);
System.exit(1);
}
SearchResult result = renum.next();
List<GrantedAuthority> groups = new ArrayList<GrantedAuthority>();
Attribute memberOf = result.getAttributes().get("memberOf");
if (memberOf != null) {// null if this user belongs to no group at all
for (int i = 0; i < memberOf.size(); i++) {
Attributes atts = context.getAttributes(memberOf.get(i).toString(), new String[] { "CN" });
Attribute att = atts.get("CN");
groups.add(new GrantedAuthorityImpl(att.get().toString()));
}
}
context.close();
System.out.println();
System.out.println("User belongs to: ");
Iterator ig = groups.iterator();
while (ig.hasNext()) {
System.out.println(" " + ig.next().toString());
}
} catch (AuthenticationException a) {
System.out.println("Authentication failed: " + a);
System.exit(1);
} catch (NamingException e) {
System.out.println("Failed to bind to LDAP / get account information: " + e);
System.exit(1);
}
}
private static String toDC(String domainName) {
StringBuilder buf = new StringBuilder();
for (String token : domainName.split("\\.")) {
if (token.length() == 0)
continue; // defensive check
if (buf.length() > 0)
buf.append(",");
buf.append("DC=").append(token);
}
return buf.toString();
}
}
Ответ 2
Существует три протокола аутентификации, которые могут использоваться для аутентификации между Java и Active Directory в Linux или на любой другой платформе (и они не только специфичны для служб HTTP):
-
Kerberos - Kerberos обеспечивает Single Sign-On (SSO) и делегирование, но веб-серверы также нуждаются в поддержке SPNEGO для принятия SSO через IE.
-
NTLM - NTLM поддерживает SSO через IE (и другие браузеры, если они правильно настроены).
-
LDAP - привязка LDAP может использоваться для простой проверки имени учетной записи и пароля.
Также есть что-то, называемое "ADFS", которое предоставляет SSO для сайтов, использующих SAML, которые вызывают в SSP Windows, поэтому на практике это в основном окольный способ использования одного из других вышеперечисленных протоколов.
У каждого протокола есть свои преимущества, но, как правило, для максимальной совместимости вы обычно пытаетесь "делать то, что делает Windows". Итак, что делает Windows?
Во-первых, аутентификация между двумя машинами Windows благоприятствует Kerberos, потому что серверам не требуется связываться с DC, и клиенты могут кэшировать билеты Kerberos, что снижает нагрузку на DC (и потому, что Kerberos поддерживает делегирование).
Но если у аутентифицирующих сторон не есть учетные записи домена или если клиент не может связаться с DC, требуется NTLM. Таким образом, Kerberos и NTLM не являются взаимоисключающими, а NTLM не устаревает Kerberos. Фактически в некотором смысле NTLM лучше, чем Kerberos. Обратите внимание, что при упоминании Kerberos и NTLM на одном дыхании я должен также упомянуть SPENGO и Integrated Windows Authentication (IWA). IWA - простой термин, который в основном означает Kerberos или NTLM или SPNEGO для согласования Kerberos или NTLM.
Использование LDAP-связывания в качестве способа проверки учетных данных неэффективно и требует SSL. Но до недавнего времени внедрение Kerberos и NTLM было затруднительным, поэтому использование LDAP в качестве службы проверки подлинности смены-сдвига сохранилось. Но в этот момент его вообще следует избегать. LDAP - это каталог информации, а не служба проверки подлинности. Используйте его по назначению.
Итак, как вы реализуете Kerberos или NTLM в Java и в контексте веб-приложений в частности?
Существует множество крупных компаний, таких как Quest Software и Centrify, которые имеют решения, специально упоминающие Java. Я не могу прокомментировать их, поскольку они являются "решениями управления идентификацией" для всей компании, поэтому, глядя на маркетинг на своем веб-сайте, трудно точно сказать, какие протоколы используются и как. Вам нужно будет связаться с ними для получения подробной информации.
Реализация Kerberos в Java не является ужасно трудной, поскольку стандартные библиотеки Java поддерживают Kerberos через классы org.ietf.gssapi. Однако до недавнего времени существовало серьезное препятствие - IE не отправляет сырые маркеры Kerberos, он отправляет токены SPNEGO. Но с Java 6 SPNEGO был реализован. Теоретически вы должны написать код GSSAPI, который может аутентифицировать IE-клиенты. Но я не пробовал. Реализация Sun Kerberos была комедией ошибок на протяжении многих лет, поэтому на основе записи Sun в этой области я бы не сделал никаких promises об их реализации SPENGO, пока у вас не будет этой птицы.
Для NTLM существует бесплатный проект OSS под названием JCIFS, в котором установлен сервлет-фильтр HTTP-аутентификации NTLM. Однако он использует метод "человек-в-середине" для проверки учетных данных с SMB-сервером, который не работает с NTLMv2 (который постепенно становится необходимой политикой безопасности домена). По этой причине и другие, часть HTTP-фильтра JCIFS планируется удалить. Обратите внимание, что существует ряд побочных эффектов, которые используют JCIFS для реализации той же методики. Поэтому, если вы видите другие проекты, которые заявляют о поддержке SSO NTLM, проверьте мелкий шрифт.
Единственный правильный способ проверки учетных данных NTLM с Active Directory - это вызов NetRLogonSamLogon DCERPC через NETLOGON с Secure Channel. Существует ли такая вещь на Java? Да. Вот он:
http://www.ioplex.com/jespa.html
Jespa - это 100% реализация Java NTLM, поддерживающая NTLMv2, NTLMv1, полные параметры целостности и конфиденциальности и вышеупомянутую проверку подлинности NETLOGON. И он включает в себя HTTP SSO Filter, JAAS LoginModule, HTTP-клиент, клиент и сервер SASL (с привязкой JNDI), общий "поставщик безопасности" для создания пользовательских служб NTLM и т.д.
Mike
Ответ 3
Я только что закончил проект, который использует AD и Java.
Мы использовали Spring ldapTemplate.
AD является совместимым с LDAP (почти), я не думаю, что у вас возникнут проблемы с вашей задачей. Я имею в виду тот факт, что это AD или любой другой сервер LDAP, не имеет значения, хотите ли вы просто подключиться.
Я бы посмотрел: Spring LDAP
У них также есть примеры.
Что касается шифрования, мы использовали SSL-соединение (так было LDAPS). AD должен был быть настроен на порт/протокол SSL.
Но прежде всего, убедитесь, что вы правильно подключаетесь к AD через LDAP IDE. Я использую Apache Directory Studio, это действительно классно, и он написан на Java. Это все, что мне нужно. Для целей тестирования вы также можете установить Apache Directory Server
Ответ 4
Вы просто проверяете учетные данные? В этом случае вы можете просто сделать простые кеберосы и не беспокоиться о LDAP.
Ответ 5
Как сказал ioplex и другие, есть много вариантов. Для аутентификации с использованием LDAP (и API Novell LDAP) я использовал что-то вроде:
LDAPConnection connection = new LDAPConnection( new LDAPJSSEStartTLSFactory() );
connection.connect(hostname, port);
connection.startTLS();
connection.bind(LDAPConnection.LDAP_V3, username+"@"+domain, password.getBytes());
Как "специальная функция", Active Directory позволяет привязывать LDAP к "user @domain" без использования отличительного имени учетной записи. Этот код использует StartTLS для включения шифрования TLS в соединении; другой альтернативой является LDAP через SSL, который не поддерживается моими серверами AD.
Реальный трюк заключается в поиске сервера и хоста; официальным способом является использование поиска DNS SRV (службы) для поиска пакета узлов-кандидатов, а затем выполнить "ping" LDAP на основе UDP (в определенном формате Microsoft), чтобы найти правильный сервер. Если вам интересно, я опубликовал некоторые статьи в блоге о моем путешествии приключений и открытий в этой области.
Если вы хотите использовать аутентификацию имени пользователя и пароля на основе Kerberos, вы смотрите на другой чайник рыбы; он выполним с кодом Java GSS-API, хотя я не уверен, что он выполняет последний шаг для проверки подлинности. (Код, выполняющий проверку, может связываться с сервером AD для проверки имени пользователя и пароля, что приводит к предоставлению билета для пользователя, но для обеспечения того, чтобы сервер AD не выдавал себя за образец, ему также необходимо попытаться получить билет для пользователь к себе, что несколько сложнее.)
Если вы хотите сделать единую регистрацию на основе Kerberos, если ваши пользователи прошли аутентификацию в домене, вы можете сделать это также с кодом Java GSS-API. Я бы опубликовал образец кода, но мне все же нужно превратить свой отвратительный прототип в нечто подходящее для глаз человека. Проверьте некоторый код из SpringSource для некоторого вдохновения.
Если вы ищете NTLM (который мне дал понять, он менее безопасен) или что-то еще, ну, удачи.
Ответ 6
Если все, что вы хотите сделать, является аутентификацией против AD, использующей Kerberos, тогда простую программу http://spnego.sourceforge.net/HelloKDC.java должна сделать это.
Взгляните на документацию по проекту "перед полетом", в которой рассказывается о программе HelloKDC.java.
Ответ 7
http://java.sun.com/docs/books/tutorial/jndi/ldap/auth_mechs.html
Механизм SASL поддерживает Kerberos v4 и v5.
http://java.sun.com/docs/books/tutorial/jndi/ldap/sasl.html
Ответ 8
ldap аутентификация без SSL небезопасна, и каждый может просматривать учетные данные пользователя, поскольку ldap-клиент переносит usernamae и пароль во время операции привязки ldap. Поэтому всегда используйте протокол ldaps.
источник: Аутентификация Ldap Active Directory в Java Spring Безопасность с примером
Ответ 9
Я рекомендую вам посмотреть пакет adbroker проекта oVirt. Он использует Spring -Ldap и модуль входа JAAS Krb5 (с GSSAPI) для аутентификации с использованием Kerberos для серверов Ldap (Active-Directory, ipa, rhds, Tivoli-DS). Ищите код в engine\backend\manager\modules\bll\src\main\java\org\ovirt\engine\core\bll\adbroker
Вы можете использовать git для клонирования репозитория или просмотра с помощью ссылки gerrit