Beispiel zur Authentifzierung via Spring Security gegen Active Directory über LDAP

Thomas Darimont

Erfahrenes Mitglied
Hallo,

hier mal ein kleines einfaches Beispiel zur LDAP Authentifizierung gegen Active Directory mit Spring Security.
Ich verwende hier das Spring (Security) Framework 3.0.5-Release, Maven und Windows Server 2008 R2.

Das Active Diretcory läuft unter Windows 2008 Server R2. Dort gibt es eine Domäne namens: ad.tutorials.de.local
In dieser Domäne gibt es einen User "Thomas.Darimont" und eine Gruppe "Moderator". Thomas.Darimont ist Mitglied der
Gruppe "Moderator".

Diese Information möchten wir gerne zu Authentifizierungszwecken in einer Anwendung verwenden.
In der "Anwendung" möchten wir zum einen mit der passenden Username / Passwort Kombination authentifzieren und zum anderen
möchten wir die Information zur Gruppenzugehörigkeit ("Thomas.Darimont" ist Mitglied der Gruppe "Moderator") in der Anwendung mitbenutzen.

pom.xml (Maven)
XML:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>de.tutorials</groupId>
	<artifactId>de.tutorials.spring.security.training</artifactId>
	<version>1.0.0-SNAPSHOT</version>


	<properties>
		<org.springframework.version>3.0.5.RELEASE</org.springframework.version>
		<org.springframework.security.version>3.0.5.RELEASE</org.springframework.security.version>
	</properties>

	<dependencies>

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-core</artifactId>
			<version>${org.springframework.security.version}</version>
			<scope>compile</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>${org.springframework.security.version}</version>
			<scope>compile</scope>
		</dependency>


		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-ldap</artifactId>
			<version>${org.springframework.security.version}</version>
			<scope>compile</scope>
		</dependency>
	</dependencies>

</project>

Java:
package de.tutorials;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

public class SpringSecurityADAuthExample {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/META-INF/spring/context.xml");
		AuthenticationProvider authProvider = ctx.getBean(AuthenticationProvider.class);
		
		Authentication authTD = authProvider.authenticate(new UsernamePasswordAuthenticationToken("Thomas.Darimont", "#123abc"));
	
		printAuthInfo(authTD);
	
		try{
			Authentication authUU = authProvider.authenticate(new UsernamePasswordAuthenticationToken("Unknown.User", "12345"));
		}catch(Exception ex){
			System.out.println("Authentication failed: " + ex.getMessage());
		}
	}

	static void printAuthInfo(Authentication auth) {
		System.out.println("Authentification: " + auth);
		System.out.println("Authentification Name: " + auth.getName());
		System.out.println("Authentification Authorities: " + auth.getAuthorities());
	}
}

context.xml:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jee="http://www.springframework.org/schema/jee" xmlns:lang="http://www.springframework.org/schema/lang"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util"
	xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:sec="http://www.springframework.org/schema/security"
	xmlns:statement="http://www.dacos.com/schema/rse/persistence/statement"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 						http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
 						http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
						http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
						http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd
						http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
						http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
						http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
						http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd 
						http://www.dacos.com/schema/rse/persistence/statement http://www.dacos.com/schema/rse/persistence/statement/statement-3.0.xsd"
	default-autowire="byName">
	
	<import resource="classpath*:/META-INF/spring/*.context.xml"/>
</beans>

security.ldap.context.xml:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jee="http://www.springframework.org/schema/jee" xmlns:lang="http://www.springframework.org/schema/lang"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xmlns:security="http://www.springframework.org/schema/security"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
		http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
		http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/util/spring-jdbc-3.0.xsd
		http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd"
	default-autowire="byName">

	<bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
		<constructor-arg value="ldap://192.168.2.100:389/dc=ad,dc=tutorials,dc=de,dc=local" /> <!-- Adresse des Domain Controllers und FQDN der Domäne-->
		<property name="userDn" value="ADUser"/> <!-- eingeschränkter Benutzer für LDAP Suchanfragen-->
		<property name="password" value="#123abc"/>
	</bean>

	<bean id="ldapUserSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
		<constructor-arg index="0" value="" />
		<constructor-arg index="1" value="(sAMAccountName={0})" />
		<constructor-arg index="2" ref="contextSource" />
	</bean>

	<bean id="ldapBindAuthenticator" class="org.springframework.security.ldap.authentication.BindAuthenticator">
		<constructor-arg ref="contextSource" />
		<property name="userSearch" ref="ldapUserSearch" />
	</bean>

	<bean id="ldapAuthoritiesPopulator" class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
		<constructor-arg ref="contextSource" />
		<constructor-arg value="" />
		<property name="groupSearchFilter" value="(member={0})" />
		<property name="groupRoleAttribute" value="CN"/>
		<property name="rolePrefix" value="ROLE_" /> <!-- Gruppen -> Authorities -> Gruppennamen ROLE_ voranstellen -> Moderator -> ROLE_MODERATOR -->
		<property name="searchSubtree" value="true" />
		<property name="convertToUpperCase" value="true" />
		<property name="ignorePartialResultException" value="true"/> <!-- 
				Wichtig****** beim  Zugriff auf Active Directory wird diese Exception intern geworfen, wir wollen das ignorieren, deshalb setzten wir hier den
				Wert true. -->
	</bean>

	<bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
		<constructor-arg ref="ldapBindAuthenticator" />
		<constructor-arg ref="ldapAuthoritiesPopulator" />
	</bean>


	<bean name="ldapUserDetailsService" class="org.springframework.security.ldap.userdetails.LdapUserDetailsService">
		<constructor-arg ref="ldapUserSearch" />
		<constructor-arg ref="ldapAuthoritiesPopulator"/>
	</bean>


</beans>

Ausgabe:
Code:
18.06.2011 02:32:06 org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31d520c4: startup date [Sat Jun 18 02:32:06 CEST 2011]; root of context hierarchy
18.06.2011 02:32:06 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [META-INF/spring/context.xml]
18.06.2011 02:32:06 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from file [C:\development\java\workspaces\2.6.1\de.tutorials.spring.security.training\target\classes\META-INF\spring\security.ldap.context.xml]
18.06.2011 02:32:06 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@f581593: defining beans [contextSource,ldapUserSearch,ldapBindAuthenticator,ldapAuthoritiesPopulator,ldapAuthProvider,ldapUserDetailsService]; root of factory hierarchy
18.06.2011 02:32:06 org.springframework.security.ldap.DefaultSpringSecurityContextSource <init>
INFO:  URL 'ldap://192.168.2.100:389/dc=ad,dc=tutorials,dc=de,dc=local', root DN is 'dc=ad,dc=tutorials,dc=de,dc=local'
18.06.2011 02:32:06 org.springframework.security.ldap.search.FilterBasedLdapUserSearch <init>
INFO: SearchBase not set. Searches will be performed from the root: dc=ad,dc=tutorials,dc=de,dc=local
18.06.2011 02:32:06 org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator setGroupSearchBase
INFO: groupSearchBase is empty. Searches will be performed from the context source base
18.06.2011 02:32:06 org.springframework.security.ldap.SpringSecurityLdapTemplate$3 executeWithContext
INFO: Ignoring PartialResultException
18.06.2011 02:32:06 org.springframework.ldap.core.LdapTemplate assureReturnObjFlagSet
INFO: The returnObjFlag of supplied SearchControls is not set but a ContextMapper is used - setting flag to true
Authentification: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@a267a95f: Principal: org.springframework.security.ldap.userdetails.LdapUserDetailsImpl@ffde1414: Dn: cn=Thomas Darimont,cn=Users,dc=ad,dc=tutorials,dc=de,dc=local; Username: Thomas.Darimont; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; CredentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_MODERATOR; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_MODERATOR
Authentification Name: Thomas.Darimont
Authentification Authorities: [ROLE_MODERATOR]
18.06.2011 02:32:06 org.springframework.security.ldap.SpringSecurityLdapTemplate$3 executeWithContext
INFO: Ignoring PartialResultException
Authentication failed: Bad credentials

Gruß Tom
 
Zuletzt bearbeitet von einem Moderator:

Thomas Darimont

Erfahrenes Mitglied
Hallo,

Wer hätte es gedacht ;)


Das ist aber nicht so gewünscht oder?
also die Partial Result Exception lässt sich AFAIK beim Zugriff via Spring LDAP auf Active Directory nur vermeiden, wenn man noch weitere Konfiguration hinterlegt... siehe auch:
https://jira.springsource.org/browse/SEC-832

Ich wollte das Beispiel möglichst klein halten.

Das für einen unbekannten Benutzer die Authentifizierung mit "Bad Credentials" fehlschlägt ist IMHO korrekt.


Gruß Tom