# LDAP via JNDI



## jj060286 (4. Aug 2010)

Hallo liebe Leute,

ich arbeite gerade an einem Bundle das zur Authentifizierung an einem Microsoft Active Directory Service dient, diese wird dann per OSGi zur Verfügung gestellt. Mein Problem dabei ist das ich nicht wirklich weiter komme, ich kann auch mit der Exception nicht wirklich viel anfangen.

Ich würde mich freuen wenn Ihr mir helfen könntet.


```
javax.naming.PartialResultException: [LDAP: error code 10 - 0000202B: RefErr: DSID-031006E0, data 0, 1 access points
	ref 1: 'test remaining name 'dc=test
	at com.sun.jndi.ldap.LdapCtx.processReturnCode(Unknown Source)
	at com.sun.jndi.ldap.LdapCtx.processReturnCode(Unknown Source)
	at com.sun.jndi.ldap.LdapCtx.c_lookup(Unknown Source)
	at com.sun.jndi.toolkit.ctx.ComponentContext.p_lookup(Unknown Source)
	at com.sun.jndi.toolkit.ctx.PartialCompositeContext.lookup(Unknown Source)
	at com.sun.jndi.toolkit.ctx.PartialCompositeContext.lookup(Unknown Source)
	at javax.naming.InitialContext.lookup(Unknown Source)
	at LDAPAuthenticator.main(LDAPAuthenticator.java:22)
```

mein dazugehöriger Code ist:

```
public class LDAPAuthenticator {

	public static void main(String[] args) {
		Hashtable<String, String> env = new Hashtable<String, String>();
		env.put(Context.INITIAL_CONTEXT_FACTORY,
				"com.sun.jndi.ldap.LdapCtxFactory");
		env.put(Context.PROVIDER_URL, "ldap://192.168.2.11:389");
		env.put(Context.SECURITY_AUTHENTICATION, "simple");
		env.put(Context.SECURITY_PRINCIPAL, "Administrator");
		env.put(Context.SECURITY_CREDENTIALS, "123456");

		try {
			Context ctx = new InitialContext(env);
			NamingEnumeration list = (NamingEnumeration) ctx.lookup("dc=test");
			System.out.println(list);

			ctx.close();
		} catch (NamingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}
```

ich möchte einfach nur mal einen Eintrag ausgeben oder wissen ob die Verbindung erfolgreich ist?!
der dc exestiert.
gibt es denn eine möglichkeit einfach mal durch das LDAP durch zu "browsen" um vielleicht die Struktur zu sehen?
vielen Dank für die netten antworten:rtfm:


----------



## cr4ch (4. Aug 2010)

Also als erstes kann ich persönlich das Tool hier empfehlen
LDAP Browser-Editor by Jarek Gawor for the Java 2 Platform
Dummerweise finde ich nicht mehr die richtige Homepage ...

zum Zweiten funktioniert dein Code was die Verbindung zum Active Directory angeht.

Zum Verbindungsaufbau empfehle ich aber anstatt
[JAVA=13]Context ctx = new InitialContext(env);[/code]
lieber

```
LdapContext ctx = new InitialLdapContext(env, null);
```
zu verwenden.

Das Suchen ist eigentlich auch recht simpel, ich kopiert mal ein wenig Code aus dem JavaMagazin

```
String filter = "(sAMAccountName=user)";
String searchSuffix = "DC=de";

SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);

NamingEnumeration<SearchResult> result = ctx.search(searchSuffix,
		filter, searchControls);
if (result.hasMoreElements())
	SearchResult sr = result.next();
```

Ich hoffe die Begriffe bzw. der Ablauf sind klar, sonst kann ich gerne noch helfen.
Gruß

P.S. Deine Fehlermeldung kommt auch dann, wenn kein Ergebnis gefunden wird. DC=de ist die unterste Einheit im Active Directory. Bei uns heist die übrigens DC=firmenname, DC=de
Unter sAMAccountName werden im Active Directory der Benutzer abgelegt
P.P.S. heute schein nicht mein Tag zu sein, zweiter Edit


----------



## jj060286 (4. Aug 2010)

Hi cr4ch,
danke für die schnelle Antwort, ich habe den Vorschlag mit dem Ldapcontext mit eingebaut, es funktioniert jetzt. Ich habe einen DC beim Server mit angegeben.
Allerdings gefällt mir der Code noch nicht siehst du eventuell noch was zum optimieren.


```
public class LDAPAuthenticator implements IAuthentication {

	@Override
	public boolean authenticate(String Username, String Password) {
		Hashtable<String, String> env = new Hashtable<String, String>();
		env.put(LdapContext.INITIAL_CONTEXT_FACTORY,
				"com.sun.jndi.ldap.LdapCtxFactory");
		env.put(LdapContext.PROVIDER_URL, "ldap://192.168.2.11:389/DC=test,DC=local");
		env.put(LdapContext.SECURITY_AUTHENTICATION, "simple");
		env.put(LdapContext.SECURITY_PRINCIPAL, Username);
		env.put(LdapContext.SECURITY_CREDENTIALS, Password);
		LdapContext ctx = null;
		try {
			ctx = new InitialLdapContext(env, null);
			
			
			NamingEnumeration<NameClassPair> list = ctx.list("OU=test");
			
			/*
			while(list.hasMore()) {
				NameClassPair item = list.next();
				System.out.println(item);
			} */
			
			ctx.close();
		} catch (NamingException e) {
			//e.printStackTrace();
			return false;
		}	
		
		return true;
	}
```


----------



## Gast2 (4. Aug 2010)

Mir fällt auf:

1) Variablennamen sollten mit Großbuchstaben  beginnen 
2) String Literale als Konstanten definieren
3) Warum ist der Server hardcoded?
4) Die NamingException mit loggen, z.B. mit log4j
5) Der LdapContext könnte in dem try-catch deklariert werden, Varibalenscope immer möglichst klein halten - auch wenn es hier jetzt mehr oder minder egal ist.
6) Die Klasse könnte final sein, ebenso wie die übergebenen Strings

Sonst - soviel code ist es ja nicht, also groß was anderes kann man ja nun nicht machen. Was gefällt DIR denn daran nicht?


----------



## jj060286 (4. Aug 2010)

Ok hab das soweit gemacht, ich habe gerade gelesen das man mit Swing auch authentifizieren kann?! evtl. auch gegen ein LDAP ? dann könnte ich mir das hier ja sparen?!

hat heir da jemadn Erfahrung? mich würde auch nco hinterressieren was es jetzt noch auszusetzen gibt an meiner LDAPAuthenticator Klasse

```
public final class LDAPAuthenticator implements IAuthentication {

	private static final String CONTEXT_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
	private static final String AUTHENTICATION_METHOD = "simple";

	private String Server;

	public LDAPAuthenticator(String Server) {
		this.Server = Server;
	}

	public String getServer() {
		return Server;
	}

	@Override
	public boolean authenticate(String Username, String Password) {
		Hashtable<String, String> Env = new Hashtable<String, String>();
		Env.put(LdapContext.INITIAL_CONTEXT_FACTORY, CONTEXT_FACTORY);
		Env.put(LdapContext.PROVIDER_URL, this.getServer());
		Env.put(LdapContext.SECURITY_AUTHENTICATION, AUTHENTICATION_METHOD);
		Env.put(LdapContext.SECURITY_PRINCIPAL, Username);
		Env.put(LdapContext.SECURITY_CREDENTIALS, Password);
		try {
			LdapContext Ctx = new InitialLdapContext(Env, null);
			Ctx.close();
		} catch (NamingException e) {
			return false;
		}

		return true;
	}

}
```

was mir vor allem nciht gefällt ist das ich bei der Exception das false zurückgeben muss und nciht sowas abfragen kann wie "isbind()" oder ähnlich also das bei einer fehlerhaften authentifizierung tatsächlich eine Exception geflogen kommt, finde ich ncith so toll gelöst.

Log4J einbauen ??? was bringt mir das? 

DANKE!


----------



## Gast2 (4. Aug 2010)

Du hast immer noch die Varibalen mit beginnenden Großbuchstaben. Aber das ist nur eine Formsache. 

Log4j hat den Vorteil das du sehr bequem deine Applikation mitloggen kannst. Wenn jetzt z.B. die NamingException geworfen wird weißt du nicht warum. Weil der Username falsch war? Oder weil es keine Connection zu dem Server gab?

Wenn du die Fehlermeldung in ein log schreibst weißt du zumindest wo du zu suchen hast.

Wenn das Interface dir vorschreibt boolean zuzugeben und keine Exception wirft hast du leider keine weiteren Chancen als es so zu machen. Und eigentlich finde ich das auch ganz gut so. Was erwartest du denn wenn sich der Service nicht authentifizieren kann? Das der Context dann null ist? Dann müsstest du dass jedesmal prüfen - und wir hätten hier jede Menge "Warum NPE bei LDAP Auth" Topics . In diesem Fall fliegt halt eine Exception die man sauber behandeln kann wenn es fehlgeschlagen ist und gut.


----------



## jj060286 (4. Aug 2010)

Hallo Fassy,

das Interface habe ich ja defineirt, wie würdest du denn ein authenitcate Interface definieren?? oder würdest du einen Authenticattor als abstrakte Klasse machen??
Ich kann ja einen DatabaseAuthenticator haben oder eben einen LDAP Authenticator .... wie würdest du dir denn diese Erweiterbarkeit offenhalten?

in Log4J muss ich mcih erst ien wenig reinarbeiten, die Variablennamen habe ich jetzt als klein buchstaben 

schreibt Log4J in ein txt File oder wie kann ich die Logs dann sichtbar machen?

danke dir!


----------



## Gast2 (4. Aug 2010)

Hi,

hab mein letzte Post nochmal editiert. Ich finde das so eigentlich ganz gut. Entweder so - oder eine Exception weiter reichen. Aber da es bei authenticate eigentlich nur "Hat geklappt" und "Hat nicht geklappt" gibt ist die LDAP Exception kapseln und auf true/false mappen eigentlich ein guter Weg.


----------



## jj060286 (4. Aug 2010)

Ich kann ja einen DatabaseAuthenticator haben oder eben einen LDAP Authenticator .... 
wie würdest du dir denn diese Erweiterbarkeit offenhalten?


----------



## Gast2 (4. Aug 2010)

Genauso wie du es gemacht hast. Bei einem DatenbankAuthenticatior wäre es ja dann:


```
@Override
    public boolean authenticate(String Username, String Password) {

        try {
            //SELECT to database
            if (no results matching) return false;

        } catch (SQLException e) {
            return false;
        }
 
        return true;
    }
```


----------



## jj060286 (4. Aug 2010)

danke dir für die hilfe, ich werde log4j noch einbauen, das finde ich ganz cool 

schönen Abend dir noch


----------



## vsk (25. Aug 2010)

dürfte ich mal ganz frech nach dem kompletten code fragen  würd ich gern benutzen.


----------



## Tomate_Salat (25. Aug 2010)

fassy hat gesagt.:


> 1) Variablennamen sollten mit Großbuchstaben  beginnen



Dürfte ich erfahren, wo du den Mist her hast? In Java schreibt man diese *klein*!


----------



## vsk (25. Aug 2010)

Ich hoffe das kommt jetzt nicht falsch rueber oder entspricht nicht der Art und Weise
Aber für alle die auf der Suche nach einem funktionierenden Beispiel (copy&past&modify) sind
hier was ich gefunden habe:
Ein Beispiel bei dem nur noch die entprechenden Werte ein zu tragen bzw. zu ändern sind
[Quelle:http://www.renemoser.net/2008/01/ldap-authentication-using-java/]

```
import javax.naming.*;
import javax.naming.directory.*;
import java.util.Hashtable;
 
/**
 * Demonstrates how to create an initial context to an LDAP server
 * using simple authentication.
 */
 
class Simple {
    public static void main(String[] args) {
    	Hashtable authEnv = new Hashtable(11);
    	String userName = "johnlennon";
    	String passWord = "sushi974";
    	String base = "ou=People,dc=example,dc=com"; //evtl. noch ein weiteres ou= abhängig von der struktur
    	String dn = "uid=" + userName + "," + base;
    	String ldapURL = "ldap://ldap.example.com:389";
 
    	authEnv.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
   		authEnv.put(Context.PROVIDER_URL, ldapURL);
   		authEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
   		authEnv.put(Context.SECURITY_PRINCIPAL, dn);
   		authEnv.put(Context.SECURITY_CREDENTIALS, passWord);
 
    	try {
    		DirContext authContext = new InitialDirContext(authEnv);
    		System.out.println("Authentication Success!");
    	} catch (AuthenticationException authEx) {
    		System.out.println("Authentication failed!");
 
    	} catch (NamingException namEx) {
    		System.out.println("Something went wrong!");
    		namEx.printStackTrace();
    	}
    }
}
```


----------

