![](/img/trans.png)
[英]Authenticating against Active Directory 2016 from Java(web applications)
[英]Authenticating against Active Directory with Java on Linux
我有一個簡單的任務,使用Java對Active Directory進行身份驗證。 只是驗證憑據而不是其他任何內容。 假設我的域名為“fun.xyz.tld”,OU路徑未知,用戶名/密碼為testu / testp。
我知道有一些Java庫可以簡化這項任務,但我沒有成功實現它們。 我發現的大多數示例都是針對LDAP的,而不是特定的Active Directory。 發出LDAP請求意味着在其中發送OU路徑,這是我沒有的。 此外,發出LDAP請求的應用程序應該已綁定到Active Directory以便訪問它...不安全,因為憑據必須存儲在某處可被發現的位置。 如果可能的話,我希望測試綁定測試憑據 - 這意味着帳戶有效。
最后,如果可能的話,有沒有辦法使這種認證機制加密? 我知道AD使用Kerberos,但不確定Java的LDAP方法是否可以。
有沒有人有一個工作代碼的例子? 謝謝。
有3種身份驗證協議可用於在Linux或任何其他平台上執行Java和Active Directory之間的身份驗證(這些協議不僅僅針對HTTP服務):
Kerberos - Kerberos提供單點登錄(SSO)和委派,但Web服務器也需要SPNEGO支持才能通過IE接受SSO。
NTLM - NTLM通過IE(以及其他瀏覽器,如果它們已正確配置)支持SSO。
LDAP - LDAP綁定可用於簡單地驗證帳戶名和密碼。
還有一種稱為“ADFS”的東西,它為使用調用Windows SSP的SAML的網站提供SSO,因此在實踐中它基本上是使用上述其他協議之一的迂回方式。
每個協議都有它的優點,但根據經驗,為了獲得最大的兼容性,你通常應該嘗試“像Windows一樣”。 那么Windows做什么?
首先,兩台Windows機器之間的身份驗證支持Kerberos,因為服務器不需要與DC通信,客戶端可以緩存Kerberos票證,從而減少DC上的負載(並且因為Kerberos支持委派)。
但是,如果身份驗證方都沒有域帳戶,或者客戶端無法與DC通信,則需要NTLM。 因此,Kerberos和NTLM不是互斥的,並且Kerberos不會廢棄NTLM。 事實上,在某些方面,NTLM比Kerberos更好。 請注意,當同時提到Kerberos和NTLM時,我還要提到SPENGO和集成Windows身份驗證(IWA)。 IWA是一個簡單的術語,基本上意味着Kerberos或NTLM或SPNEGO協商Kerberos或NTLM。
使用LDAP綁定作為驗證憑據的方法效率不高,需要SSL。 但直到最近,實施Kerberos和NTLM一直很困難,因此使用LDAP作為生成班次認證服務仍然存在。 但在這一點上通常應該避免。 LDAP是信息目錄,而不是身份驗證服務。 將它用於預期目的。
那么如何在Java中以及在Web應用程序的上下文中實現Kerberos或NTLM呢?
像Quest Software和Centrify這樣的大公司都有專門提到Java的解決方案。 我無法對這些進行評論,因為它們是公司范圍內的“身份管理解決方案”,所以,通過查看其網站上的營銷活動,很難確切地說明正在使用哪些協議以及如何使用。 您需要與他們聯系以獲取詳細信息。
在Java中實現Kerberos並不是非常困難,因為標准Java庫通過org.ietf.gssapi類支持Kerberos。 然而,直到最近還存在一個主要障礙 - IE不發送原始Kerberos令牌,它發送SPNEGO令牌。 但是使用Java 6,SPNEGO已經實現。 從理論上講,您應該能夠編寫一些可以驗證IE客戶端的GSSAPI代碼。 但我沒試過。 多年來,Sun實施的Kerberos一直是一個錯誤的喜劇,所以根據Sun在這個領域的記錄,我不會對他們的SPENGO實施做出任何承諾,直到你掌握了這只鳥。
對於NTLM,有一個名為JCIFS的免費OSS項目,它具有NTLM HTTP身份驗證Servlet過濾器。 但是,它使用中間人方法來驗證使用不能與NTLMv2一起使用的SMB服務器的憑證(這正逐漸成為必需的域安全策略)。 出於這個原因和其他原因,計划刪除JCIFS的HTTP過濾器部分。 請注意,有許多衍生產品使用JCIFS來實現相同的技術。 因此,如果您看到聲稱支持NTLM SSO的其他項目,請檢查細則。
使用Active Directory驗證NTLM憑據的唯一正確方法是使用帶有安全通道的NETLOGON上的NetrLogonSamLogon DCERPC調用。 Java中是否存在這樣的事情? 是。 這里是:
http://www.ioplex.com/jespa.html
Jespa是一個100%Java NTLM實現,支持NTLMv2,NTLMv1,完整的完整性和機密性選項以及前面提到的NETLOGON憑證驗證。 它包括HTTP SSO過濾器,JAAS LoginModule,HTTP客戶端,SASL客戶端和服務器(帶有JNDI綁定),用於創建自定義NTLM服務的通用“安全提供程序”等。
麥克風
以下是我根據此博客中的示例匯總的代碼: 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();
}
}
我剛剛完成了一個使用AD和Java的項目。 我們使用了Spring ldapTemplate。
AD符合LDAP標准(差不多),我認為您的任務沒有任何問題。 我的意思是它是AD或任何其他LDAP服務器,如果你只想連接它並不重要。
我想看看: Spring LDAP
他們也有例子。
至於加密,我們使用SSL連接(因此它是LDAPS)。 必須在SSL端口/協議上配置AD。
但首先,請確保您可以通過LDAP IDE正確連接到AD。 我使用Apache Directory Studio ,它非常酷,它是用Java編寫的。 這就是我所需要的一切。 出於測試目的,您還可以安裝Apache Directory Server
正如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允許LDAP綁定“user @ domain”而不使用帳戶的可分辨名稱。 此代碼使用StartTLS在連接上啟用TLS加密; 另一種選擇是LDAP over SSL, 我的 AD服務器不支持。
真正的訣竅是找到服務器和主機; 官方的方法是使用DNS SRV(服務)記錄查找來定位一組候選主機,然后執行基於UDP的LDAP“ping”(以特定的Microsoft格式)來定位正確的服務器。 如果您有興趣,我已經發布了一些關於我在該領域的冒險和發現之旅的博客文章 。
如果你想做基於Kerberos的用戶名/密碼驗證,你正在看另一個魚的水壺; 它可以使用Java GSS-API代碼,但我不確定它是否執行驗證身份驗證的最后一步。 (執行驗證的代碼可以聯系AD服務器以檢查用戶名和密碼,這會導致為用戶授予票證,但為了確保AD服務器沒有被模擬,它還需要嘗試獲取票證用戶自己,這有點復雜。)
如果要進行基於Kerberos的單點登錄,假設您的用戶已對域進行身份驗證,那么您也可以使用Java GSS-API代碼執行此操作。 我會發布一個代碼示例,但我仍然需要將我可怕的原型變成適合人眼的東西。 查看SpringSource的一些代碼以獲得一些靈感。
如果你正在尋找NTLM(我被理解為不太安全)或其他東西,那么,祝你好運。
你只是驗證憑證嗎? 在這種情況下,你可以做普通的kerberos
而不是麻煩LDAP
。
如果你想要做的就是使用Kerberos對AD進行身份驗證,那么一個簡單的http://spnego.sourceforge.net/HelloKDC.java程序應該這樣做。
看看該項目的“飛行前”文檔,該文檔討論了HelloKDC.java程序。
沒有SSL的ldap身份驗證是不安全的,任何人都可以查看用戶憑據,因為ldap客戶端在ldap綁定操作期間傳輸usernamae和密碼所以始終使用ldaps協議。 source: Ldap authentication Java Spring Security with Example中的活動目錄
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.