Linux에서 Java를 사용하여 Active Directory에 대해 인증
Java를 사용하여 Active Directory에 대해 인증하는 간단한 작업이 있습니다. 자격 증명 만 확인하고 다른 것은 없습니다. 내 도메인이 "fun.xyz.tld"이고 OU 경로를 알 수없고 사용자 이름 / 암호가 testu / testp라고 가정 해 보겠습니다.
이 작업을 단순화하는 Java 라이브러리가 몇 개 있다는 것을 알고 있지만 구현에 성공하지 못했습니다. 내가 찾은 대부분의 예는 일반적으로 Active Directory가 아닌 LDAP를 다루었습니다. LDAP 요청을 발행한다는 것은 내가 가지고 있지 않은 OU 경로를 보내는 것을 의미합니다. 또한 LDAP 요청을 발행하는 응용 프로그램은 액세스하려면 이미 Active Directory에 바인딩되어 있어야합니다. 자격 증명이 검색 가능한 위치에 저장되어야하므로 안전하지 않습니다. 가능한 경우 테스트 자격 증명으로 테스트 바인딩을 원합니다. 이것은 계정이 유효 함을 의미합니다.
마지막으로 가능하다면 이러한 인증 메커니즘을 암호화하는 방법이 있습니까? AD가 Kerberos를 사용한다는 것을 알고 있지만 Java의 LDAP 방법이 사용하는지 확실하지 않습니다.
누구든지 작업 코드의 예가 있습니까? 감사.
Linux 또는 기타 플랫폼에서 Java와 Active Directory간에 인증을 수행하는 데 사용할 수있는 3 가지 인증 프로토콜이 있습니다 (HTTP 서비스에만 국한되지 않음).
Kerberos-Kerberos는 SSO (Single Sign-On) 및 위임을 제공하지만 웹 서버도 IE를 통해 SSO를 허용하려면 SPNEGO 지원이 필요합니다.
NTLM-NTLM은 IE (올바르게 구성된 경우 다른 브라우저)를 통해 SSO를 지원합니다.
LDAP-LDAP 바인딩을 사용하여 계정 이름과 암호를 간단히 확인할 수 있습니다.
또한 Windows SSP를 호출하는 SAML을 사용하여 웹 사이트에 SSO를 제공하는 "ADFS"라는 것이 있으므로 실제로는 기본적으로 위의 다른 프로토콜 중 하나를 사용하는 우회 방법입니다.
각 프로토콜에는 장점이 있지만 경험상 최대의 호환성을 위해 일반적으로 "Windows처럼 수행"해야합니다. 그렇다면 Windows는 무엇을합니까?
첫째, 두 Windows 시스템 간의 인증은 서버가 DC와 통신 할 필요가없고 클라이언트가 Kerberos 티켓을 캐시하여 DC에 대한 부하를 줄일 수 있기 때문에 Kerberos를 선호합니다 (Kerberos가 위임을 지원하기 때문).
그러나 인증 당사자 모두에 도메인 계정이 없거나 클라이언트가 DC와 통신 할 수없는 경우 NTLM이 필요합니다. 따라서 Kerberos와 NTLM은 상호 배타적이지 않으며 NTLM은 Kerberos에 의해 폐기되지 않습니다. 사실 어떤면에서 NTLM은 Kerberos보다 낫습니다. Kerberos와 NTLM을 동시에 언급 할 때 SPENGO와 IWA (Integrated Windows Authentication)도 언급해야합니다. IWA는 기본적으로 Kerberos 또는 NTLM 또는 SPNEGO를 의미하는 간단한 용어로 Kerberos 또는 NTLM을 협상합니다.
자격 증명을 확인하는 방법으로 LDAP 바인딩을 사용하는 것은 효율적이지 않으며 SSL이 필요합니다. 그러나 최근까지 Kerberos 및 NTLM을 구현하는 것이 어려웠으므로 LDAP를 임시 인증 서비스로 사용하는 것은 계속되었습니다. 그러나이 시점에서는 일반적으로 피해야합니다. LDAP는 인증 서비스가 아니라 정보의 디렉토리입니다. 의도 된 목적으로 사용하십시오.
그렇다면 Java 및 특히 웹 응용 프로그램의 컨텍스트에서 Kerberos 또는 NTLM을 어떻게 구현합니까?
Java를 구체적으로 언급하는 솔루션을 보유한 Quest Software 및 Centrify와 같은 여러 대기업이 있습니다. 회사 전체의 "아이덴티티 관리 솔루션"이기 때문에 실제로 언급 할 수 없습니다. 따라서 웹 사이트의 마케팅 스핀을 보면 어떤 프로토콜이 어떻게 사용되고 있는지 정확히 알 수 없습니다. 자세한 내용은 해당 기관에 문의해야합니다.
표준 Java 라이브러리가 org.ietf.gssapi 클래스를 통해 Kerberos를 지원하므로 Java에서 Kerberos를 구현하는 것은 그리 어렵지 않습니다. 그러나 최근까지도 IE는 원시 Kerberos 토큰을 보내지 않고 SPNEGO 토큰을 보냅니다. 그러나 Java 6에서는 SPNEGO가 구현되었습니다. 이론상 IE 클라이언트를 인증 할 수있는 GSSAPI 코드를 작성할 수 있어야합니다. 그러나 나는 그것을 시도하지 않았습니다. 썬의 Kerberos 구현은 수년 동안 오류의 코미디 였으므로이 분야에서 썬의 실적을 기반으로 할 때 그 새를 손에 들고있을 때까지 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 서비스 생성을위한 일반 "보안 공급자"등이 포함됩니다.
마이크
다음은이 블로그의 예를 기반으로 작성한 코드입니다. 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
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.
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
As ioplex and others have said, there are many options. To authenticate using LDAP (and the Novell LDAP API), I have used something like:
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 my AD servers.
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 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; 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.)
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 SpringSource for some inspiration.
If you are looking for NTLM (which I was given to understand is less secure) or something else, well, good luck.
Are you just verifying credentials? In that case you could just do plain kerberos
and not bother with 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.
Take a look at the project's "pre-flight" documentation which talks about the HelloKDC.java program.
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
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
I recommend you to look at the adbroker package of the oVirt project. 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
You can use git to clone the repository or browse using the gerrit link
'Nice programing' 카테고리의 다른 글
Django Rest Framework 파일 업로드 (0) | 2020.10.23 |
---|---|
Thread가 추상 클래스가 아니고 start ()가 final이 아닌 이유는 무엇입니까? (0) | 2020.10.23 |
.keystore 파일을 분실 했습니까? (0) | 2020.10.23 |
고유 값 계산 (0) | 2020.10.23 |
여러 줄 bash 코드를 터미널에 붙여넣고 한 번에 실행하려면 어떻게해야합니까? (0) | 2020.10.23 |