web-dev-qa-db-fra.com

Authentification auprès d'Active Directory avec Java sous Linux

J'ai une tâche simple d'authentification auprès d'Active Directory à l'aide de Java. Juste vérifier les informations d'identification et rien d'autre. Supposons que mon domaine est "fun.xyz.tld", que le chemin de l'unité d'organisation est inconnu et que le nom d'utilisateur/mot de passe est testu/testp. 

Je sais que quelques bibliothèques Java simplifient cette tâche, mais je n'ai pas réussi à les implémenter. La plupart des exemples que j'ai trouvés concernent LDAP en général, pas spécifiquement Active Directory. Émettre une demande LDAP signifie envoyer un chemin d’UO que je n’ai pas. En outre, l'application qui émet une requête LDAP doit déjà être liée à Active Directory pour pouvoir y accéder ... Non sécurisée, car les informations d'identification doivent être stockées dans un emplacement détectable. Je voudrais une liaison de test avec les informations d'identification de test, si possible - cela signifierait que le compte est valide.

Enfin, si possible, existe-t-il un moyen de chiffrer ce mécanisme d'authentification? Je sais que AD utilise Kerberos, mais je ne sais pas si les méthodes LDAP de Java le font.

Quelqu'un at-il un exemple de code de travail? Merci.

71
DV.

Voici le code que je compose à partir d’exemples de ce blog: LINK et cette source: 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;

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

}
47
DV.

Il existe 3 protocoles d'authentification pouvant être utilisés pour effectuer l'authentification entre Java et Active Directory sous Linux ou toute autre plate-forme (et ceux-ci ne sont pas spécifiques aux services HTTP):

  1. Kerberos - Kerberos fournit une authentification unique (SSO) et une délégation, mais les serveurs Web ont également besoin de la prise en charge de SPNEGO pour accepter l'authentification unique via Internet Explorer.

  2. NTLM - NTLM prend en charge la connexion unique via IE (et les autres navigateurs s'ils sont correctement configurés).

  3. LDAP - Une liaison LDAP peut être utilisée pour valider simplement un nom de compte et un mot de passe.

Il existe également un élément appelé "ADFS" qui fournit une authentification unique aux sites Web utilisant SAML qui appelle Windows SSP. En pratique, il s’agit donc d’une manière détournée d’utiliser l’un des protocoles susmentionnés.

Chaque protocole a ses avantages, mais en règle générale, pour une compatibilité maximale, vous devez généralement essayer de "faire comme Windows". Alors, que fait Windows?

Tout d'abord, l'authentification entre deux ordinateurs Windows favorise Kerberos car les serveurs n'ont pas besoin de communiquer avec le DC et les clients peuvent mettre en cache les tickets Kerberos, ce qui réduit la charge sur les contrôleurs de domaine (et parce que Kerberos prend en charge la délégation).

Mais si les parties authentifiantes ne possèdent pas toutes les deux un compte de domaine ou si le client ne peut pas communiquer avec le contrôleur de domaine, NTLM est requis. Ainsi, Kerberos et NTLM ne s'excluent pas mutuellement et NTLM n'est pas obsolète pour Kerberos. En fait, à certains égards, NTLM est meilleur que Kerberos. Notez que lorsque Kerberos et NTLM sont mentionnés dans le même souffle, je dois également mentionner SPENGO et l'authentification intégrée de Windows (IWA). IWA est un terme simple qui désigne essentiellement Kerberos ou NTLM ou SPNEGO pour négocier Kerberos ou NTLM.

L'utilisation d'une liaison LDAP comme moyen de valider les informations d'identification n'est pas efficace et nécessite SSL. Cependant, jusqu'à récemment, la mise en œuvre de Kerberos et de NTLM était difficile, de sorte que l'utilisation de LDAP en tant que service d'authentification de substitution a persisté. Mais à ce stade, il convient généralement de l'éviter. LDAP est un répertoire d'informations et non un service d'authentification. Utilisez-le pour son but prévu.

Alors, comment implémentez-vous Kerberos ou NTLM en Java et dans le contexte d'applications Web en particulier?

Un certain nombre de grandes entreprises telles que Quest Software et Centrify ont des solutions qui mentionnent spécifiquement Java. Je ne peux pas vraiment en parler, car ce sont des "solutions de gestion des identités" à l'échelle de l'entreprise. Il est donc difficile de savoir exactement quels protocoles sont utilisés et comment. Vous devrez les contacter pour plus de détails.

L'implémentation de Kerberos en Java n'est pas très difficile, car les bibliothèques Java standard prennent en charge Kerberos via les classes org.ietf.gssapi. Cependant, jusqu'à récemment, il y avait un obstacle majeur: IE n'envoie pas de jetons Kerberos bruts, mais des jetons SPNEGO. Mais avec Java 6, SPNEGO a été implémenté. En théorie, vous devriez pouvoir écrire du code GSSAPI pouvant authentifier les clients IE. Mais je n'ai pas essayé. L’implémentation de Kerberos par Sun a été une comédie d’erreurs au fil des années. C’est sur la base de la feuille de route de Sun dans ce domaine que je ne ferais aucune promesse quant à leur implémentation de SPENGO tant que vous n’auriez pas cet oiseau à la main.

Pour NTLM, il existe un projet Free OSS appelé JCIFS qui possède un filtre de servlet d'authentification NTLM HTTP. Cependant, il utilise une méthode d'interception pour valider les informations d'identification avec un serveur SMB qui ne fonctionne pas avec NTLMv2 (qui devient lentement une stratégie de sécurité de domaine requise). Pour cette raison, entre autres, la partie Filtre HTTP de JCIFS doit être supprimée. Notez qu'il existe un certain nombre de spin-offs qui utilisent JCIFS pour implémenter la même technique. Par conséquent, si vous voyez d'autres projets prétendant prendre en charge NTLM SSO, vérifiez les détails.

Le seul moyen correct de valider les informations d'identification NTLM avec Active Directory consiste à utiliser l'appel DCRPC NetrLogonSamLogon sur NETLOGON avec Secure Channel. Est-ce qu'une telle chose existe en Java? Oui. C'est ici:

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

Jespa est une implémentation 100% Java NTLM qui prend en charge les options NTLMv2, NTLMv1, d’intégrité totale et de confidentialité et la validation des informations d’identité NETLOGON susmentionnée. Et il inclut un filtre HTTP SSO, un module de connexion JAAS, un client HTTP, un client et un serveur SASL (avec liaison JNDI), un "fournisseur de sécurité" générique pour la création de services NTLM personnalisés, etc.

Mike

94
user8134

Je viens de terminer un projet qui utilise AD et Java ... Nous avons utilisé Spring ldapTemplate.

AD est conforme (presque) à LDAP, je ne pense pas que vous ayez de problèmes avec la tâche que vous avez. Je veux dire le fait que ce soit AD ou tout autre serveur LDAP, peu importe si vous voulez juste vous connecter.

Je voudrais jeter un oeil à: Spring LDAP

Ils ont des exemples aussi.

En ce qui concerne le cryptage, nous avons utilisé la connexion SSL (il s’agissait donc de LDAPS). AD devait être configuré sur un port/protocole SSL.

Mais tout d’abord, assurez-vous que vous pouvez vous connecter correctement à votre AD par l’intermédiaire d’un IDE LDAP. J'utilise Apache Directory Studio , c'est vraiment cool, et c'est écrit en Java. C'est tout ce dont j'avais besoin. À des fins de test, vous pouvez également installer Apache Directory Server

6
Alexandru Luchian

Comme l'ont dit ioplex et d'autres, les options sont nombreuses. Pour m'authentifier à l'aide de LDAP (et de l'API LDAP de Novell), j'ai utilisé quelque chose comme:


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

En tant que "fonctionnalité spéciale", Active Directory autorise les liaisons LDAP avec "utilisateur @ domaine" sans utiliser le nom distinctif du compte. Ce code utilise StartTLS pour activer le cryptage TLS sur la connexion. L'autre alternative est LDAP sur SSL, qui n'est pas pris en charge par les serveurs my AD.

Le vrai truc est de localiser le serveur et l'hôte; La méthode officielle consiste à utiliser une recherche d'enregistrement DNS SRV (service) pour localiser un groupe d'hôtes candidats, puis à effectuer un "ping" LDAP basé sur UDP (dans un format Microsoft particulier) pour localiser le serveur approprié. Si vous êtes intéressé, j'ai posté des articles de blog / concernant mon voyage d'aventure et de découverte dans cette région.

Si vous souhaitez effectuer une authentification de nom d'utilisateur/mot de passe basée sur Kerberos, vous recherchez une autre source de problèmes; c'est faisable avec le code Java GSS-API, bien que je ne sois pas sûr qu'il effectue l'étape finale pour valider l'authentification. (Le code effectuant la validation peut contacter le serveur AD pour vérifier le nom d'utilisateur et le mot de passe, ce qui donne un ticket permettant d'accorder un ticket à l'utilisateur. Toutefois, pour s'assurer que le serveur AD n'est pas emprunté, il doit également essayer d'obtenir un ticket pour. l'utilisateur à lui-même, ce qui est un peu plus compliqué.)

Si vous souhaitez effectuer une connexion unique basée sur Kerberos, en supposant que vos utilisateurs soient authentifiés auprès du domaine, vous pouvez également le faire avec le code Java GSS-API. Je publierais un exemple de code, mais je dois tout de même transformer mon prototype hideux en quelque chose de digne des yeux humains. Découvrez du code de SpringSource pour vous inspirer.

Si vous êtes à la recherche de NTLM (ce qui m'a été mal compris) ou autre chose, bonne chance.

5
Tommy McGuire

Êtes-vous simplement en train de vérifier les informations d'identification? Dans ce cas, vous pouvez simplement faire kerberos et ne pas vous ennuyer avec LDAP.

3
Anthony

Si vous souhaitez simplement vous authentifier auprès de AD à l'aide de Kerberos, un simple programme http://spnego.sourceforge.net/HelloKDC.Java devrait le faire.

Jetez un coup d'œil à la documentation "de pré-vol" du projet qui parle du programme HelloKDC.Java.

2
Pat Gonzalez
1
Mash See

l'authentification LDAP sans SSL n'est pas sécurisée et tout le monde peut voir les informations d'identification de l'utilisateur, car le client LDAP transfère l'utilisateur et le mot de passe lors de l'opération de liaison LDAP. Toujours utiliser le protocole LDAP. source: authentification LDAP Active Directory dans JavaSécurité de printemps avec l'exemple

1
Seema Kiran

Je vous recommande de regarder le paquet adbroker du projet oVirt . Il utilise Spring-Ldap et le module Krb5 JAAS Login (avec GSSAPI) pour s’authentifier à l'aide de Kerberos auprès des serveurs Ldap (Active-Directory, ipa, rhds, Tivoli-DS). Recherchez le code dans engine\backend\manager\modules\bll\src\main\Java\org\ovirt\engine\core\bll\adbroker

Vous pouvez utiliser git pour cloner le référentiel ou naviguer en utilisant le lien gerrit

0
Yair Zaslavsky