springboot配置ldaps连接方式
作者:天道有情战天下
这篇文章主要介绍了springboot配置ldaps连接方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
springboot配置ldaps连接
spring boot配置ldap 连接时,通过ldap://xxxxx:389 连接,一般来说都能成功,但是如果配置ldap ssl 连接,ldaps://xxxx:636 那么很大概率会出现
javax.naming.CommunicationException: simple bind failed: xxxxxtest.com.local:636
这种异常 。
百度,谷歌搜索 大部分解决方案是需要从ldap 服务器上导出证书,然后再通过Java的keytool 工具导入证书,比较繁琐,我也没试过好不好使,反正从服务器上导出证书那一步就很烦了。
说一下如何代码配置ldap跳过ssl
直接上代码。
package com.github.wxiaoqi.security.common.config; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.ldap.core.LdapTemplate; import org.springframework.ldap.core.support.LdapContextSource; import java.util.Hashtable; import java.util.Objects; /** * @author margo * @date 2021/11/4 */ @Slf4j // @ConditionalOnExpression("${ldap.enabled:false}") public class LdapConfiguration { private LdapTemplate ldapTemplate; @Value("${ldap.url}") private String ldapUrl; @Value("${ldap.basedc}") private String ldapBaseDc; @Value("${ldap.username}") private String ldapUsername; @Value("${ldap.passwd}") private String ldapPasswd; /** * 继承LdapContextSource重写getAnonymousEnv方法来加载, * 使连接ldap时用SSL连接(由于修改AD密码时必须使用SSL连接) */ public class SsldapContextSource extends LdapContextSource { @Override public Hashtable<String, Object> getAnonymousEnv(){ Hashtable<String, Object> anonymousEnv = super.getAnonymousEnv(); anonymousEnv.put("java.naming.security.protocol", "ssl"); anonymousEnv.put("java.naming.ldap.factory.socket", CustomSslSocketFactory.class.getName()); return anonymousEnv; } } @Bean public LdapContextSource contextSource() { SsldapContextSource ldapContextSource = new SsldapContextSource(); ldapContextSource.setBase(ldapBaseDc); ldapContextSource.setUrl(ldapUrl); ldapContextSource.setUserDn(ldapUsername); ldapContextSource.setPassword(ldapPasswd); ldapContextSource.setPooled(false); ldapContextSource.setReferral("follow"); ldapContextSource.afterPropertiesSet(); return ldapContextSource; } @Bean public LdapTemplate ldapTemplate(LdapContextSource contextSource) { if (Objects.isNull(contextSource)) { throw new RuntimeException("ldap contextSource error"); } if (null == ldapTemplate) { ldapTemplate = new LdapTemplate(contextSource); } return ldapTemplate; } }
package com.github.wxiaoqi.security.common.config; import javax.net.SocketFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; import java.security.SecureRandom; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; /** * 自定义的SSL工厂里面加载自己实现X509TrustManager,信任自签证书 * @author cb */ public class CustomSslSocketFactory extends SSLSocketFactory { private SSLSocketFactory socketFactory; public CustomSslSocketFactory() { try { SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(null, new TrustManager[]{new DummyTrustmanager()}, new SecureRandom()); socketFactory = ctx.getSocketFactory(); } catch (Exception ex) { ex.printStackTrace(System.err); } } public static SocketFactory getDefault() { return new CustomSslSocketFactory(); } @Override public String[] getDefaultCipherSuites() { return socketFactory.getDefaultCipherSuites(); } @Override public String[] getSupportedCipherSuites() { return socketFactory.getSupportedCipherSuites(); } @Override public Socket createSocket(Socket socket, String string, int num, boolean bool) throws IOException { return socketFactory.createSocket(socket, string, num, bool); } @Override public Socket createSocket(String string, int num) throws IOException, UnknownHostException { return socketFactory.createSocket(string, num); } @Override public Socket createSocket(String string, int num, InetAddress netAdd, int i) throws IOException, UnknownHostException { return socketFactory.createSocket(string, num, netAdd, i); } @Override public Socket createSocket(InetAddress netAdd, int num) throws IOException { return socketFactory.createSocket(netAdd, num); } @Override public Socket createSocket(InetAddress netAdd1, int num, InetAddress netAdd2, int i) throws IOException { return socketFactory.createSocket(netAdd1, num, netAdd2, i); } /** * 证书 */ public static class DummyTrustmanager implements X509TrustManager { @Override public void checkClientTrusted(X509Certificate[] cert, String string) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] cert, String string) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return new java.security.cert.X509Certificate[0]; } } }
主要的配置是 CustomSslSocketFactory 这个类,其他的正常配置。
配置好后启动应用,又出现了另外一个错误,
javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException
在启动main方法中加上一行环境变量配置即可
@EnableEurekaClient @SpringBootApplication @EnableConfigurationProperties @EnableTransactionManagement @Import(value = {RedissonConfig.class, GatewayReqInterceptor.class, UserInfoInterceptor.class, InterceptorConfig.class, CoreConfig.class, AuthConfig.class, AuthServerRunner.class, LdapConfiguration.class}) @EnableScheduling public class WxCpApplication { public static void main(String[] args) { System.setProperty("com.sun.jndi.ldap.object.disableEndpointIdentification", "true"); // important resolve javax.net.ssl.SSLHandshakeException SpringApplication.run(WxCpApplication.class, args); } } System.setProperty("com.sun.jndi.ldap.object.disableEndpointIdentification", "true"); 这行
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。