Je dois effectuer une authentification LDAP pour une application.
J'ai essayé le programme suivant:
import Java.util.Hashtable;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
public class LdapContextCreation {
public static void main(String[] args) {
LdapContextCreation ldapContxCrtn = new LdapContextCreation();
LdapContext ctx = ldapContxCrtn.getLdapContext();
}
public LdapContext getLdapContext(){
LdapContext ctx = null;
try{
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.Sun.jndi.ldap.LdapCtxFactory");
env.put(Context.SECURITY_AUTHENTICATION, "Simple");
//it can be <domain\\userid> something that you use for windows login
//it can also be
env.put(Context.SECURITY_PRINCIPAL, "[email protected]");
env.put(Context.SECURITY_CREDENTIALS, "password");
//in following property we specify ldap protocol and connection url.
//generally the port is 389
env.put(Context.PROVIDER_URL, "ldap://server.domain.com");
ctx = new InitialLdapContext(env, null);
System.out.println("Connection Successful.");
}catch(NamingException nex){
System.out.println("LDAP Connection: FAILED");
nex.printStackTrace();
}
return ctx;
}
}
Obtention de l'exception suivante:
Connexion LDAP: FAILED Javax.naming.AuthenticationException: [LDAP: code d'erreur 49 - Informations d'identification non valides] à l'adresse com.Sun.jndi.ldap.LdapCtx.mapErrorCode (LdapCtx.Java:3053) à l'adresse com.Sun.jndi.ldap.LdapCtx.processReturnCode (LdapCtx.Java:2999) à l'adresse com.Sun.jndi.ldap.LdapCtx.processReturnCode (LdapCtx.Java:2801) à l'adresse com.Sun.jndi.ldap.LdapCtx.connect (LdapCtx.Java:2715) sur com.Sun.jndi.ldap.LdapCtx. (LdapCtx.Java:305) à l'adresse com.Sun.jndi.ldap.LdapCtxFactory.getUsingURL (LdapCtxFactory.Java:187) à l'adresse com.Sun.jndi.ldap.LdapCtxFactory.getUsingURLs (LdapCtxFactory.Java:205) à l'adresse com.Sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance (LdapCtxFactory.Java:148) à l'adresse com.Sun.jndi.ldap.LdapCtxFactory.getInitialContext (LdapCtxFactory.Java:78) à javax.naming.spi.NamingManager.getInitialContext (NamingManager.Java:235) à l'adresse javax.naming.InitialContext.initializeDefaultInitCtx (InitialContext.Java:318) sur javax.naming.InitialContext.getDefaultInitCtx (InitialContext.Java:348) sur javax.naming.InitialContext.internalInit (InitialContext.Java:286) sur javax.naming.InitialContext.init (InitialContext.Java:308) sur javax.naming.ldap.InitialLdapContext. (InitialLdapContext.Java:99) sur LdapContextCreation.getLdapContext (LdapContextCreation.Java:27) à LdapContextCreation.main (LdapContextCreation.Java:12)
Quelques points à prendre en compte:
Auparavant, j'utilisais Tomcat 5.3.5
mais quelqu'un m'a dit que seul Tomcat 6 le prend en charge. J'ai donc téléchargé Tomcat 6.0.35
et n'utilise actuellement que cette version.
server.xml
configuré et ajouté le code suivant -
<Realm className="org.Apache.catalina.realm.JNDIRealm"
debug="99"
connectionURL="ldap://server.domain.com:389/"
userPattern="{0}" />
A commenté le code suivant de server.xml
-
<!-- Commenting for LDAP
<Realm className="org.Apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/> -->
Etapes 2 et 3 de article
Quelqu'un a suggéré que certains fichiers JAR sont supposés être copiés sur Tomcat afin d'exécuter l'authentification ldap
. Est-ce une chose à faire? Et quels fichiers jar
?
En outre, j'utilise bien les informations d'identification correctes, alors qu'est-ce qui cause ce problème?
Existe-t-il un moyen de déterminer les attributs corrects pour LDAP au cas où j'utiliserais des attributs incorrects?
Le code suivant s'authentifie à partir de LDAP à l'aide de JNDI Java pur. Le principe est: -
Extrait de code
public static boolean authenticateJndi(String username, String password) throws Exception{
Properties props = new Properties();
props.put(Context.INITIAL_CONTEXT_FACTORY, "com.Sun.jndi.ldap.LdapCtxFactory");
props.put(Context.PROVIDER_URL, "ldap://LDAPSERVER:PORT");
props.put(Context.SECURITY_PRINCIPAL, "uid=adminuser,ou=special users,o=xx.com");//adminuser - User with special priviledge, dn user
props.put(Context.SECURITY_CREDENTIALS, "adminpassword");//dn user password
InitialDirContext context = new InitialDirContext(props);
SearchControls ctrls = new SearchControls();
ctrls.setReturningAttributes(new String[] { "givenName", "sn","memberOf" });
ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration<javax.naming.directory.SearchResult> answers = context.search("o=xx.com", "(uid=" + username + ")", ctrls);
javax.naming.directory.SearchResult result = answers.nextElement();
String user = result.getNameInNamespace();
try {
props = new Properties();
props.put(Context.INITIAL_CONTEXT_FACTORY, "com.Sun.jndi.ldap.LdapCtxFactory");
props.put(Context.PROVIDER_URL, "ldap://LDAPSERVER:PORT");
props.put(Context.SECURITY_PRINCIPAL, user);
props.put(Context.SECURITY_CREDENTIALS, password);
context = new InitialDirContext(props);
} catch (Exception e) {
return false;
}
return true;
}
Ceci est mon application de test de connexion Java LDAP prenant en charge les certificats LDAP: // et LDAPS: // auto-signé. Le code provient de quelques SO publications, d'une implémentation simplifiée et de la suppression des importations Sun.Java. * Héritées.
Utilisation
Je l'ai exécuté sur des machines Windows7 et Linux avec le service d'annuaire WinAD. L'application imprime le nom d'utilisateur et les groupes de membres.
$ Java -cp classes test.LoginLDAP url = ldap: //1.2.3.4: 389 nom_utilisateur = pré[email protected] mot de passe = mypwd
$ Java -cp classes test.LoginLDAP url = ldaps: //1.2.3.4: 636 nom_utilisateur = pré[email protected] mot de passe = mypwd
L’application de test prend en charge les certificats de test auto-signés temporaires pour le protocole ldaps: //, ce DummySSLFactory accepte n’importe quel certificat de serveur, de sorte que l’intermédiaire est possible Une installation réelle doit importer le certificat du serveur dans un fichier de clés JKS local et ne pas utiliser de fabrique factice.
L'application utilise le nom d'utilisateur et le mot de passe de l'utilisateur final pour les requêtes de contexte et LDAP initiales. Cela fonctionne pour WinAD mais ne sait pas si peut être utilisé pour toutes les implémentations de serveur LDAP. Vous pouvez créer un contexte avec un nom d'utilisateur interne + pwd, puis exécuter des requêtes pour voir si un utilisateur donné est trouvé.
LoginLDAP.Java
package test;
import Java.util.*;
import javax.naming.*;
import javax.naming.directory.*;
public class LoginLDAP {
public static void main(String[] args) throws Exception {
Map<String,String> params = createParams(args);
String url = params.get("url"); // ldap://1.2.3.4:389 or ldaps://1.2.3.4:636
String principalName = params.get("username"); // [email protected]
String domainName = params.get("domain"); // mydomain.com or empty
if (domainName==null || "".equals(domainName)) {
int delim = principalName.indexOf('@');
domainName = principalName.substring(delim+1);
}
Properties props = new Properties();
props.put(Context.INITIAL_CONTEXT_FACTORY, "com.Sun.jndi.ldap.LdapCtxFactory");
props.put(Context.PROVIDER_URL, url);
props.put(Context.SECURITY_PRINCIPAL, principalName);
props.put(Context.SECURITY_CREDENTIALS, params.get("password")); // secretpwd
if (url.toUpperCase().startsWith("LDAPS://")) {
props.put(Context.SECURITY_PROTOCOL, "ssl");
props.put(Context.SECURITY_AUTHENTICATION, "simple");
props.put("Java.naming.ldap.factory.socket", "test.DummySSLSocketFactory");
}
InitialDirContext context = new InitialDirContext(props);
try {
SearchControls ctrls = new SearchControls();
ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration<SearchResult> results = context.search(toDC(domainName),"(& (userPrincipalName="+principalName+")(objectClass=user))", ctrls);
if(!results.hasMore())
throw new AuthenticationException("Principal name not found");
SearchResult result = results.next();
System.out.println("distinguisedName: " + result.getNameInNamespace() ); // CN=Firstname Lastname,OU=Mycity,DC=mydomain,DC=com
Attribute memberOf = result.getAttributes().get("memberOf");
if(memberOf!=null) {
for(int idx=0; idx<memberOf.size(); idx++) {
System.out.println("memberOf: " + memberOf.get(idx).toString() ); // CN=Mygroup,CN=Users,DC=mydomain,DC=com
//Attribute att = context.getAttributes(memberOf.get(idx).toString(), new String[]{"CN"}).get("CN");
//System.out.println( att.get().toString() ); // CN part of groupname
}
}
} finally {
try { context.close(); } catch(Exception ex) { }
}
}
/**
* Create "DC=sub,DC=mydomain,DC=com" string
* @param domainName sub.mydomain.com
* @return
*/
private static String toDC(String domainName) {
StringBuilder buf = new StringBuilder();
for (String token : domainName.split("\\.")) {
if(token.length()==0) continue;
if(buf.length()>0) buf.append(",");
buf.append("DC=").append(token);
}
return buf.toString();
}
private static Map<String,String> createParams(String[] args) {
Map<String,String> params = new HashMap<String,String>();
for(String str : args) {
int delim = str.indexOf('=');
if (delim>0) params.put(str.substring(0, delim).trim(), str.substring(delim+1).trim());
else if (delim==0) params.put("", str.substring(1).trim());
else params.put(str, null);
}
return params;
}
}
Et la classe d'assistance SSL.
package test;
import Java.io.*;
import Java.net.*;
import Java.security.SecureRandom;
import Java.security.cert.X509Certificate;
import javax.net.*;
import javax.net.ssl.*;
public class DummySSLSocketFactory extends SSLSocketFactory {
private SSLSocketFactory socketFactory;
public DummySSLSocketFactory() {
try {
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, new TrustManager[]{ new DummyTrustManager()}, new SecureRandom());
socketFactory = ctx.getSocketFactory();
} catch ( Exception ex ){ throw new IllegalArgumentException(ex); }
}
public static SocketFactory getDefault() { return new DummySSLSocketFactory(); }
@Override public String[] getDefaultCipherSuites() { return socketFactory.getDefaultCipherSuites(); }
@Override public String[] getSupportedCipherSuites() { return socketFactory.getSupportedCipherSuites(); }
@Override public Socket createSocket(Socket socket, String string, int i, boolean bln) throws IOException {
return socketFactory.createSocket(socket, string, i, bln);
}
@Override public Socket createSocket(String string, int i) throws IOException, UnknownHostException {
return socketFactory.createSocket(string, i);
}
@Override public Socket createSocket(String string, int i, InetAddress ia, int i1) throws IOException, UnknownHostException {
return socketFactory.createSocket(string, i, ia, i1);
}
@Override public Socket createSocket(InetAddress ia, int i) throws IOException {
return socketFactory.createSocket(ia, i);
}
@Override public Socket createSocket(InetAddress ia, int i, InetAddress ia1, int i1) throws IOException {
return socketFactory.createSocket(ia, i, ia1, i1);
}
}
class DummyTrustManager implements X509TrustManager {
@Override public void checkClientTrusted(X509Certificate[] xcs, String str) {
// do nothing
}
@Override public void checkServerTrusted(X509Certificate[] xcs, String str) {
/*System.out.println("checkServerTrusted for authType: " + str); // RSA
for(int idx=0; idx<xcs.length; idx++) {
X509Certificate cert = xcs[idx];
System.out.println("X500Principal: " + cert.getSubjectX500Principal().getName());
}*/
}
@Override public X509Certificate[] getAcceptedIssuers() {
return new Java.security.cert.X509Certificate[0];
}
}
Vous devrez fournir l'intégralité de l'utilisateur dn dans SECURITY_PRINCIPAL
comme ça
env.put(Context.SECURITY_PRINCIPAL, "cn=username,ou=testOu,o=test");
// this class will authenticate LDAP UserName or Email
// simply call LdapAuth.authenticateUserAndGetInfo (username,password);
//Note: Configure ldapURI ,requiredAttributes ,ADSearchPaths,accountSuffex
import Java.util.*;
import javax.naming.*;
import Java.util.regex.*;
import javax.naming.directory.*;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
public class LdapAuth {
private final static String ldapURI = "ldap://20.200.200.200:389/DC=corp,DC=local";
private final static String contextFactory = "com.Sun.jndi.ldap.LdapCtxFactory";
private static String[] requiredAttributes = {"cn","givenName","sn","displayName","userPrincipalName","sAMAccountName","objectSid","userAccountControl"};
// see you Active Directory user OU's hirarchy
private static String[] ADSearchPaths =
{
"OU=O365 Synced Accounts,OU=ALL USERS",
"OU=Users,OU=O365 Synced Accounts,OU=ALL USERS",
"OU=In-House,OU=Users,OU=O365 Synced Accounts,OU=ALL USERS",
"OU=Torbram Users,OU=Users,OU=O365 Synced Accounts,OU=ALL USERS",
"OU=Migrated Users,OU=TES-Users"
};
private static String accountSuffex = "@corp.local"; // this will be used if user name is just provided
private static void authenticateUserAndGetInfo (String user, String password) throws Exception {
try {
Hashtable<String,String> env = new Hashtable <String,String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory);
env.put(Context.PROVIDER_URL, ldapURI);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, user);
env.put(Context.SECURITY_CREDENTIALS, password);
DirContext ctx = new InitialDirContext(env);
String filter = "(sAMAccountName="+user+")"; // default for search filter username
if(user.contains("@")) // if user name is a email then
{
//String parts[] = user.split("\\@");
//use different filter for email
filter = "(userPrincipalName="+user+")";
}
SearchControls ctrl = new SearchControls();
ctrl.setSearchScope(SearchControls.SUBTREE_SCOPE);
ctrl.setReturningAttributes(requiredAttributes);
NamingEnumeration userInfo = null;
Integer i = 0;
do
{
userInfo = ctx.search(ADSearchPaths[i], filter, ctrl);
i++;
} while(!userInfo.hasMore() && i < ADSearchPaths.length );
if (userInfo.hasMore()) {
SearchResult UserDetails = (SearchResult) userInfo.next();
Attributes userAttr = UserDetails.getAttributes();System.out.println("adEmail = "+userAttr.get("userPrincipalName").get(0).toString());
System.out.println("adFirstName = "+userAttr.get("givenName").get(0).toString());
System.out.println("adLastName = "+userAttr.get("sn").get(0).toString());
System.out.println("name = "+userAttr.get("cn").get(0).toString());
System.out.println("AdFullName = "+userAttr.get("cn").get(0).toString());
}
userInfo.close();
}
catch (javax.naming.AuthenticationException e) {
}
}
}