Spring Security: Default AuthenticationManager der namespace-Konfiguration austauschen

Die namespace-Konfiguration legt einen eigenen AuthenticationManager an, der sich nicht über die XML-Konfiguration austauschen lässt:

You can’t use a custom AuthenticationProvider if you are using either HTTP or method security through the namespace, but this should not be a problem as you have full control over the AuthenticationProviders that are used.

http://static.springframework.org/spring-security/site/reference/html/ns-config.html (Kapitel 2.6).

Es gibt aber Situationen, wo man genau dies möchte. Beispielsweise im Osgi-Umfeld: In einem Bundle wird ein AuthenticationManager deklariert, der von anderen Web Bundles wiederverwendet werden soll.

Der Trick (bzw. der Hack) ist, dass die BeanId des AuthenticationManager genau “_authenticationManager” heissen muss. Wenn eine solche Bean existiert, dann verwendet die namespace-Konfiguration genau diesen AuthenticationManager statt einen eigenen zu erzeugen. Der Beanname wird in der BeanIds Klasse definiert.

Vorteil der Lösung: Copy&Paste wird in den Web Bundles reduziert.
Nachteil der Lösung: Man verwendet einen nicht-dokumentierten, implementierungsabhängigen Key.

Die Vorteile überwiegen jedoch. Credits für diese Lösung gehen an Eberhard Wolff (Springsource).

Spring Security 2.0.4 – namespace-based configuration. Teil 3.

Für jeden LDAP-Server wird mit dem ldap-server Tag eine DefaultSpringSecurityContextSource Instanz registriert.

  1. <s:ldap-server id="ldapA" url="ldaps://ldapA.ads"
  2.         port="636"
  3.         manager-dn="cn=LDAPAdmin, cn=Users, dc=ldapa, dc=ads"
  4.         manager-password="1234"/>
  5.        
  6.     <s:ldap-server id="ldapB" url="ldap://ldapB.ads"
  7.         port="389"
  8.         manager-dn="cn=LDAPAdmin, cn=Users, dc=ldapb, dc=ads"
  9.         manager-password="1234"/>

Jetzt fehlen für die beiden Server nur noch zwei AuthenticationProvider:

  1. <s:ldap-authentication-provider server-ref="ldapA"
  2.         user-search-base="ou=Customer,dc=ldapa,dc=ads"
  3.         user-search-filter="(sAMAccountName={0})"
  4.         group-search-base="ou=Groups,dc=ldapa,dc=ads"
  5.         group-search-filter="(member={0})" />
  6.                    
  7. <s:ldap-authentication-provider server-ref="ldapB"
  8.         user-search-base="ou=Customer,dc=ldapb,dc=ads"
  9.         user-search-filter="(sAMAccountName={0})"
  10.         group-search-base="ou=Groups,dc=ldapb,dc=ads"
  11.         group-search-filter="(member={0})" />

Das ldap-authentication-provider Tag erzeugt einen LdapAuthenticationProvider mit einem BindAuthenticator und einen DefaultLdapAuthoritiesPopulator.
(Ein BindAuthenticator prüft ob Passwort korrekt ist, ein LdapAuthoritiesPopulator erzeugt GrantedAuthorities aus der Gruppenzugehörigkeit des Users)

Mit der namespace-Konfiguration werden diese Objekte automatisch erzeugt. Soll für eine der beiden Objekte eine eigene Implementierung greifen, so kann das ldap-authentication-provider Tag nicht verwendet werden. Hier kommt wieder das custom-authentication-provider ins Spiel.

Spring Security 2.0.4 – namespace-based configuration. Teil 2.

Beim Einsatz der namespace-Konfiguration registriert Spring Security im Hintergrund automatisch einen ProviderManager. Wenn nun (ebenfalls via namespace-Konfiguration) ein oder mehrere AuthenticationProvider konfiguriert werden, so werden sie automatisch dem ProviderManager bekannt gegeben. Dieser delegiert dann die eigentliche Authentifizierung an die konfigurierten AuthenticationProvider und erlaubt somit eine flexible Gestaltung der Authentifizierungslogik. Beispielsweise kann Spring Security den User erst gegen LDAP Server A authentifizieren, bei Misserfolg dann LDAP Server B anfragen.

Zurück zum Konfigurationsbeispiel aus Teil 1: Es ist noch unvollständig, da ein AuthenticationProvider fehlt. Spring Security liefert eine Reihe von Standardimplementierungen mit:

  • DaoAuthenticationProvider
  • LdapAuthenticationProvider
  • OpenIDAuthenticationProvider
  • CasAuthenticationProvider

Für das nächste Codebeispiel entscheiden wir uns für den DaoAuthenticationProvider, der Userattribute wie username oder password aus einem UserDetailService bezieht.

  1. <s:http>
  2.     <s:intercept-url pattern="/public/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
  3.     <s:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY " />
  4.     <s:http-basic />
  5.     <s:anonymous />
  6.   </s:http>
  7.  
  8.   <s:authentication-provider>
  9.     <s:user-service>
  10.       <s:user name="pmeier" password="secret" authorities="ROLE_USER, ROLE_ADMIN"/>
  11.       <s:user name="hmueller" password="geheim" authorities="ROLE_USER"/>
  12.     </s:user-service>
  13.   </s:authentication-provider>

Mit dieser Konfiguration haben wir zwei statische User angelegt. Mit pmeier und hmueller kann man sich nun an der Webapplikation anmelden. Was passiert aber im Hintergrund ?
Das Tag authentication-provider registriert einen DaoAuthenticationProvider. Diesem Provider wird über das Tag user-service ein UserDetailsService vom konkreten Typ InMemoryDaoImpl übergeben und mit zwei Usern statisch konfiguriert.

Alternativ hätte man für das gleiche Resultat folgendes schreiben können:

  1. <!-- DaoAuthenticationProvider with InMemoryDaoImpl -->
  2. <bean id="InMemoryProvider"
  3. class="org.springframework.security.providers.dao.DaoAuthenticationProvider">
  4.   <s:custom-authentication-provider />
  5.     <property name="userDetailsService">
  6.     <bean class="org.springframework.security.userdetails.memory.InMemoryDaoImpl">
  7.       <property name="userMap">
  8.         <value>
  9.           pmeier=secret,ROLE_USER, ROLE_ADMIN
  10.           hmueller=geheim,ROLE_USER
  11.         </value>
  12.       </property>
  13.     </bean>       
  14.   </property>
  15. </bean>

Wichtig bei dieser Alternative ist das custom-authentication-provider Tag. Es sorgt dafür, das der ProviderManager unseren InMemoryProvider überhaupt kennt.

Im nächsten Teil sehen wir uns die Konfiguration für zwei LDAP Server an.

Spring Security 2.0.4 – namespace-based configuration. Teil 1.

Mit der Version 2 von Spring Security (vormals Acegi Security) wurde die Konfiguration durch die Einführung neuer Tags erheblich vereinfacht. Die neuen Tags kapseln die Erzeugung der komplexen Filter- und Service-Beans und erhöhen somit die Lesbarkeit der Konfiguration.

Mit dem http-Tag legt der Entwickler die geschützten URLs fest und bestimmt die Authentifizierungsmethode wie z.B. Http Basic oder Forms Authentication.

Im folgenden XML-Schnipsel wird der Webauftritt in einen öffentlichen und einen geschützten Bereich aufgeteilt. Die öffentlichen Seiten beginnen mit dem URL-Prefix /public, alle anderen Seiten erfordern eine Authentifizerung.

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2.    xmlns:s="http://www.springframework.org/schema/security"
  3.    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.    xsi:schemaLocation="http://www.springframework.org/schema/beans
  5.    http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
  6.    http://www.springframework.org/schema/security
  7.    http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">
  8.    
  9.   <s:http>
  10.     <s:intercept-url pattern="/public/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
  11.     <s:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY " />
  12.     <s:http-basic />
  13.     <s:anonymous />
  14.   </s:http>

Die Reihenfolge der intercept-url-Tags ist entscheidend. Hier gilt die Regel: “most specific first”. Im access-Attribut gibt der Entwickler ein oder mehrere sog. ConfigAttribute an. Ein ConfigAttribute definiert eine Rolle oder einen Authentifizierungzustand, der benötigt wird, um eine URL aus dem Pattern aufrufen zu können. Ach ja, die Patterns müssen der Ant Pattern Notation entsprechen. Die im Beispiel verwendeten Attribute (IS_AUTHENTICATED_FULLY und IS_AUTHENTICATED_ANONYMOUS) werden in der Klasse AuthenticatedVoter definiert.

Mit dem Tag http-basic hat der Entwickler automatisch eine Http Basic Authentication registriert, d.h. beim Zugriff auf den geschützten Bereich schickt Spring Security die Http Header für die Authentifizierung mit. Der Entwickler braucht sich darum gar nicht mehr kümmern.

Allerdings ist die Konfiguration noch nicht vollständig: Es fehlt noch ein AuthenticationProvider. Zwar haben wir durch das anonymous-Tag einen AnonymousAuthenticationProvider registriert, aber es fehlt natürlich ein Provider für den nicht-anonymen Zugriff, den wir uns im nächsten Teil ansehen werden.