A autenticar-se contra a pasta activa com o Java no Linux

Tenho uma tarefa simples de autenticar contra o directório activo usando Java. Apenas a verificar as credenciais e nada mais. Digamos que o meu domínio é "divertido".xyz.tld", ou caminho é desconhecido, e username/senha é testu/testp.

Eu sei que existem algumas bibliotecas Java por aí que simplificam essa tarefa, mas eu não tive sucesso em implementá-las. A maioria dos exemplos que eu encontrei endereçados LDAP em geral, não especificamente Diretório Ativo. A emissão do pedido LDAP significa o envio de um canal horário OU nela, que eu não tenho. Além disso, a aplicação que emite o pedido LDAP já deve estar vinculada ao Diretório Ativo, a fim de acessá-lo... Insegura, uma vez que as credenciais teriam de ser guardadas em algum lugar que pudesse ser descoberto. Eu gostaria de um teste bind com credenciais de teste, se possível - isso significaria que a conta é válida.

Por último, se possível, existe uma maneira de tornar esse mecanismo de autenticação criptografado? Eu sei que o anúncio usa Kerberos, mas não sei se os métodos LDAP de Java fazer.

Alguém tem um exemplo de código de trabalho? Obrigado.

Author: DV., 2008-12-24

9 answers

Existem 3 protocolos de autenticação que podem ser usados para efectuar a autenticação entre Java e Active Directory no Linux ou em qualquer outra plataforma (e estes não são apenas específicos dos serviços HTTP):

  1. Kerberos-Kerberos fornece um Sign-On (SSO) e delegação, mas os servidores web também precisam de suporte SPNEGO para aceitar SSO através do IE.

  2. NTLM-NTLM suporta SSO através do IE (e outros navegadores se estiverem devidamente configurados).

  3. LDAP - Um bind LDAP pode ser usado para simplesmente validar um nome de conta e senha.

Há também algo chamado "ADFS" que fornece SSO para sites que usam SAML que chama para o Windows SSP então, na prática, é basicamente uma forma rotunda de usar um dos outros protocolos acima.

Cada protocolo tem as suas vantagens, mas como regra geral, para a máxima compatibilidade, você deve geralmente tentar "fazer como o Windows faz". Então, O Que Faz O Windows?

Primeiro, autenticação entre duas máquinas Windows favorece Kerberos porque os servidores não precisam se comunicar com o DC e os clientes podem cache bilhetes Kerberos que reduz a carga no DCs (e porque Kerberos suporta delegação).

Mas se as partes que autenticam não tiverem ambas contas de domínio ou se o cliente não puder comunicar com a DC, é necessário o NTLM. Kerberos e NTLM não são mutuamente exclusivos e NTLM não é obsoleto por Kerberos. Na verdade, em alguns aspectos NTLM é melhor do que o Kerberos. Note que ao mencionar Kerberos e NTLM na mesma respiração eu também tenho que mencionar SPENGO e autenticação do Windows Integrado (IWA). IWA é um termo simples que basicamente significa Kerberos ou NTLM ou SPNEGO para negociar Kerberos ou NTLM.

Usar um bind LDAP como forma de validar credenciais não é eficiente e requer SSL. Mas até recentemente a implementação de Kerberos e NTLM tem sido difícil, então o uso de LDAP como um serviço de autenticação make-shift persistiu. Mas em este ponto deve, em geral, ser evitado. LDAP é um diretório de informações e não um serviço de autenticação. Usa-o para o fim pretendido.

Então como você implementa Kerberos ou NTLM em Java e no contexto de aplicações web em particular?

Há uma série de grandes empresas como a Quest Software e a Centrify que têm soluções que mencionam especificamente Java. Eu realmente não posso comentar sobre estes como eles são "soluções de gestão de identidade" em toda a empresa, por isso, de olhar o spin de marketing em seu site, é difícil dizer exatamente quais protocolos estão sendo usados e como. Você precisa contatá-los para os detalhes.

Implementar Kerberos em Java não é muito difícil, uma vez que as bibliotecas Java padrão suportam Kerberos através da org.ietf.aulas de gssapi. No entanto, até recentemente tem havido um grande obstáculo-ou seja, não envia tokens Kerberos raw, ele envia tokens SPNEGO. Mas com Java 6, O SPNEGO foi implementado. Em teoria você deve ser capaz de escrever algum código GSSAPI que pode autenticar clientes IE. Mas ainda não experimentei. A implementação do sol de Kerberos tem sido uma comédia de erros ao longo dos anos, então baseado no histórico da Sun nesta área Eu não faria nenhuma promessa sobre a sua implementação SPENGO até que você tenha esse pássaro na mão.

Para o NTLM, existe um projecto OSS livre chamado JCIFS que tem um filtro de Servlet de autenticação HTTP NTLM. No entanto, ele usa um método man-in-the-middle para validar as credenciais com um servidor SMB isso não funciona com o NTLMv2 (que está lentamente se tornando uma política de segurança de domínio necessária). Por essa razão e outras, a parte do filtro HTTP do JCIFS está agendada para ser removida. Note que há um número de spin-offs que usam JCIFS para implementar a mesma técnica. Então, se você ver outros projetos que afirmam apoiar NTLM SSO, verifique a impressão fina.

A única forma correcta de validar as credenciais NTLM com o directório activo é usar a chamada Dcerpc do Netlogonsamlogon com Canal Seguro. Existe tal coisa em Java? Sim. Aqui está:

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

Jespa é uma implementação NTLM Java 100% que suporta NTLMv2, NTLMv1, completa integridade e opções de confidencialidade e a validação credencial NETLOGON acima mencionada. E inclui um filtro SSO HTTP, um LoginModule JAAS, cliente HTTP, cliente SASL e servidor( com o JNDI binding), "provedor de segurança" genérico para a criação de serviços NTLM personalizados e mais.

Mike

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

Aqui está o código que eu montei baseado no exemplo deste blog: LINK e esta fonte: 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
Acabei de terminar um projeto que usa AD e Java. Usámos ldapTemplate de Primavera.

AD é compatível com LDAP (quase), Eu não acho que você terá quaisquer problemas com a tarefa que você tem. Quero dizer o fato de que é um anúncio ou qualquer outro servidor LDAP não importa se você quer apenas se conectar.

Eu daria uma olhada em: LDAP De Primavera

Eles também têm exemplos.

Quanto à encriptação, usámos a ligação SSL (por isso era LDAPS). O AD teve de ser configurado num SSL Porto / protocolo.

Mas antes de tudo, certifique-se de que pode ligar-se adequadamente ao seu anúncio através de um IDE LDAP. Eu uso Apache Directory Studio , é muito legal, e está escrito em Java. Era tudo o que precisava. Para fins de teste, Poderá também instalar o servidor de Directórios Apache

 6
Author: Alexandru Luchian, 2016-11-07 09:08:36
Como o ioplex e outros já disseram, há muitas opções. Para autenticar usando LDAP( e a API Novell LDAP), eu usei algo como:

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

Como uma "funcionalidade especial", o directório activo permite que o LDAP se ligue ao" utilizador@domínio " sem usar o nome distinto da conta. Este código usa StartTLS para activar a encriptação TLS na ligação; a outra alternativa é LDAP sobre SSL, que não é suportada por Os meus servidores AD.

O verdadeiro truque está em localizando o servidor e a máquina; a forma oficial é usar uma pesquisa de registro DNS SRV (serviço) para localizar um pacote de hosts candidatos, em seguida, fazer um LDAP baseado em UDP "ping" (em um formato particular Microsoft) para localizar o servidor correto. Se você estiver interessado, eu postei alguns artigos do blog sobre minha jornada de aventura e descoberta naquela área.

Se quiser fazer a autenticação de utilizador/senha baseada no Kerberos, está a olhar para outra chaleira de peixe; é possível fazer com o Java GSS-API code, embora eu não tenha certeza se ele executa o passo final para validar a autenticação. (O código que faz a validação pode entrar em contato com o servidor de AD para verificar o nome de usuário e senha, o que resulta em um ticket de concessão de ticket para o usuário, mas para garantir que o servidor de AD não está sendo personificado, ele também precisa tentar obter um ticket para o Usuário para si mesmo, o que é um pouco mais complicado.)

Se quiser fazer um único sinal baseado em Kerberos, assumindo que os seus utilizadores estão autenticados para o domínio, você pode fazer isso também com o código GSS-API Java. Eu colocaria uma amostra de código, mas ainda preciso de transformar o meu protótipo hediondo em algo adequado para os olhos humanos. Confira algum código da SpringSource para alguma inspiração.

Se você está procurando NTLM (que eu fui dado para entender é menos seguro) ou algo mais, bem, boa sorte.

 4
Author: Tommy McGuire, 2009-11-20 19:56:12
Está apenas a verificar as credenciais? Nesse caso, você poderia apenas fazer simples {[[0]} e não se preocupar com LDAP.
 3
Author: Anthony, 2018-01-06 04:27:23

Se tudo o que você quer fazer é autenticar contra o anúncio usando Kerberos, então um simples {[[2]}http://spnego.sourceforge.net/HelloKDC.java o programa deve fazê-lo.

Dê uma olhada na documentação "pré-voo" do projeto, que fala sobre o HelloKDC.programa java.
 2
Author: Pat Gonzalez, 2009-11-04 16:10:22

A autenticação Ldap sem SSL não é segura e qualquer um pode ver a credencial do utilizador porque o cliente ldap transfere usernamae e senha durante a operação LDAP bind, por isso use sempre o protocolo ldaps. fonte: Pasta activa de autenticação Ldap na segurança da mola Java com o exemplo

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

Eu recomendo que você olhe para o pacote adbroker do projetooVirt . Ele usa Spring-Ldap e o módulo de Login Krb5 JAAS (com GSSAPI), a fim de autenticar usando Kerberos contra servidores Ldap (Active-Directory, ipa, rhds, Tivoli-DS). Procure o código no motor de\back\manager\modules\bll\src\main\java\org\ovirt\engine\core\bll\adbroker

Pode usar o git para clonar o repositório ou navegar através do gerrit link

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