Authentification avec Active Directory avec Java sous Linux


J'ai une tâche simple d'authentification contre Active Directory en utilisant Java. Juste vérifier les informations d'identification et rien d'autre. Disons que mon domaine est "amusant.xyz.tld", OU path 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 implémenter. La plupart des exemples que j'ai trouvés adressaient LDAP en général, pas spécifiquement Active Directory. L'émission d'une requête LDAP signifie l'envoi d'un chemin OU dans c', je n'en ai pas. En outre, l'application qui émet une demande LDAP doit être déjà liée à Active Directory afin d'y accéder... Non sécurisé, car les informations d'identification devraient être stockées quelque part découvrable. 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 crypter un tel mécanisme d'authentification? Je sais que AD utilise Kerberos, mais je ne sais pas si les méthodes LDAP de Java faire.

Quelqu'un a un exemple de code de travail? Grâce.

Author: DV., 2008-12-24

9 answers

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

  1. Kerberos-Kerberos fournit l'authentification unique (SSO) et la délégation, mais les serveurs Web ont également besoin du support SPNEGO pour accepter l'authentification unique via IE.

  2. NTLM-NTLM prend en charge l'authentification unique via IE (et d'autres navigateurs s'ils sont correctement configurés).

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

Il y a aussi quelque chose appelé "ADFS" qui fournit une authentification unique pour les sites Web utilisant SAML qui appelle dans le SSP Windows, donc en pratique, c'est essentiellement une façon détournée d'utiliser l'un des autres protocoles ci-dessus.

Chaque protocole a ses avantages mais en règle générale, pour une compatibilité maximale, vous devriez généralement essayer de "faire comme Windows". Donc, ce n'est Windows faire?

Tout d'Abord, l'authentification entre deux machines Windows favorise Kerberos parce que 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 le DCs (et parce que Kerberos prend en charge la délégation).

Mais si les parties authentifiantes n'ont pas les deux comptes de domaine ou si le client ne peut pas communiquer avec le DC, NTLM est requis. Kerberos et NTLM ne sont donc pas mutuellement exclusifs et NTLM n'est pas obsolète par Kerberos. En fait, à certains égards, NTLM est meilleur que Kerberos. Notez que lorsque vous mentionnez Kerberos et NTLM dans le même souffle, je dois également mentionner SPENGO et l'authentification Windows intégrée (IWA). IWA est un terme simple qui signifie 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. Mais jusqu'à récemment, l'implémentation de Kerberos et NTLM a été difficile, donc l'utilisation de LDAP comme service d'authentification make-shift a persisté. Mais à ce stade il devrait généralement être évité. LDAP est un répertoire d'informations et non un service d'authentification. L'utiliser pour son usage prévu.

Alors, comment implémentez-vous 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 Java. Je ne peux pas vraiment commenter ceux-ci car ce sont des "solutions de gestion d'identité" à l'échelle de l'entreprise, donc, en regardant le marketing tourner sur leur site Web, il est difficile de dire exactement quels protocoles sont utilisés et comment. Vous devrez les contacter pour les détails.

Implémenter Kerberos en Java n'est pas terriblement difficile car les bibliothèques Java standard prennent en charge Kerberos via l'organisation.IETF.classes gssapi. Cependant, jusqu'à récemment, il y avait un obstacle majeur - c'est-à-dire qu'il n'envoie pas de jetons Kerberos bruts, il envoie des jetons SPNEGO. Mais avec Java 6, SPNEGO a été implémenté. En théorie vous devriez pouvoir écrire du code GSSAPI cela peut authentifier les clients IE. Mais je n'ai pas essayé. La mise en œuvre Sun de Kerberos a été une comédie d'erreurs au fil des ans, donc sur la base des antécédents de Sun dans ce domaine, je ne ferais aucune promesse sur leur mise en œuvre SPENGO jusqu'à ce que vous ayez cet oiseau en main.

Pour NTLM, il existe un projet OSS libre appelé JCIFS qui a un filtre de servlet d'authentification HTTP NTLM. Cependant, il utilise une méthode man-in-the-middle pour valider les informations d'identification avec un serveur SMB qui ne le fait pas travaillez avec NTLMv2 (qui devient lentement une politique de sécurité de domaine requise). Pour cette raison et d'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. Donc, si vous voyez d'autres projets qui prétendent prendre en charge NTLM SSO, vérifiez les petits caractères.

La seule façon correcte de valider les informations d'identification NTLM avec Active Directory est d'utiliser l'appel NetrLogonSamLogon DCERPC sur NETLOGON avec Secure Channel. Une telle chose existe-t-elle en Java? Oui. Le voici:

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

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

Mike

 92
Author: user8134, 2009-01-17 18:14:13

Voici le code que j'ai mis en place sur la base de l'exemple 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
Author: DV., 2018-03-21 22:02:27

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

AD est conforme LDAP (presque), je ne pense pas que vous aurez des problèmes avec la tâche que vous avez. Je veux dire le fait qu'il s'agisse d'AD ou de tout autre serveur LDAP, peu importe si vous voulez simplement vous connecter.

Je voudrais jeter un oeil à: Spring LDAP

Ils ont aussi des exemples.

En ce qui concerne le cryptage, nous avons utilisé la connexion SSL (donc c'était LDAPS). AD devait être configuré sur un SSL port/protocole.

Mais tout d'abord, assurez-vous de pouvoir vous connecter correctement à votre ANNONCE via unE LDAP. J'utilise Apache Directory Studio, c'est vraiment cool, et il est écrit en Java. C'est tout ce dont j'avais besoin. À des fins de test, vous pouvez également installer Apache Directory Server

 6
Author: Alexandru Luchian, 2016-11-07 09:08:36

Comme ioplex et d'autres l'ont dit, il existe de nombreuses options. Pour m'authentifier à l'aide de LDAP (et de l'API Novell LDAP), 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 permet de lier LDAP à "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 pris en charge par mes serveurs AD.

La vraie astuce est de localiser le serveur et l'hôte; la manière officielle consiste à utiliser une recherche d'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 posté 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, vous regardez une autre bouilloire de poisson; c'est faisable avec Java GSS-API code, bien que je ne sois pas sûr qu'il effectue la dernière étape pour valider l'authentification. (Le code effectuant la validation peut contacter le serveur PUBLICITAIRE pour vérifier le nom d'utilisateur et le mot de passe, ce qui entraîne l'octroi d'un ticket pour l'utilisateur, mais pour s'assurer que le serveur publicitaire n'est pas usurpé, il doit également essayer d'obtenir un ticket pour l'utilisateur, 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 le faire aussi bien avec le code Java GSS-API. Je posterais un exemple de code, mais je dois encore transformer mon prototype hideux en quelque chose de digne des yeux humains. Découvrez du code de SpringSource pour vous inspirer.

Si vous cherchez NTLM (ce qui m'a été donné de comprendre est moins sûr) ou autre chose, eh bien, bonne chance.

 4
Author: Tommy McGuire, 2009-11-20 19:56:12

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

 3
Author: Anthony, 2018-01-06 04:27:23

Si tout ce que vous voulez faire est de vous authentifier contre AD en utilisant Kerberos, alors un simple http://spnego.sourceforge.net/HelloKDC.java le programme devrait le faire.

Jetez un oeil à la documentation "pré-vol" du projet qui parle du HelloKDC.programme java.

 2
Author: Pat Gonzalez, 2009-11-04 16:10:22
 1
Author: Mash See, 2008-12-23 22:17:29

L'authentification Ldap sans SSL n'est pas sûre et n'importe qui peut afficher les informations d'identification de l'utilisateur car le client ldap transfère les noms d'utilisateur et le mot de passe pendant l'opération de liaison ldap, Utilisez donc toujours le protocole ldaps. source: Authentification Ldap Active directory dans Java Spring Security avec l'exemple

 1
Author: Seema Kiran, 2011-11-19 16:35:42

Je vous recommande de regarder le paquet adbroker du projet oVirt. Il utilise Spring-Ldap et le module de connexion Krb5 JAAS (avec GSSAPI) afin de s'authentifier en utilisant Kerberos contre les serveurs Ldap (Active-Directory, ipa, rhds, Tivoli-DS). Recherchez le code à moteur\backend\manager\modules\bll\src\main\java\org\ovirt\engine\core\bll\adbroker

Vous pouvez utiliser git pour cloner le dépôt ou naviguer en utilisant le liengerrit

 0
Author: Yair Zaslavsky, 2012-06-18 19:06:02