简体   繁体   English

在Linux上使用Java对Active Directory进行身份验证

[英]Authenticating against Active Directory with Java on Linux

I have a simple task of authenticating against Active Directory using Java. 我有一个简单的任务,使用Java对Active Directory进行身份验证。 Just verifying credentials and nothing else. 只是验证凭据而不是其他任何内容。 Let's say my domain is "fun.xyz.tld", OU path is unknown, and username/password is testu/testp. 假设我的域名为“fun.xyz.tld”,OU路径未知,用户名/密码为testu / testp。

I know there's a few Java libraries out there that simplify this task, but I wasn't successful at implementing them. 我知道有一些Java库可以简化这项任务,但我没有成功实现它们。 Most examples that I've found addressed LDAP in general, not specifically Active Directory. 我发现的大多数示例都是针对LDAP的,而不是特定的Active Directory。 Issuing LDAP request means sending an OU path in it, which I don't have. 发出LDAP请求意味着在其中发送OU路径,这是我没有的。 Also, the application that issues LDAP request should be already bound to Active Directory in order to access it... Insecure, since the credentials would have to be stored someplace discoverable. 此外,发出LDAP请求的应用程序应该已绑定到Active Directory以便访问它...不安全,因为凭据必须存储在某处可被发现的位置。 I would like a test bind with test credentials, if possible - this would mean that account is valid. 如果可能的话,我希望测试绑定测试凭据 - 这意味着帐户有效。

Last, if possible, is there a way to make such authentication mechanism encrypted? 最后,如果可能的话,有没有办法使这种认证机制加密? I know that AD uses Kerberos, but not sure if Java's LDAP methods do. 我知道AD使用Kerberos,但不确定Java的LDAP方法是否可以。

Does anyone has an example of working code? 有没有人有一个工作代码的例子? Thanks. 谢谢。

There are 3 authentication protocols that can be used to perform authentication between Java and Active Directory on Linux or any other platform (and these are not just specific to HTTP services): 有3种身份验证协议可用于在Linux或任何其他平台上执行Java和Active Directory之间的身份验证(这些协议不仅仅针对HTTP服务):

  1. Kerberos - Kerberos provides Single Sign-On (SSO) and delegation but web servers also need SPNEGO support to accept SSO through IE. Kerberos - Kerberos提供单点登录(SSO)和委派,但Web服务器也需要SPNEGO支持才能通过IE接受SSO。

  2. NTLM - NTLM supports SSO through IE (and other browsers if they are properly configured). NTLM - NTLM通过IE(以及其他浏览器,如果它们已正确配置)支持SSO。

  3. LDAP - An LDAP bind can be used to simply validate an account name and password. LDAP - LDAP绑定可用于简单地验证帐户名和密码。

There's also something called "ADFS" which provides SSO for websites using SAML that calls into the Windows SSP so in practice it's basically a roundabout way of using one of the other above protocols. 还有一种称为“ADFS”的东西,它为使用调用Windows SSP的SAML的网站提供SSO,因此在实践中它基本上是使用上述其他协议之一的迂回方式。

Each protocol has it's advantages but as a rule of thumb, for maximum compatibility you should generally try to "do as Windows does". 每个协议都有它的优点,但根据经验,为了获得最大的兼容性,你通常应该尝试“像Windows一样”。 So what does Windows do? 那么Windows做什么?

First, authentication between two Windows machines favors Kerberos because servers do not need to communicate with the DC and clients can cache Kerberos tickets which reduces load on the DCs (and because Kerberos supports delegation). 首先,两台Windows机器之间的身份验证支持Kerberos,因为服务器不需要与DC通信,客户端可以缓存Kerberos票证,从而减少DC上的负载(并且因为Kerberos支持委派)。

But if the authenticating parties do not both have domain accounts or if the client cannot communicate with the DC, NTLM is required. 但是,如果身份验证方都没有域帐户,或者客户端无法与DC通信,则需要NTLM。 So Kerberos and NTLM are not mutually exclusive and NTLM is not obsoleted by Kerberos. 因此,Kerberos和NTLM不是互斥的,并且Kerberos不会废弃NTLM。 In fact in some ways NTLM is better than Kerberos. 事实上,在某些方面,NTLM比Kerberos更好。 Note that when mentioning Kerberos and NTLM in the same breath I have to also mention SPENGO and Integrated Windows Authentication (IWA). 请注意,当同时提到Kerberos和NTLM时,我还要提到SPENGO和集成Windows身份验证(IWA)。 IWA is a simple term that basically means Kerberos or NTLM or SPNEGO to negotiate Kerberos or NTLM. IWA是一个简单的术语,基本上意味着Kerberos或NTLM或SPNEGO协商Kerberos或NTLM。

Using an LDAP bind as a way to validate credentials is not efficient and requires SSL. 使用LDAP绑定作为验证凭据的方法效率不高,需要SSL。 But until recently implementing Kerberos and NTLM have been difficult so using LDAP as a make-shift authentication service has persisted. 但直到最近,实施Kerberos和NTLM一直很困难,因此使用LDAP作为生成班次认证服务仍然存在。 But at this point it should generally be avoided. 但在这一点上通常应该避免。 LDAP is a directory of information and not an authentication service. LDAP是信息目录,而不是身份验证服务。 Use it for it's intended purpose. 将它用于预期目的。

So how do you implement Kerberos or NTLM in Java and in the context of web applications in particular? 那么如何在Java中以及在Web应用程序的上下文中实现Kerberos或NTLM呢?

There are a number of big companies like Quest Software and Centrify that have solutions that specifically mention Java. 像Quest Software和Centrify这样的大公司都有专门提到Java的解决方案。 I can't really comment on these as they are company-wide "identity management solutions" so, from looking the marketing spin on their website, it's hard to tell exactly what protocols are being used and how. 我无法对这些进行评论,因为它们是公司范围内的“身份管理解决方案”,所以,通过查看其网站上的营销活动,很难确切地说明正在使用哪些协议以及如何使用。 You would need to contact them for the details. 您需要与他们联系以获取详细信息。

Implementing Kerberos in Java is not terribly hard as the standard Java libraries support Kerberos through the org.ietf.gssapi classes. 在Java中实现Kerberos并不是非常困难,因为标准Java库通过org.ietf.gssapi类支持Kerberos。 However, until recently there's been a major hurdle - IE doesn't send raw Kerberos tokens, it sends SPNEGO tokens. 然而,直到最近还存在一个主要障碍 - IE不发送原始Kerberos令牌,它发送SPNEGO令牌。 But with Java 6, SPNEGO has been implemented. 但是使用Java 6,SPNEGO已经实现。 In theory you should be able to write some GSSAPI code that can authenticate IE clients. 从理论上讲,您应该能够编写一些可以验证IE客户端的GSSAPI代码。 But I haven't tried it. 但我没试过。 The Sun implementation of Kerberos has been a comedy of errors over the years so based on Sun's track record in this area I wouldn't make any promises about their SPENGO implementation until you have that bird in hand. 多年来,Sun实施的Kerberos一直是一个错误的喜剧,所以根据Sun在这个领域的记录,我不会对他们的SPENGO实施做出任何承诺,直到你掌握了这只鸟。

For NTLM, there is a Free OSS project called JCIFS that has an NTLM HTTP authentication Servlet Filter. 对于NTLM,有一个名为JCIFS的免费OSS项目,它具有NTLM HTTP身份验证Servlet过滤器。 However it uses a man-in-the-middle method to validate the credentials with an SMB server that does not work with NTLMv2 (which is slowly becoming a required domain security policy). 但是,它使用中间人方法来验证使用不能与NTLMv2一起使用的SMB服务器的凭证(这正逐渐成为必需的域安全策略)。 For that reason and others, the HTTP Filter part of JCIFS is scheduled to be removed. 出于这个原因和其他原因,计划删除JCIFS的HTTP过滤器部分。 Note that there are number of spin-offs that use JCIFS to implement the same technique. 请注意,有许多衍生产品使用JCIFS来实现相同的技术。 So if you see other projects that claim to support NTLM SSO, check the fine print. 因此,如果您看到声称支持NTLM SSO的其他项目,请检查细则。

The only correct way to validate NTLM credentials with Active Directory is using the NetrLogonSamLogon DCERPC call over NETLOGON with Secure Channel. 使用Active Directory验证NTLM凭据的唯一正确方法是使用带有安全通道的NETLOGON上的NetrLogonSamLogon DCERPC调用。 Does such a thing exist in Java? Java中是否存在这样的事情? Yes. 是。 Here it is: 这里是:

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

Jespa is a 100% Java NTLM implementation that supports NTLMv2, NTLMv1, full integrity and confidentiality options and the aforementioned NETLOGON credential validation. Jespa是一个100%Java NTLM实现,支持NTLMv2,NTLMv1,完整的完整性和机密性选项以及前面提到的NETLOGON凭证验证。 And it includes an HTTP SSO Filter, a JAAS LoginModule, HTTP client, SASL client and server (with JNDI binding), generic "security provider" for creating custom NTLM services and more. 它包括HTTP SSO过滤器,JAAS LoginModule,HTTP客户端,SASL客户端和服务器(带有JNDI绑定),用于创建自定义NTLM服务的通用“安全提供程序”等。

Mike 麦克风

Here's the code I put together based on example from this blog: LINK and this source: LINK . 以下是我根据此博客中的示例汇总的代码: 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();
    }

}

I just finished a project that uses AD and Java. 我刚刚完成了一个使用AD和Java的项目。 We used Spring ldapTemplate. 我们使用了Spring ldapTemplate。

AD is LDAP compliant (almost), I don't think you will have any issues with the task you have. AD符合LDAP标准(差不多),我认为您的任务没有任何问题。 I mean the fact that it is AD or any other LDAP server it doesn't matter if you want just to connect. 我的意思是它是AD或任何其他LDAP服务器,如果你只想连接它并不重要。

I would take a look at: Spring LDAP 我想看看: Spring LDAP

They have examples too. 他们也有例子。

As for encryption, we used SSL connection (so it was LDAPS). 至于加密,我们使用SSL连接(因此它是LDAPS)。 AD had to be configured on a SSL port/protocol. 必须在SSL端口/协议上配置AD。

But first of all, make sure you can properly connect to your AD via an LDAP IDE. 但首先,请确保您可以通过LDAP IDE正确连接到AD。 I use Apache Directory Studio , it is really cool, and it is written in Java. 我使用Apache Directory Studio ,它非常酷,它是用Java编写的。 That is all I needed. 这就是我所需要的一切。 For testing purposes you could also install Apache Directory Server 出于测试目的,您还可以安装Apache Directory Server

As ioplex and others have said, there are many options. 正如ioplex和其他人所说,有很多选择。 To authenticate using LDAP (and the Novell LDAP API), I have used something like: 要使用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());

As a "special feature", Active Directory allows LDAP binds against "user@domain" without using the distinguished name of the account. 作为“特殊功能”,Active Directory允许LDAP绑定“user @ domain”而不使用帐户的可分辨名称。 This code uses StartTLS to enable TLS encryption on the connection; 此代码使用StartTLS在连接上启用TLS加密; the other alternative is LDAP over SSL, which is not supported by my AD servers. 另一种选择是LDAP over SSL, 我的 AD服务器不支持。

The real trick is in locating the server and host; 真正的诀窍是找到服务器和主机; the official way is to use a DNS SRV (service) record lookup to locate a bundle of candidate hosts, then do a UDP-based LDAP "ping" (in a particular Microsoft format) to locate the correct server. 官方的方法是使用DNS SRV(服务)记录查找来定位一组候选主机,然后执行基于UDP的LDAP“ping”(以特定的Microsoft格式)来定位正确的服务器。 If you are interested, I've posted some blog articles about my journey of adventure and discovery in that area. 如果您有兴趣,我已经发布了一些关于我在该领域的冒险和发现之旅的博客文章

If you want to do Kerberos-based username/password authentication, you are looking at another kettle of fish; 如果你想做基于Kerberos的用户名/密码验证,你正在看另一个鱼的水壶; it is doable with the Java GSS-API code, although I am not sure it performs the final step to validate the authentication. 它可以使用Java GSS-API代码,但我不确定它是否执行验证身份验证的最后一步。 (The code doing the validation can contact the AD server to check the username and password, which results in a ticket granting ticket for the user, but to ensure the AD server is not being impersonated, it also needs to try to get a ticket for the user to itself, which is somewhat more complicated.) (执行验证的代码可以联系AD服务器以检查用户名和密码,这会导致为用户授予票证,但为了确保AD服务器没有被模拟,它还需要尝试获取票证用户自己,这有点复杂。)

If you want to do Kerberos-based single sign-on, assuming your users are authenticated to the domain, you can do that as well with the Java GSS-API code. 如果要进行基于Kerberos的单点登录,假设您的用户已对域进行身份验证,那么您也可以使用Java GSS-API代码执行此操作。 I would post a code sample, but I still need to turn my hideous prototype into something fit for human eyes. 我会发布一个代码示例,但我仍然需要将我可怕的原型变成适合人眼的东西。 Check out some code from SpringSource for some inspiration. 查看SpringSource的一些代码以获得一些灵感。

If you are looking for NTLM (which I was given to understand is less secure) or something else, well, good luck. 如果你正在寻找NTLM(我被理解为不太安全)或其他东西,那么,祝你好运。

Are you just verifying credentials? 你只是验证凭证吗? In that case you could just do plain kerberos and not bother with LDAP . 在这种情况下,你可以做普通的kerberos而不是麻烦LDAP

If all you want to do is authenticate against AD using Kerberos, then a simple http://spnego.sourceforge.net/HelloKDC.java program should do it. 如果你想要做的就是使用Kerberos对AD进行身份验证,那么一个简单的http://spnego.sourceforge.net/HelloKDC.java程序应该这样做。

Take a look at the project's "pre-flight" documentation which talks about the HelloKDC.java program. 看看该项目的“飞行前”文档,该文档讨论了HelloKDC.java程序。

ldap authentication without SSL is not safe and anyone can view user credential because ldap client transfer usernamae and password during ldap bind operation So Always use ldaps protocol. 没有SSL的ldap身份验证是不安全的,任何人都可以查看用户凭据,因为ldap客户端在ldap绑定操作期间传输usernamae和密码所以始终使用ldaps协议。 source: Ldap authentication Active directory in Java Spring Security with Example source: Ldap authentication Java Spring Security with Example中的活动目录

I recommend you to look at the adbroker package of the oVirt project. 我建议你看一下oVirt项目的adbroker包。 It uses Spring-Ldap and the Krb5 JAAS Login module (with GSSAPI) in order to authenticate using Kerberos against Ldap servers (Active-Directory, ipa, rhds, Tivoli-DS). 它使用Spring-Ldap和Krb5 JAAS登录模块(使用GSSAPI),以便使用Kerberos对Ldap服务器(Active-Directory,ipa,rhds,Tivoli-DS)进行身份验证。 Look for the code at engine\\backend\\manager\\modules\\bll\\src\\main\\java\\org\\ovirt\\engine\\core\\bll\\adbroker 在engine \\ backend \\ manager \\ modules \\ bll \\ src \\ main \\ java \\ org \\ ovirt \\ engine \\ core \\ bll \\ adbroker中查找代码

You can use git to clone the repository or browse using the gerrit link 您可以使用git克隆存储库或使用gerrit链接进行浏览

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 从 Java(Web 应用程序)对 Active Directory 2016 进行身份验证 - Authenticating against Active Directory 2016 from Java(web applications) 在Linux上针对Java中的远程活动目录进行身份验证 - Authentication against remote active directory in java on Linux 使用LDAP WITHOUT servername从Java(Linux)到Active Directory进行身份验证 - Authenticating from Java (Linux) to Active Directory using LDAP WITHOUT servername 通过LdapExtLoginModule针对Active Directory林(LDAP_REFERRAL)进行身份验证 - Authenticating via LdapExtLoginModule against Active Directory Forest (LDAP_REFERRAL) 针对Active Directory的Java身份验证,身份验证不匹配? - Java Authentication against Active Directory, authentication mismatch? Java SSO:针对 Active Directory 的 Kerberos 身份验证 - Java SSO: Kerberos authentication against Active Directory 使用活动目录自动验证 java 或 .net 应用程序中的用户 - Automatically authenticating a user in java or .net app using active directory 通过Kerberos使用Active Directory进行身份验证 - Authenticating with Active Directory via Kerberos Java-使用Linux访问远程活动目录 - Java - Access to remote active directory with linux 如何通过Java从Active Directory验证用户身份? - How to authenticate user against Active Directory from java?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM