【问题标题】:How to pass arguments to an LDAP custom socket factory with JNDI?如何使用 JNDI 将参数传递给 LDAP 自定义套接字工厂?
【发布时间】:2011-09-19 10:42:40
【问题描述】:

我正在使用 JNDI 建立 LDAP 连接。示例:

Hashtable env = new Hashtable();
env.put(Context.PROVIDER_URL, LDAPS_URL);
env.put(Context.SECURITY_AUTHENTICATION, "EXTERNAL");
env.put("java.naming.ldap.factory.socket", "ldaptest.CustomSocketFactory");
...

我需要在运行时将参数传递给 CustomSocketFactory。特别是对客户端证书的引用。

我该怎么做? 我可以使用线程本地存储。

有没有更好的办法?

【问题讨论】:

  • 我遇到了同样的问题。你找到解决办法了吗?

标签: java ldap jndi


【解决方案1】:

据我所知,您所能做的就是传递一个类名(参见 icrovett 的回答),JNDI 将通过反射调用 getDefault() 方法。所以你不能在那里真正传递任何参数。所以我认为没有好的和干净的解决方案。

根据我的需要,我构建了一个SelectiveLdapSslSocketFactory,其中包含一个静态映射主机到不同 SSLSocketFactories 的映射。首先,您可以为不同的主机注册您的自定义SSLSocketFactories。然后,当调用任何createSocket(...) 方法时,它会知道为其创建套接字的主机,因此可以将调用委托给相应的SSLSocketFactory。它还包含一个defaultSslSocketFactory,用于没有任何映射的主机,也包含在getDefaultCipherSuites() 和getSupportedCipherSuites() 方法中。我不确定它是否完全正确,但对我来说效果很好,所以如果你愿意,可以测试一下:

public class SelectiveLdapSslSocketFactory extends SSLSocketFactory {

    private static SSLSocketFactory defaultSslSocketFactory;
    private static final Map<String, SSLSocketFactory> hostToSslSocketFactoryMap = new HashMap<>();
    
    {
        try {
            defaultSslSocketFactory = <yourOwnDefaultSslSocketFactory>;
        } catch (Exception ex) {
            Logger.warn(ex, "Couldn't initialize a defaultSslSocketFactory for LDAP connections!");
        }
    }

    public static SSLSocketFactory getRegisteredSslSocketFactory(String host) {
        return hostToSslSocketFactoryMap.get(host);
    }

    public static void registerSslSocketFactory(String host, SSLSocketFactory sslSocketFactory) {
        hostToSslSocketFactoryMap.put(host, sslSocketFactory);
    }

    public static void deregisterSslSocketFactory(String host) {
        hostToSslSocketFactoryMap.remove(host);
    }

    public SelectiveLdapSslSocketFactory() {
    }

    public static SocketFactory getDefault() {
        return new SelectiveLdapSslSocketFactory();
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return defaultSslSocketFactory.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return defaultSslSocketFactory.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        SSLSocketFactory sslSocketFactory = Objects.requireNonNullElse(hostToSslSocketFactoryMap.get(host), defaultSslSocketFactory);
        return sslSocketFactory.createSocket(s, host, port, autoClose);
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
        SSLSocketFactory sslSocketFactory = Objects.requireNonNullElse(hostToSslSocketFactoryMap.get(host), defaultSslSocketFactory);
        return sslSocketFactory.createSocket(host, port);
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
        SSLSocketFactory sslSocketFactory = Objects.requireNonNullElse(hostToSslSocketFactoryMap.get(host), defaultSslSocketFactory);
        return sslSocketFactory.createSocket(host, port, localHost, localPort);
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        SSLSocketFactory sslSocketFactory = getSslSocketFactory(host);
        return sslSocketFactory.createSocket(host, port);
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        SSLSocketFactory sslSocketFactory = getSslSocketFactory(address);
        return sslSocketFactory.createSocket(address, port, localAddress, localPort);
    }

    private SSLSocketFactory getSslSocketFactory(InetAddress inetAddress) {
        SSLSocketFactory sslSocketFactory = hostToSslSocketFactoryMap.get(Objects.requireNonNullElse(inetAddress.getCanonicalHostName(), ""));
        if (sslSocketFactory == null) {
            sslSocketFactory = hostToSslSocketFactoryMap.get(Objects.requireNonNullElse(inetAddress.getHostName(), ""));
            if (sslSocketFactory == null) {
                sslSocketFactory = hostToSslSocketFactoryMap.get(Objects.requireNonNullElse(inetAddress.getHostAddress(), ""));
                if (sslSocketFactory == null) {
                    sslSocketFactory = defaultSslSocketFactory;
                }
            }
        }
        return sslSocketFactory;
    }
}

然后你可以像这样使用它:

...
SelectiveLdapSslSocketFactory.registerSslSocketFactory(host01, sslSocketFactory01);
SelectiveLdapSslSocketFactory.registerSslSocketFactory(host02, sslSocketFactory02);
SelectiveLdapSslSocketFactory.registerSslSocketFactory(host03, sslSocketFactory03);

props.put("java.naming.ldap.factory.socket", SelectiveLdapSslSocketFactory.class.getName());

【讨论】:

    【解决方案2】:

    实际上,到目前为止,线程本地是解决此问题的唯一方法。 我在这里发布了我的解决方案:

    jndi LDAPS custom HostnameVerifier and TrustManager

    【讨论】:

      【解决方案3】:

      我认为您可能正在寻找这样的东西:

      env.put("javax.net.ssl.keyStore", keystorePath);
      //Where keystorePath is the path to the Keys file resource
      
      env.put("javax.net.ssl.keyStorePassword", "password");
      

      【讨论】:

      • 这种方法只允许在运行时使用一个证书。我正在管理自己的密钥存储(与密钥管理器和信任管理器),我需要管理多个证书并将其提供给不同的 ldap 服务器。
      • 好的,这就是我所知道的,抱歉帮不上忙,我会留意这个问题。
      猜你喜欢
      • 1970-01-01
      • 2023-03-20
      • 2015-11-29
      • 1970-01-01
      • 1970-01-01
      • 2012-08-29
      • 1970-01-01
      • 2017-07-30
      • 2021-08-18
      相关资源
      最近更新 更多