Аутентификация в Active Directory с помощью Java в Linux


У меня есть простая задача аутентификации против Active Directory с помощью Java. Просто проверка учетных данных и ничего больше. Допустим, мой домен- " fun.xyz.tld", путь OU неизвестен, а имя пользователя/пароль-testu/testp.

Я знаю, что есть несколько библиотек Java, которые упрощают эту задачу, но мне не удалось их реализовать. Большинство примеров, которые я нашел, адресованы LDAP в целом, а не конкретно Active Directory. Выдача запроса LDAP означает отправку подразделения путь в нем, которого у меня нет. Кроме того, приложение, которое выдает запрос LDAP, должно быть уже привязано к Active Directory для доступа к нему... Небезопасно, так как учетные данные должны были бы храниться где-то обнаруживаемые. Я хотел бы привязать тест с тестовыми учетными данными, если это возможно - это будет означать, что учетная запись действительна.

последние, если возможно, есть ли способ сделать такой механизм аутентификации шифруется? Я знаю, что AD использует Kerberos, но не уверен, что методы LDAP Java делать.

У кого-нибудь есть пример рабочего кода? Спасибо.

9 68

9 ответов:

существует 3 протокола проверки подлинности, которые могут быть использованы для выполнения проверки подлинности между Java и Active Directory на Linux или любой другой платформе (и они не только специфичны для служб HTTP):

  1. Kerberos-Kerberos обеспечивает единый вход (SSO) и делегирование, но веб-серверы также нуждаются в поддержке SPNEGO для принятия SSO через IE.

  2. NTLM-NTLM поддерживает SSO через IE (и другие браузеры, если они правильно сконфигурированный.)

  3. LDAP-привязка LDAP может использоваться для простой проверки имени учетной записи и пароля.

есть также что-то под названием "ADFS", которое предоставляет SSO для веб-сайтов с использованием SAML, который вызывает в Windows SSP, поэтому на практике это в основном окольный способ использования одного из других вышеуказанных протоколов.

каждый протокол имеет свои преимущества, но, как правило, для максимальной совместимости вы должны попытаться " сделать как Windows делает". Так что же делает Windows?

во-первых, проверка подлинности между двумя машинами Windows поддерживает Kerberos, поскольку серверы не должны взаимодействовать с контроллером домена, а клиенты могут кэшировать билеты Kerberos, что снижает нагрузку на контроллеры домена (и поскольку Kerberos поддерживает делегирование).

но если аутентифицирующие стороны не имеют учетных записей домена или если клиент не может связаться с DC, требуется NTLM. Таким образом, Kerberos и NTLM не являются взаимоисключающими и NTLM не является устаревшим Kerberos. На самом деле в некоторых отношениях NTLM лучше, чем Kerberos. Обратите внимание, что при упоминании Kerberos и NTLM на одном дыхании я должен также упомянуть SPENGO и встроенную проверку подлинности Windows (IWA). IWA-это простой термин, который в основном означает Kerberos или NTLM или SPNEGO для согласования Kerberos или NTLM.

использование привязки LDAP в качестве способа проверки учетных данных не является эффективным и требует SSL. Но до недавнего времени реализация Kerberos и NTLM была сложной таким образом, использование LDAP в качестве службы аутентификации make-shift сохранилось. Но в этот момент его вообще следует избегать. LDAP-это каталог информации, а не служба аутентификации. Используйте его по прямому назначению.

Итак, как вы реализуете Kerberos или NTLM в Java и в контексте веб-приложений в частности?

есть ряд крупных компаний, таких как Quest Software и Centrify, которые имеют решения, которые специально упоминают Java. Я не могу прокомментируйте их, поскольку они являются общекорпоративными "решениями для управления идентификацией", поэтому, глядя на маркетинговый спин на своем веб-сайте, трудно точно сказать, какие протоколы используются и как. Вам потребуется связаться с ними для деталей.

реализация Kerberos в Java не очень сложна, поскольку стандартные библиотеки Java поддерживают Kerberos через организацию.группа IETF.классы gssapi с. Однако до недавнего времени было серьезное препятствие-IE не отправляет необработанные маркеры Kerberos, он отправляет Содержит маркеры. Но с Java 6, SPNEGO был реализован. Теоретически вы должны быть в состоянии написать некоторый код GSSAPI, который может аутентифицировать клиентов IE. Но я еще не пробовал. Реализация Sun Kerberos была комедией ошибок на протяжении многих лет, поэтому, основываясь на послужном списке Sun в этой области, я бы не стал давать никаких обещаний об их реализации SPENGO, пока у вас не будет этой птицы в руке.

для NTLM, есть и бесплатный проект, под названием ОСС JCIFS, что имеет проверку подлинности NTLM протокол HTTP Фильтр Сервлетов. Однако он использует метод man-in-the-middle для проверки учетных данных с помощью сервера SMB, который не работает с NTLMv2 (который постепенно становится необходимой политикой безопасности домена). По этой и другим причинам часть фильтра HTTP JCIFS планируется удалить. Обратите внимание, что существует ряд побочных эффектов, которые используют JCIFS для реализации той же техники. Поэтому, если вы видите другие проекты, которые утверждают, что поддерживают NTLM SSO, проверьте мелкий шрифт.

единственный правильный способ проверка учетных данных NTLM с помощью Active Directory использует вызов NetrLogonSamLogon DCERPC через NETLOGON с защищенным каналом. Существует ли такая вещь в Java? Да. Вот это:

http://www.ioplex.com/jespa.html

Jespa-это 100% реализация Java NTLM, которая поддерживает NTLMv2, NTLMv1, параметры полной целостности и конфиденциальности и вышеупомянутую проверку учетных данных NETLOGON. И он включает в себя HTTP SSO фильтр, JAAS LoginModule, HTTP-клиент, Клиент и сервер SASL (с привязкой JNDI), общий "поставщик безопасности" для создания пользовательских служб NTLM и многое другое.

Майк

вот код, который я собрал на основе примера из ЭТОТ БЛОГ: ссылке и этого источника: ссылке.

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;

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's 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<String> groups = new ArrayList<String>();
            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(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());
            }

        } 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();
    }

}

Я только что закончил проект, который использует AD и Java. Мы использовали пружину ldapTemplate.

AD совместим с LDAP (почти), я не думаю, что у вас будут какие-либо проблемы с задачей, которую вы имеете. Я имею в виду тот факт, что это AD или любой другой сервер LDAP, не имеет значения, хотите ли вы просто подключиться.

Я бы посмотрел на: Весна LDAP

у них тоже есть примеры.

Что касается шифрования, мы использовали SSL-соединение (так что это был LDAPS). Объявление было настраивается на порт/протокол SSL.

но прежде всего, убедитесь, что вы можете правильно подключиться к своему объявлению через IDE LDAP. Я использую Apache Directory Studio, это действительно круто, и это написано на Java. Это все, что мне было нужно. Для целей тестирования вы также можете установить Сервер Каталогов Apache

Как ioplex и другие сказали, есть много вариантов. Для аутентификации с помощью LDAP (и Novell LDAP API), я использовал что-то вроде:


LDAPConnection connection = new LDAPConnection( new LDAPJSSEStartTLSFactory() );
connection.connect(hostname, port);
connection.startTLS();
connection.bind(LDAPConnection.LDAP_V3, username+"@"+domain, password.getBytes());

в качестве" специальной функции "Active Directory позволяет привязывать LDAP к" [email protected] " без использования отличительного имени учетной записи. Этот код использует StartTLS для включения шифрования TLS в соединении; другой альтернативой является LDAP через SSL, который не поддерживается мой рекламные серверы.

в реальный трюк заключается в поиске сервера и Хоста; официальный способ-использовать поиск записей DNS SRV (service) для поиска пакета потенциальных хостов, а затем выполнить LDAP-пинг на основе UDP (в определенном формате Microsoft), чтобы найти правильный сервер. Если вам интересно, я опубликовал некоторые статьи о моем путешествии, приключений и открытий в этой области.

Если вы хотите сделать проверку подлинности имени пользователя/пароля на основе Kerberos, вы смотрите на другой чайник fish; это выполнимо с кодом Java GSS-API, хотя я не уверен, что он выполняет последний шаг для проверки подлинности. (Код, выполняющий проверку, может связаться с сервером объявлений, чтобы проверить имя пользователя и пароль, что приводит к выдаче билета для пользователя, но чтобы убедиться, что сервер объявлений не олицетворяется, ему также нужно попытаться получить билет для пользователя к себе, что несколько сложнее.)

Если вы хотите сделать единый вход на основе Kerberos, предполагая, что ваши пользователи аутентифицируются в домене, вы можете сделать это также с помощью кода Java GSS-API. Я бы опубликовал пример кода, но мне все еще нужно превратить мой отвратительный прототип во что-то подходящее для человеческих глаз. Проверьте какой-то код из SpringSource для некоторых вдохновения.

Если вы ищете NTLM (который мне дали понять, что он менее безопасен) или что-то еще, ну, удачи.

вы просто проверяете учетные данные? В этом случае вы могли бы просто сделать простой kerberos и не заморачивайтесь с LDAP.

Если все, что вы хотите сделать, это аутентифицировать против AD с помощью Kerberos, то простой http://spnego.sourceforge.net/HelloKDC.java программа должна это сделать.

взгляните на" предполетную " документацию проекта, в которой говорится о HelloKDC.программа Java.

аутентификация ldap без SSL небезопасна, и любой может просматривать учетные данные пользователя, потому что клиент ldap передает usernamae и пароль во время операции привязки ldap, поэтому всегда используйте протокол ldaps. источник: аутентификация Ldap Active directory в Java Spring Security с примером