web-dev-qa-db-ja.com

LinuxでJavaを使用したActive Directoryに対する認証

Javaを使用してActive Directoryに対して認証する簡単なタスクがあります。資格情報を確認するだけです。私のドメインが「fun.xyz.tld」であり、OUパスが不明であり、ユーザー名/パスワードがtestu/testpであるとします。

私は、このタスクを簡素化するいくつかのJavaライブラリが存在することを知っていますが、それらを実装することに成功しませんでした。 LDAP要求を発行するということは、その中にOUパスを送信することを意味しますが、LDAP要求を発行するアプリケーションは、アクセスするためにActive Directoryに既にバインドされている必要があります。可能であれば、テスト資格情報を使用したテストバインドが必要です-これは、アカウントが有効であることを意味します。

最後に、可能であれば、そのような認証メカニズムを暗号化する方法はありますか? ADはKerberosを使用することは知っていますが、JavaのLDAPメソッドが使用するかどうかはわかりません。

誰かが動作するコードの例を持っていますか?ありがとう。

73
DV.

このブログの例に基づいて作成したコードは次のとおりです。 [〜#〜] link [〜#〜] およびこのソース: [〜#〜] 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();
    }

}
49
DV.

JavaとLinuxまたは他のプラットフォーム上のActive Directoryとの間の認証を実行するために使用できる3つの認証プロトコルがあります(これらはHTTPサービスに固有のものではありません):

  1. Kerberos-Kerberosはシングルサインオン(SSO)および委任を提供しますが、WebサーバーはIEを介してSSOを受け入れるためにSPNEGOサポートも必要とします。

  2. NTLM-NTLMはIE(および適切に構成されている場合は他のブラウザ)を介してSSOをサポートします。

  3. LDAP-LDAPバインドを使用して、アカウント名とパスワードを簡単に検証できます。

また、Windows SSPを呼び出すSAMLを使用するWebサイトにSSOを提供する「ADFS」と呼ばれるものもあります。したがって、実際には基本的に上記の他のプロトコルのいずれかを使用する方法です。

各プロトコルには利点がありますが、経験則として、互換性を最大限に高めるには、一般的に「Windowsと同じように」する必要があります。それでは、Windowsは何をするのでしょうか?

まず、2台のWindowsマシン間の認証では、サーバーがDCと通信する必要がなく、クライアントがKerberosチケットをキャッシュしてDCの負荷を軽減するため(Kerberosは委任をサポートするため)Kerberosを優先します。

ただし、認証側の両方にドメインアカウントがない場合、またはクライアントがDCと通信できない場合は、NTLMが必要です。したがって、KerberosとNTLMは相互に排他的ではなく、NTLMはKerberosによって廃止されません。実際、いくつかの点でNTLMはKerberosより優れています。 KerberosとNTLMについて同じように言及する場合、SPENGOとIntegrated Windows Authentication(IWA)も言及する必要があることに注意してください。 IWAは基本的にKerberosまたはNTLMまたはSPNEGOを意味する単純な用語で、KerberosまたはNTLMをネゴシエートします。

資格情報を検証する方法としてLDAPバインドを使用するのは効率的ではなく、SSLが必要です。しかし、最近までKerberosとNTLMの実装は困難であったため、LDAPを仮の認証サービスとして使用し続けています。しかし、この時点では一般的には避けるべきです。 LDAPは情報のディレクトリであり、認証サービスではありません。意図した目的に使用します。

では、Javaで、特にWebアプリケーションのコンテキストでKerberosまたはNTLMをどのように実装しますか?

Quest SoftwareやCentrifyなど、Javaに特化したソリューションを提供する大企業が多数あります。これらは全社的な「アイデンティティ管理ソリューション」であるため、実際にコメントすることはできません。したがって、Webサイトでのマーケティングスピンを見て、どのプロトコルがどのように使用されているかを正確に伝えることは困難です。詳細については、それらに連絡する必要があります。

JavaでのKerberosの実装は、標準のJavaライブラリがorg.ietf.gssapiクラスを介してKerberosをサポートしているため、それほど難しくはありません。しかし、最近までは、ハードル-IEは生のKerberosトークンを送信せず、SPNEGOトークンを送信します。ただし、Java 6、SPNEGOが実装されています。理論的には、 IEクライアントを認証できるGSSAPIコードを作成します。しかし、私は試していません。SunのKerberos実装は、長年にわたってエラーの喜劇でした。その鳥を手に入れるまで、私は彼らのSPENGOの実施について何も約束しません。

NTLMには、NTLM HTTP認証サーブレットフィルターを持つJCIFSと呼ばれる無料のOSSプロジェクトがあります。ただし、man-in-the-middleメソッドを使用して、SMB NTLMv2で動作しないサーバー(ゆっくりと必須のドメインセキュリティポリシーになりつつあります)で資格情報を検証します。 JCIFSのHTTPフィルター部分は削除される予定ですJCIFSを使用して同じ手法を実装するスピンオフが多数あることに注意してください。NTLMSSOをサポートすると主張する他のプロジェクトを見る場合は、細字部分を確認してください。

Active DirectoryでNTLM資格情報を検証する唯一の正しい方法は、セキュアチャネルでNETLOGONを介したNetrLogonSamLogon DCERPC呼び出しを使用することです。そのようなことはJavaに存在しますか?はい。ここにあります:

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

Jespaは100%Java NTLMv2、NTLMv1、完全な整合性と機密性のオプション、前述のNETLOGON資格情報の検証をサポートするNTLM実装です。HTTPSSOフィルター、JAAS LoginModule、HTTPクライアント、 SASLクライアントおよびサーバー(JNDIバインディングを使用)、カスタムNTLMサービスなどを作成するための汎用「セキュリティプロバイダー」。

マイク

96
user8134

ADとJavaを使用するプロジェクトを終了しました。 Spring ldapTemplateを使用しました。

ADは(ほぼ)LDAPに準拠しているので、タスクに問題はないと思います。 ADまたは他のLDAPサーバーであるという事実を意味します。接続するだけなら問題ではありません。

私は見てみます: Spring LDAP

例もあります。

暗号化に関しては、SSL接続を使用しました(つまりLDAPSでした)。 ADはSSLポート/プロトコルで構成する必要がありました。

ただし、最初に、LDAP IDEを介してADに適切に接続できることを確認してください。 Apache Directory Studio を使用します。本当にクールで、Javaで書かれています。それが私が必要とするすべてです。テスト目的で Apache Directory Server をインストールすることもできます

6

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はアカウントの識別名を使用せずに「user @ domain」に対するLDAPバインドを許可します。このコードは、StartTLSを使用して、接続でTLS暗号化を有効にします。もう1つの選択肢はLDAP over SSLです。これは、myADサーバーではサポートされていません。

本当のトリックは、サーバーとホストを見つけることです。公式な方法は、DNS SRV(サービス)レコードルックアップを使用して候補ホストのバンドルを検索し、UDPベースのLDAP「ping」(特定のMicrosoft形式)を実行して正しいサーバーを検索することです。興味のある方は、その地域での冒険と発見の旅について ブログ記事 を投稿しました。

Kerberosベースのユーザー名/パスワード認証を行いたい場合は、別のやかんを見ています。 Java GSS-APIコードで実行可能ですが、認証を検証する最終ステップを実行するかどうかはわかりません。検証を実行するコードは、ADサーバーに接続してユーザー名を確認できますパスワードを使用すると、チケットがユーザーにチケットを付与されますが、ADサーバーが偽装されないようにするには、ユーザー自身のチケットを取得する必要があります。これはやや複雑です。)

ユーザーがドメインに認証されていると仮定して、Kerberosベースのシングルサインオンを行いたい場合は、Java GSS-APIコードで同様に行うことができます。コードを投稿しますサンプルですが、私はまだ恐ろしいプロトタイプを人間の目に合うものに変える必要があります。 SpringSourceのコード いくつかのインスピレーションを確認してください。

NTLM(私が理解するために与えられた安全性が低い)または何か他のものを探しているなら、幸運を祈ります。

5
Tommy McGuire

資格情報を確認していますか?その場合、単純なkerberosを実行でき、LDAPに煩わされることはありません。

3
Anthony

Kerberosを使用してADに対して認証するだけであれば、単純な http://spnego.sourceforge.net/HelloKDC.Java プログラムで実行する必要があります。

HelloKDC.Javaプログラムについて説明しているプロジェクトの「プリフライト」ドキュメントをご覧ください。

2
Pat Gonzalez
1
Mash See

sSLを使用しないldap認証は安全ではなく、ldapバインド操作中にldapクライアントがユーザー名とパスワードを転送するため、だれでもユーザー資格情報を表示できます。したがって、常にldapsプロトコルを使用してください。ソース: Java Spring Security with Example)のActive DirectoryのLdap認証

1
Seema Kiran

oVirt プロジェクトのadbrokerパッケージをご覧になることをお勧めします。 Ldapサーバー(Active-Directory、ipa、rhds、Tivoli-DS)に対してKerberosを使用して認証するために、Spring-LdapとKrb5 JAAS Loginモジュール(GSSAPIを使用)を使用します。 engine\backend\manager\modules\bll\src\main\Java\org\ovirt\engine\core\bll\adbrokerでコードを探します

Gitを使用してリポジトリを複製するか、 gerrit リンクを使用して参照できます

0
Yair Zaslavsky