2012-02-22 84 views
3

我們正在編寫一個應連接到不同的LDAP服務器的應用程序。對於每臺服務器,我們只能接受某個證書。該證書中的主機名稱無關緊要。這很簡單,因爲我們使用LDAP和STARTTLS,因爲我們可以使用StartTlsResponse.setHostnameVerifier(..-)並使用StartTlsResponse.negotiate(...)和匹配的SSLSocketFactory。但是我們也需要支持LDAPS連接。 Java本身支持這一點,但前提是服務器證書由默認的Java密鑰庫信任。雖然我們可以替換它,但我們仍然無法爲不同的服務器使用不同的密鑰庫。jndi LDAPS自定義HostnameVerifier和TrustManager

現有的連接代碼如下:

Hashtable<String,String> env = new Hashtable<String,String>(); 
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); 
env.put(Context.PROVIDER_URL, (encryption == SSL ? "ldaps://" : "ldap://") + host + ":" + port); 
if (encryption == SSL) { 
    // env.put("java.naming.ldap.factory.socket", "CustomSocketFactory"); 
} 
ctx = new InitialLdapContext(env, null); 
if (encryption != START_TLS) 
    tls = null; 
else { 
    tls = (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest()); 
    tls.setHostnameVerifier(hostnameVerifier); 
    tls.negotiate(sslContext.getSocketFactory()); 
} 

我們可以添加自己出來CustomSocketFactory,但如何對信息傳遞給?

回答

3

對於其他人有同樣的問題:我發現我的情況非常醜陋的解決方案:

import javax.net.SocketFactory; 

public abstract class ThreadLocalSocketFactory 
    extends SocketFactory 
{ 

    static ThreadLocal<SocketFactory> local = new ThreadLocal<SocketFactory>(); 

    public static SocketFactory getDefault() 
    { 
    SocketFactory result = local.get(); 
    if (result == null) 
     throw new IllegalStateException(); 
    return result; 
    } 

    public static void set(SocketFactory factory) 
    { 
    local.set(factory); 
    } 

    public static void remove() 
    { 
    local.remove(); 
    } 

} 

使用這樣的:

env.put("java.naming.ldap.factory.socket", ThreadLocalSocketFactory.class.getName()); 
ThreadLocalSocketFactory.set(sslContext.getSocketFactory()); 
try { 
    ctx = new InitialLdapContext(env, null); 
} finally { 
    ThreadLocalSocketFactory.remove(); 
} 

不是很好,但它的作品。 JNDI應該在這裏更加靈活...

+0

所以基本上,你已經產生了你自己的答案,在我的答案的延續中,你說你已經低估了... – Bruno 2012-03-27 09:57:00

+0

I從來沒有說過你寫的東西是錯的,只是我的問題中已經提到了這些信息,並沒有以任何方式幫助解決這個問題。這就是爲什麼我低估了。如果這不是一個有效的降低投票的提議,我會刪除那個downvote,但是然後EJPs upvote(這只是爲了清除我的downvote)留下來,投了一個無用的答案,所以我不會。 – 2012-03-28 10:29:17

+0

正如我所說的,在編輯之前就已經足夠了,但是一旦你得到了一個答案,它顯示了OpenJDK代碼中的原因,除非你使用靜態成員(你已經完成)或者像JDNI之類的東西,否則你爲什麼不能做你想做的事情,我認爲這是對你的問題的回答。然後,你已經接受了你自己的答案,這非常明顯地符合我的建議。我不關心downvote,我不介意你已經接受了你自己的答案(因爲你對ThreadLocal更具體)。我只是發現downvoting +接受自己的回答來自downvoted的答案。 – Bruno 2012-03-28 11:17:16

1

你應該通過自己的SSLSocketFactory子類的名稱,並通過其全限定命名爲"java.naming.ldap.factory.socket" ENV財產,如"Using Custom Sockets" section of the Java LDAP/SSL guide描述:

env.put("java.naming.ldap.factory.socket", "example.CustomSocketFactory"); 

你不能傳遞任何具體的參數傳遞給這個類,見實例在com.sun.jndi.ldap.Connection.createSocket(...)

Class socketFactoryClass = Obj.helper.loadClass(socketFactory); 
Method getDefault = 
    socketFactoryClass.getMethod("getDefault", new Class[]{}); 
Object factory = getDefault.invoke(null, new Object[]{}); 

如果你想要更多的參數,你可能需要使用靜態成員或JNDI也許(通常不理想)。

據我所知,不幸的是,在此實現中使用ldaps://似乎沒有任何主機名驗證。如果您只信任信任管理器中的一個顯式證書,則無論如何這應該補償缺乏主機名驗證。

+0

我在我的上面列表中有這行代碼。但是,因爲我只能指定一個類名而不是某個實例,所以我無法將參數傳遞給該類 - 這正是我所需要的。你讀過這個問題了嗎?如果是這樣,告訴我我應該更詳細的位置。 – 2012-02-22 15:57:32

+0

@SteffenHeil,對不起,我回答得太快,我已經添加了更多細節。 – Bruno 2012-02-22 16:33:50

+0

Upvoted unfoldined downvote – EJP 2012-02-23 23:27:03