4

테스트 서버의 LDAP를 통해 Active Directory에 대해 성공적으로 인증 할 수있는 개념 증명 작업 응용 프로그램을 보유하고 있지만, 프로덕션 응용 프로그램은 TLS를 통해이를 수행해야합니다. 도메인 컨트롤러는 다음을 통해 시작하지 않는 연결을 닫습니다. TLS.TLS를 통해 LDAP를 통해 Active Directory를 인증하는 방법은 무엇입니까?

Eclipse에 LDAP 브라우저를 설치했으며 의 TLS를 사용하여 직접 바인딩 할 수 있지만 내 인생에서 TLS를 사용하는 방법을 파악할 수는 없습니다.

ldap.xml :

<bean id="ldapAuthenticationProvider" 
     class="my.project.package.OverrideActiveDirectoryLdapAuthenticationProvider"> 

    <!-- this works to authenticate by binding as the user in question --> 
    <constructor-arg value="test.server"/> 
    <constructor-arg value="ldap://192.168.0.2:389"/> 

    <!-- this doesn't work, because the server requires a TLS connection --> 
    <!-- <constructor-arg value="production.server"/> --> 
    <!-- <constructor-arg value="ldaps://192.168.0.3:389"/> --> 

    <property name="convertSubErrorCodesToExceptions" value="true"/> 
</bean> 

OverrideActiveDirectoryLdapAuthenticationProviderfinal 지정된 몇 가지 이유입니다 봄의 ActiveDirectoryLdapAuthenticationProvider 클래스의 사본을 확장 재정의 클래스입니다. 우선적 인 이유는 사용 권한/권한이 사용자 개체에 채워지는 방식을 사용자 지정하는 것과 관련이 있습니다. 우리는 관련 그룹의 그룹 구성원을 사용하여 사용자의 사용 권한을 작성하거나 AD 사용자 개체의 필드에서 읽습니다. 이 방법에서는 loadUserAuthorities() 메서드를 재정의 할 뿐이지 만 bindAsUser() 메서드 또는 doAuthentication() 메서드를 재정의해야 할 수도 있습니다.

XML 및 하나의 대체 클래스는 Spring이 작업을 수행하는 것과 반대되는 인증이 어플리케이션에서 관리되는 유일한 두 곳입니다. TLS를 활성화하기 위해 여러 곳을 읽었습니다. DefaultTlsDirContextAuthenticationStrategy 클래스를 확장해야하지만 어디에 연결해야합니까? 네임 스페이스 솔루션이 있습니까? 완전히 다른 작업 (예 : ActiveDirectoryLdapAuthenticationProvider 사용을 포기하고 대신 LdapAuthenticationProvider 사용)을 수행해야합니까?

도움을 주시면 감사하겠습니다.

답변

6

좋아요, 그러니 하루 반 정도 작업 한 후에 알아 냈습니다.

내 원래 접근 방식은 ActiveDirectoryLdapAuthenticationProvider 클래스를 확장하고 그 loadUserAuthorities() 메서드를 재정 의하여 인증 된 사용자의 사용 권한을 사용자 정의하는 것이 었습니다. 당연한 이유로, ActiveDirectoryLdapAuthenticationProvider 클래스는 final으로 지정되어 있으므로 확장 할 수 없습니다.

는 다행히, 오픈 소스 해킹을 제공한다 (그 클래스의 슈퍼 클래스는 하지final 있습니다) 그래서 간단하게 따라 패키지와 클래스 참조를, 그것의 전체 내용을 복사 한 final 지정을 제거하고 조정. 나는이 클래스의 코드를 편집하지 않았다. 편집 할 필요가 없다는 눈에 잘 띄는 주석을 추가했다. 그 다음이 클래스를 OverrideActiveDirectoryLdapAuthenticationProvider으로 확장했습니다.이 파일은 ldap.xml 파일에서도 참조되었으며 loadUserAuthorities에 대한 재정의 메서드가 추가되었습니다. 이러한 모든 기능은 격리 된 가상 서버의 암호화되지 않은 세션에서 단순한 LDAP 바인딩을 사용하여 효과적이었습니다.

실제 네트워크 환경에서는 모든 LDAP 쿼리가 TLS 핸드 셰이크로 시작해야하지만 쿼리되는 서버는 PDC가 아니며 이름은 'sub.domain.tld'이지만 사용자는 적절하게 인증됩니다 'domain.tld' 또한 바인딩하려면 사용자 이름 앞에 'NT_DOMAIN \'이 와야합니다. 이 모든 작업은 커스터마이징 작업을 필요로했으며 불행히도 어디서나 거의 도움이되지 않았습니다.

그래서 여기가 OverrideActiveDirectoryLdapAuthenticationProvider에 더 재 지정을 포함 모두의 preposterously 간단한 변경입니다 :

입니다
@Override 
protected DirContext bindAsUser(String username, String password) { 
    final String bindUrl = url; //super reference 
    Hashtable<String,String> env = new Hashtable<String,String>(); 
    env.put(Context.SECURITY_AUTHENTICATION, "simple"); 
    //String bindPrincipal = createBindPrincipal(username); 
    String bindPrincipal = "NT_DOMAIN\\" + username; //the bindPrincipal() method builds the principal name incorrectly 
    env.put(Context.SECURITY_PRINCIPAL, bindPrincipal); 
    env.put(Context.PROVIDER_URL, bindUrl); 
    env.put(Context.SECURITY_CREDENTIALS, password); 
    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxtFactory"); 
    //and finally, this simple addition 
    env.put(Context.SECURITY_PROTOCOL, "tls"); 

    //. . . try/catch portion left alone 
} 

, 나는이 방법에게 한 모두가 bindPrincipal 문자열이 포맷 된 방식을 변경했다, 나는 추가 해시 테이블의 키/값

ldap.xml에 전달 되었기 때문에 domain 매개 변수에서 내 클래스로 전달 된 하위 도메인을 제거 할 필요가 없었습니다.

@Override 
protected DirContextOperations searchForUser(DirContext ctx, String username) throws NamingException { 
    SearchControls searchCtls = new SearchControls(); 
    searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); 

    //this doesn't work, and I'm not sure exactly what the value of the parameter {0} is 
    //String searchFilter = "(&(objectClass=user)(userPrincipalName={0}))"; 
    String searchFilter = "(&(objectClass=user)(userPrincipalName=" + username + "@domain.tld))"; 

    final String bindPrincipal = createBindPrincipal(username); 
    String searchRoot = rootDn != null ? rootDn : searchRootFromPrincipal(bindPrincipal); 

    return SpringSecurityLdapTemplate.searchForSingleEntryInternal(ctx, searchCtls, searchRoot, searchFilter, new Object[]{bindPrincipal}); 

마지막 변화가 제대로 (내 목적을 위해) 문자열을 구축하려면 createBindPrincipal() 방법이었다 : 나는 단순히 내가 OverrideActiveDirectoryLdapAuthenticationProvidersearchForUser() 방법을 변경 <constructor-arg value="domain.tld"/> 다음

거기 매개 변수 을 변경 : 여전히 내 테스트 및 headdesking 모두에서 정리 필요 - -

@Override String createBindPrincipal(String username) { if (domain == null || username.toLowerCase().endsWith(domain)) { return username; } return "NT_DOMAIN\\" + username; } 

그리고 위의 변경과

가 나는 결합 할 수 있었다 네트워크에서 Active Directory에 대해 본인임을 인증하고, 원하는 모든 사용자 개체 필드를 캡처하고, 그룹 구성원을 식별합니다.

오, 분명히 TLS에는 'ldaps : //'가 필요하지 않으므로 내 ldap.xmlldap://192.168.0.3:389입니다.


TL; DR :

은 TLS를 활성화하려면,의 final 지정을 제거 할 사용자 정의 클래스에 확장, 환경의 해시 테이블에 env.put(Context.SECURITY_PROTOCOL, "tls");을 추가하여 bindAsUser()를 오버라이드 (override), 봄의 ActiveDirectoryLdapAuthenticationProvider 클래스를 복사합니다. 그게 전부 야.

바인드 사용자 이름, 도메인 및 LDAP 쿼리 문자열을보다 자세히 제어하려면 적용 가능한 방법을 적절하게 재정의하십시오. 내 경우에는 {0}의 값을 식별 할 수 없으므로 완전히 제거하고 전달 된 username 문자열을 대신 삽입했습니다.

바라기를, 누군가가 도움이 될 것으로 기대합니다.

관련 문제