在 Linux 上使用 Java 对 Active Directory 进行身份验证
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/390150/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Authenticating against Active Directory with Java on Linux
提问by DV.
I have a simple task of authenticating against Active Directory using Java. 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.
我有一个使用 Java 对 Active Directory 进行身份验证的简单任务。只是验证凭据而已。假设我的域是“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. Most examples that I've found addressed LDAP in general, not specifically Active Directory. Issuing LDAP request means sending an OU path in it, which I don't have. 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. I would like a test bind with test credentials, if possible - this would mean that account is valid.
我知道有一些 Java 库可以简化这项任务,但我没有成功实现它们。我发现的大多数示例一般都针对 LDAP,而不是专门针对 Active Directory。发出 LDAP 请求意味着在其中发送一个 OU 路径,而我没有。此外,发出 LDAP 请求的应用程序应该已经绑定到 Active Directory 才能访问它......不安全,因为凭据必须存储在某个可发现的地方。如果可能的话,我想要一个带有测试凭据的测试绑定 - 这意味着该帐户是有效的。
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.
有没有人有工作代码的例子?谢谢。
采纳答案by DV.
Here's the code I put together based on example from this blog: LINKand 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();
}
}
回答by Anthony
Are you just verifying credentials? In that case you could just do plain kerberos
and not bother with LDAP
.
您只是在验证凭据吗?在这种情况下,您可以只做简单的事情,kerberos
而不必理会LDAP
.
回答by Mash See
http://java.sun.com/docs/books/tutorial/jndi/ldap/auth_mechs.html
http://java.sun.com/docs/books/tutorial/jndi/ldap/auth_mechs.html
SASL mechanism supports Kerberos v4 and v5. http://java.sun.com/docs/books/tutorial/jndi/ldap/sasl.html
SASL 机制支持 Kerberos v4 和 v5。 http://java.sun.com/docs/books/tutorial/jndi/ldap/sasl.html
回答by Alexandru Luchian
I just finished a project that uses AD and Java. We used Spring ldapTemplate.
我刚刚完成了一个使用 AD 和 Java 的项目。我们使用了 Spring ldapTemplate。
AD is LDAP compliant (almost), I don't think you will have any issues with the task you have. 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 兼容(几乎),我认为您的任务不会有任何问题。我的意思是,它是 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). AD had to be configured on a SSL port/protocol.
至于加密,我们使用了 SSL 连接(所以它是 LDAPS)。AD 必须在 SSL 端口/协议上配置。
But first of all, make sure you can properly connect to your AD via an LDAP IDE. I use Apache Directory Studio, it is really cool, and it is written in Java. That is all I needed. For testing purposes you could also install Apache Directory Server
但首先,请确保您可以通过 LDAP IDE 正确连接到您的 AD。我使用Apache Directory Studio,它真的很酷,而且它是用 Java 编写的。这就是我所需要的。出于测试目的,您还可以安装Apache Directory Server
回答by user8134
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 服务):
Kerberos - Kerberos provides Single Sign-On (SSO) and delegation but web servers also need SPNEGO support to accept SSO through IE.
NTLM - NTLM supports SSO through IE (and other browsers if they are properly configured).
LDAP - An LDAP bind can be used to simply validate an account name and password.
Kerberos - Kerberos 提供单点登录 (SSO) 和委派,但 Web 服务器还需要 SPNEGO 支持才能通过 IE 接受 SSO。
NTLM - NTLM 通过 IE 支持 SSO(以及其他浏览器,如果它们配置正确)。
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". So what does Windows do?
每个协议都有它的优点,但根据经验,为了获得最大的兼容性,您通常应该尝试“像 Windows 那样做”。那么 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. So Kerberos and NTLM are not mutually exclusive and NTLM is not obsoleted by Kerberos. In fact in some ways NTLM is better than Kerberos. Note that when mentioning Kerberos and NTLM in the same breath I have to also mention SPENGO and Integrated Windows Authentication (IWA). IWA is a simple term that basically means Kerberos or NTLM or SPNEGO to negotiate Kerberos or NTLM.
但是,如果身份验证方都没有域帐户,或者如果客户端无法与 DC 通信,则需要 NTLM。因此 Kerberos 和 NTLM 并不相互排斥,并且 NTLM 不会被 Kerberos 淘汰。事实上,在某些方面 NTLM 比 Kerberos 更好。请注意,在同时提及 Kerberos 和 NTLM 时,我还必须提及 SPENGO 和集成 Windows 身份验证 (IWA)。IWA 是一个简单的术语,基本上是指 Kerberos 或 NTLM 或 SPNEGO 来协商 Kerberos 或 NTLM。
Using an LDAP bind as a way to validate credentials is not efficient and requires SSL. But until recently implementing Kerberos and NTLM have been difficult so using LDAP as a make-shift authentication service has persisted. But at this point it should generally be avoided. LDAP is a directory of information and not an authentication service. Use it for it's intended purpose.
使用 LDAP 绑定作为验证凭据的方法效率不高,并且需要 SSL。但是直到最近,实施 Kerberos 和 NTLM 一直很困难,因此一直使用 LDAP 作为临时身份验证服务。但在这一点上,一般应该避免。LDAP 是一个信息目录,而不是身份验证服务。将其用于预期目的。
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. 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.
Quest Software 和 Centrify 等许多大公司都有专门提到 Java 的解决方案。我不能真正评论这些,因为它们是全公司范围的“身份管理解决方案”,因此,从他们网站上的营销旋转来看,很难确切地说出正在使用哪些协议以及如何使用。您需要与他们联系以获取详细信息。
Implementing Kerberos in Java is not terribly hard as the standard Java libraries support Kerberos through the org.ietf.gssapi classes. However, until recently there's been a major hurdle - IE doesn't send raw Kerberos tokens, it sends SPNEGO tokens. But with Java 6, SPNEGO has been implemented. In theory you should be able to write some GSSAPI code that can authenticate IE clients. 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.
在 Java 中实现 Kerberos 并不难,因为标准 Java 库通过 org.ietf.gssapi 类支持 Kerberos。然而,直到最近还存在一个主要障碍——IE 不发送原始 Kerberos 令牌,它发送 SPNEGO 令牌。但是在 Java 6 中,已经实现了 SPNEGO。从理论上讲,您应该能够编写一些可以对 IE 客户端进行身份验证的 GSSAPI 代码。但我没试过。多年来,Sun 对 Kerberos 的实现一直是一出错误喜剧,因此根据 Sun 在这方面的记录,我不会对他们的 SPENGO 实现做出任何承诺,直到您掌握了那只鸟。
For NTLM, there is a Free OSS project called JCIFS that has an NTLM HTTP authentication Servlet Filter. 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). For that reason and others, the HTTP Filter part of JCIFS is scheduled to be removed. Note that there are number of spin-offs that use JCIFS to implement the same technique. So if you see other projects that claim to support NTLM SSO, check the fine print.
对于 NTLM,有一个名为 JCIFS 的 Free OSS 项目,它具有 NTLM HTTP 身份验证 Servlet 过滤器。但是,它使用中间人方法来验证与 NTLMv2 不兼容的 SMB 服务器的凭据(这正逐渐成为必需的域安全策略)。由于这个原因和其他原因,JCIFS 的 HTTP 过滤器部分计划被删除。请注意,有许多衍生产品使用 JCIFS 来实现相同的技术。因此,如果您看到其他声称支持 NTLM SSO 的项目,请查看细则。
The only correct way to validate NTLM credentials with Active Directory is using the NetrLogonSamLogon DCERPC call over NETLOGON with Secure Channel. Does such a thing exist in Java? Yes. Here it is:
使用 Active Directory 验证 NTLM 凭据的唯一正确方法是使用 NetrLogonSamLogon DCERPC 调用通过 NETLOGON 和安全通道。Java中存在这样的东西吗?是的。这里是:
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. 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.
Jespa 是一个 100% Java NTLM 实现,支持 NTLMv2、NTLMv1、完全完整性和机密性选项以及前面提到的 NETLOGON 凭据验证。它包括一个 HTTP SSO 过滤器、一个 JAAS LoginModule、HTTP 客户端、SASL 客户端和服务器(带有 JNDI 绑定)、用于创建自定义 NTLM 服务的通用“安全提供者”等等。
Mike
麦克风
回答by Pat Gonzalez
If all you want to do is authenticate against AD using Kerberos, then a simple http://spnego.sourceforge.net/HelloKDC.javaprogram 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 程序。
回答by Tommy McGuire
As ioplex and others have said, there are many options. To authenticate using LDAP (and the Novell LDAP API), I have used something like:
正如 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());
As a "special feature", Active Directory allows LDAP binds against "user@domain" without using the distinguished name of the account. This code uses StartTLS to enable TLS encryption on the connection; the other alternative is LDAP over SSL, which is not supported by myAD servers.
作为一项“特殊功能”,Active Directory 允许 LDAP 绑定到“user@domain”而不使用帐户的专有名称。此代码使用 StartTLS 在连接上启用 TLS 加密;另一种选择是 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. If you are interested, I've posted some blog articlesabout my journey of adventure and discovery in that area.
真正的诀窍在于定位服务器和主机;官方方法是使用 DNS SRV(服务)记录查找来定位一组候选主机,然后执行基于 UDP 的 LDAP“ping”(以特定的 Microsoft 格式)以定位正确的服务器。如果您有兴趣,我已经发布了一些关于我在该领域的冒险和发现之旅的博客文章。
If you want to do Kerberos-based username/password authentication, you are looking at another kettle of fish; it is doable with the Java GSS-API code, although I am not sure it performs the final step to validate the authentication. (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.)
如果你想做基于 Kerberos 的用户名/密码认证,你正在寻找另一个鱼缸;它可以使用 Java GSS-API 代码执行,但我不确定它是否执行验证身份验证的最后一步。(进行验证的代码可以联系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. 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 SpringSourcefor some inspiration.
如果您想要进行基于 Kerberos 的单点登录,假设您的用户已通过域的身份验证,您也可以使用 Java GSS-API 代码来实现。我会发布一个代码示例,但我仍然需要将我丑陋的原型变成适合人眼的东西。查看SpringSource 的一些代码以获得一些灵感。
If you are looking for NTLM (which I was given to understand is less secure) or something else, well, good luck.
如果您正在寻找 NTLM(我了解到它不太安全)或其他东西,那么祝您好运。
回答by Seema Kiran
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. source: Ldap authentication Active directory in Java Spring Security with Example
没有 SSL 的 ldap 身份验证是不安全的,任何人都可以查看用户凭据,因为 ldap 客户端在 ldap 绑定操作期间传输用户名和密码,因此始终使用 ldaps 协议。来源: Java Spring Security 中的 LDAP 身份验证 Active directory with Example
回答by Yair Zaslavsky
I recommend you to look at the adbroker package of the oVirtproject. 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). Look for the code at engine\backend\manager\modules\bll\src\main\java\org\ovirt\engine\core\bll\adbroker
建议你看一下oVirt项目的adbroker包。它使用 Spring-Ldap 和 Krb5 JAAS 登录模块(带有 GSSAPI),以便使用 Kerberos 对 Ldap 服务器(Active-Directory、ipa、rhds、Tivoli-DS)进行身份验证。在 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 gerritlink
您可以使用 git 克隆存储库或使用gerrit链接浏览