76

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

このタスクを単純化する Java ライブラリがいくつかあることは知っていますが、それらの実装には成功しませんでした。私が見つけたほとんどの例は、特に Active Directory ではなく、一般的な LDAP に対応しています。LDAP リクエストを発行するということは、そこに OU パスを送信することを意味しますが、それは私にはありません。また、LDAP 要求を発行するアプリケーションは、アクセスするために Active Directory に既にバインドされている必要があります...資格情報を検出可能な場所に保存する必要があるため、安全ではありません。可能であれば、テスト資格情報を使用したテスト バインドが必要です。これは、アカウントが有効であることを意味します。

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

作業コードの例はありますか? ありがとう。

4

9 に答える 9

98

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は何をしますか?

まず、サーバーが DC と通信する必要がなく、クライアントが Kerberos チケットをキャッシュできるため、2 台の Windows マシン間の認証では 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 は情報のディレクトリであり、認証サービスではありません。本来の目的で使用してください。

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

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

標準 Java ライブラリは org.ietf.gssapi クラスを通じて Kerberos をサポートしているため、Java での Kerberos の実装はそれほど難しくありません。ただし、最近まで大きなハードルがありました。IE は未加工の Kerberos トークンを送信せず、SPNEGO トークンを送信します。しかし、Java 6 では SPNEGO が実装されています。理論的には、IE クライアントを認証できる GSSAPI コードを記述できるはずです。しかし、私はそれを試していません。Sun による Kerberos の実装は、何年にもわたってエラーの連続でした。そのため、この分野での Sun の実績に基づいて、その鳥を手にするまで SPENGO の実装について約束することはできません。

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

Active Directory で NTLM クレデンシャルを検証する唯一の正しい方法は、セキュア チャネルを使用して NETLOGON 経由で NetrLogonSamLogon DCERPC 呼び出しを使用することです。そのようなことはJavaに存在しますか?はい。ここにあります:

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

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

マイク

于 2009-01-17T18:14:13.943 に答える
53

このブログの例に基づいてまとめたコードは次のとおりです: 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();
    }

}
于 2008-12-26T19:51:44.513 に答える
6

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

AD は LDAP に (ほぼ) 準拠しています。この作業で問題が発生することはないと思います。つまり、それが AD またはその他の LDAP サーバーであるということは、単に接続したい場合でも問題ありません。

私は見てみましょう:Spring LDAP

彼らにも例があります。

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

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

于 2008-12-23T23:55:38.300 に答える
3

資格情報を確認しているだけですか?その場合kerberos、気にせずに単純に行うことができますLDAP

于 2008-12-23T21:49:31.347 に答える
2

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

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

于 2009-11-04T16:10:22.657 に答える
1

http://java.sun.com/docs/books/tutorial/jndi/ldap/auth_mechs.html

SASLメカニズムはKerberosv4およびv5をサポートします。 http://java.sun.com/docs/books/tutorial/jndi/ldap/sasl.html

于 2008-12-23T22:17:29.293 に答える
1

SSL を使用しない LDAP 認証は安全ではなく、LDAP クライアントは LDAP バインド操作中にユーザー名とパスワードを転送するため、誰でもユーザー資格情報を表示できます。そのため、常に LDAP プロトコルを使用します。出典: Java Spring Security の LDAP 認証 Active directory with Example

于 2011-11-19T16:35:42.913 に答える
0

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

gitを使用してリポジトリのクローンを作成するか、gerritリンクを使用して参照できます

于 2012-06-18T19:06:02.013 に答える