76 votes

Authentification contre Active Directory avec Java sous Linux

J'ai une tâche simple d'authentification par rapport à Active Directory en utilisant Java. Je vérifie simplement les informations d'identification et rien d'autre. Disons que mon domaine est "fun.xyz.tld", le chemin OU est inconnu, et le nom d'utilisateur/mot de passe est testu/testp.

Je sais qu'il existe quelques bibliothèques Java qui simplifient cette tâche, mais je n'ai pas réussi à les mettre en œuvre. La plupart des exemples que j'ai trouvés concernaient LDAP en général, et pas spécifiquement Active Directory. L'émission d'une requête LDAP implique l'envoi d'un chemin OU dans celle-ci, ce que je n'ai pas. De plus, l'application qui émet la requête LDAP doit être déjà liée à Active Directory pour pouvoir y accéder... Ce qui n'est pas sûr, puisque les informations d'identification devraient être stockées à un endroit accessible. Je voudrais une liaison de test avec des informations d'identification de test, si possible - cela signifierait que le compte est valide.

Enfin, si possible, existe-t-il un moyen de rendre ce mécanisme d'authentification crypté ? Je sais que AD utilise Kerberos, mais je ne suis pas sûr que les méthodes LDAP de Java le fassent.

Quelqu'un a-t-il un exemple de code fonctionnel ? Merci.

98voto

user8134 Points 1273

Il y a 3 protocoles d'authentification qui peut être utilisé pour effectuer l'authentification entre Java et Active Directory sur Linux ou sur toute autre plate-forme (et ce ne sont pas seulement spécifique pour les services HTTP):

  1. Kerberos Kerberos fournit l'authentification Unique (SSO) et de la délégation, mais les serveurs web doivent également SPNEGO soutien à accepter l'authentification unique par IE.

  2. NTLM - NTLM prend en charge l'authentification unique par IE (et les autres navigateurs si elles sont correctement configurés).

  3. LDAP LDAP bind peut être utilisé pour simplement valider un nom de compte et mot de passe.

Il y a aussi quelque chose appelé "ADF", qui fournit l'authentification unique pour les sites web à l'aide de SAML qui appelle le Windows SSP donc, dans la pratique, c'est essentiellement une façon détournée de l'aide de l'un de l'autre au-dessus des protocoles.

Chaque protocole a ses avantages, mais comme une règle du pouce, pour un maximum de compatibilité, vous devez généralement tenter de "faire en tant que Windows n'". Donc, ce n'est Windows?

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

Mais si l'authentification des parties ne sont pas tous les deux ont des comptes de domaine ou si le client ne peut pas communiquer avec le contrôleur de domaine, NTLM est nécessaire. Si Kerberos et NTLM ne sont pas mutuellement exclusives et NTLM n'est pas rendu obsolète par Kerberos. En fait, à certains égards, NTLM est mieux que Kerberos. Notez que lorsque vous mentionnez Kerberos et NTLM dans le même souffle, je dois aussi mentionner SPENGO et l'Authentification Intégrée de Windows (IWA). IWA est un terme simple qui signifie essentiellement Kerberos ou NTLM ou SPNEGO à négocier Kerberos ou NTLM.

À l'aide d'une liaison LDAP comme un moyen de valider les informations d'identification n'est pas efficace et requiert SSL. Mais jusqu'à récemment, la mise en œuvre de Kerberos et NTLM ont été difficile à l'aide de LDAP comme une maj du service d'authentification a persisté. Mais à ce stade, il doit généralement être évitée. LDAP est un répertoire d'informations et non d'un service d'authentification. L'utiliser pour son usage prévu.

Alors, comment pensez-vous mettre en œuvre Kerberos ou NTLM en Java et dans le contexte des applications web en particulier?

Il y a un certain nombre de grandes entreprises comme Quest Software et Centrify qui ont des solutions qui mentionnent spécifiquement les Java. Je ne peux pas vraiment commenter sur ceux-ci car ils sont à l'échelle de l'entreprise "solutions de gestion des identités", et donc, de la recherche à la commercialisation tour sur leur site web, il est difficile de dire exactement ce que sont les protocoles utilisés et comment. Vous auriez besoin de les contacter pour les détails.

La mise en œuvre de Kerberos en Java n'est pas très difficile comme le standard des bibliothèques Java en charge Kerberos par le biais de l'org.l'ietf.gssapi classes. Cependant, jusqu'à récemment, il a été un obstacle majeur - c'est à dire ne pas envoyer brut des jetons Kerberos, il envoie SPNEGO jetons. Mais avec Java 6, SPNEGO a été mis en œuvre. En théorie, vous devriez être en mesure d'écrire quelques GSSAPI code qui peut s'authentifier IE clients. Mais je n'ai pas essayé. Le Soleil de la mise en œuvre de Kerberos a été une comédie d'erreurs au fil des ans sur la base de Soleil track record dans ce domaine, je ne voudrais pas faire de promesses sur leur SPENGO mise en œuvre jusqu'à ce que vous avez cet oiseau dans la main.

Pour NTLM, il est Gratuit OSS projet appelé JCIFS qui a un NTLM l'authentification HTTP Filtre de Servlet. Cependant, il utilise un man-in-the-middle méthode pour valider les informations d'identification avec un serveur SMB qui ne fonctionne pas avec l'authentification NTLMv2 (qui est en train de devenir une nécessaire stratégie de sécurité du domaine). Pour cette raison et pour d'autres, le Filtre HTTP cadre de JCIFS est prévu pour être retiré. Noter qu'il existe un certain nombre de spin-offs qui utilisent JCIFS pour mettre en œuvre la même technique. Donc, si vous voyez d'autres projets qui prétendent soutenir NTLM SSO, vérifiez les petits caractères.

La seule bonne façon de valider les informations d'identification NTLM avec Active Directory à l'aide de la NetrLogonSamLogon DCERPC appel au NETLOGON avec Canal Sécurisé. Est-ce une telle chose existe pas en Java? Oui. Ici, il est:

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

Jespa est un 100% Java NTLM mise en œuvre qui prend en charge l'authentification NTLMv2, NTLMv1, l'intégrité et la confidentialité des options et de ladite NETLOGON validation d'informations d'identification. Et il comprend un HTTP SSO Filtre, un JAAS LoginModule, client HTTP, SASL le client et le serveur (avec JNDI de liaison), générique de "fournisseur de services de sécurité" pour la création de services NTLM et plus.

Mike

52voto

DV. Points 1755

Voici le code que j'ai mis en place en me basant sur l'exemple de ce blog : LIEN et cette source : LIEN .

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

}

4 votes

import com.sun.jndi.ldap.LdapCtxFactory; - cela ne fonctionnera probablement qu'avec une JVM Sun.

6voto

Alexandru Luchian Points 1614

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

AD est compatible avec LDAP (presque), je ne pense pas que vous aurez des problèmes avec la tâche que vous avez. Je veux dire que le fait que ce soit AD ou n'importe quel autre serveur LDAP n'a pas d'importance si vous voulez juste vous connecter.

J'y jetterais un coup d'oeil : Spring LDAP

Ils ont aussi des exemples.

En ce qui concerne le cryptage, nous avons utilisé une connexion SSL (il s'agissait donc de LDAPS). AD a dû être configuré sur un port/protocole SSL.

Mais avant tout, assurez-vous que vous pouvez vous connecter correctement à votre AD via 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. Pour les tests, vous pouvez également installer Serveur d'annuaire Apache

0 votes

Luchiani, je suis en train de développer une application web pour l'intégration de java spring à share point (Windows), le mien alors que je ne suis pas en mesure de créer l'utilisateur dans l'annuaire actif en utilisant le code java, pouvez-vous partager votre code pour créer l'utilisateur dans l'annuaire actif avec le commentaire afin que je puisse continuer mon travail à temps.

5voto

Tommy McGuire Points 492

Comme ioplex et d'autres l'ont dit, il existe de nombreuses options. Pour s'authentifier en utilisant LDAP (et 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 "user@domain" 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 supporté par Active Directory. mi Serveurs AD.

La méthode officielle consiste à utiliser un enregistrement DNS SRV (service) pour localiser un ensemble d'hôtes candidats, puis à effectuer un "ping" LDAP basé sur UDP (dans un format Microsoft particulier) pour localiser le serveur correct. Si vous êtes intéressé, j'ai mis en ligne quelques articles de blog sur mon voyage d'aventure et de découverte dans cette région.

Si vous voulez faire une authentification par nom d'utilisateur/mot de passe basée sur Kerberos, c'est une autre paire de manches ; 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 aboutit à l'octroi d'un ticket pour l'utilisateur, mais pour s'assurer que le serveur AD n'est pas usurpé, 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 authentification unique basée sur Kerberos, en supposant que vos utilisateurs sont authentifiés auprès du domaine, vous pouvez également le faire avec le code Java GSS-API. Je posterais bien un exemple de code, mais je dois encore transformer mon prototype hideux en quelque chose de convenable pour les yeux humains. Consultez le site un peu de code de SpringSource pour trouver de l'inspiration.

Si vous cherchez NTLM (dont on m'a dit qu'il était moins sûr) ou autre chose, eh bien, bonne chance.

0 votes

Des articles de blog très utiles. Merci !

3voto

Anthony Points 474

Vous vérifiez juste les références ? Dans ce cas, vous pouvez simplement faire kerberos et ne pas s'embêter avec LDAP .

0 votes

Oui, je ne fais que vérifier les accréditations. J'ai édité la question avec une clarification. Le code est-il différent de celui de l'authentification LDAP ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X