Spring Security
By: Jethro Bakker, 4 July 2008Het is inmiddels al even geleden dat Acegi Security gerebrand is naar Spring Security. Acegi stond bekend om de erg lastige configuratie. Een gebruiker heeft zelfs gezegd dat: “every time I use Acegi a fairy dies..”
Je moest bijvoorbeeld handmatig een heel aantal Servlet Filters configureren en een werkende configuratie bestond uit minimaal een A4tje.
Dan Diephouse zegt hierover:
“The fact that I need to write *180* lines of XML to configure Acegi for a simple simple user authentication in front of a webapp means that something is eriously wrong. There should be default profiles which configure things out of the box and provide minimal switches so users can’t screw things up.”
Met de komst van Spring 2.0 is er de mogelijkheid om custom namespaces te gebruiken in je XML configuratie. Spring Security maakt hier, sinds versie 2.0, ook gebruik van. De configuratie kan hierdoor bestaan uit een paar regels ipv een A4tje. De referentie handleiding is een goed punt om te starten.
Om te beginnen moet je een DelegatingFilterProxy definiëren in je web.xml:
<filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter><filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
In je Spring configuratie voeg je een extra namespace toe:
<beans xmlns="http://www.springframework.org/schema/security"> </beans> xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd" ...
Met behulp van een paar regels heb je een basis configuratie (form based):
<http auto-config="true"> <intercept-url pattern="/**" access="ROLE_USER"></intercept-url> </http><authentication-provider> <user-service> <user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN"> </user> <user name="bob" password="bobspassword" authorities="ROLE_USER"></user> </user-service> </authentication-provider>
Op de achtergrond worden alle filters die in Acegi gebruikt werden gestart.
Om bovenstaande configuratie werkend te krijgen moest ik eerst upgraden naar de nieuwste versie van WAS 6. Er zit een enorme bug in zowel 6.0 als 6.1: http://www-1.ibm.com/support/docview.wss?rs=180&uid=swg1PK27620 Na het installeren van de laatste fixpack werkte alles.
Case
Op mijn huidige project is het de bedoeling dat onze applicatie geïntegreerd wordt in een Siebel omgeving. De gebruiker logt in op Siebel en kan vervolgens naar onze Java applicatie gaan. De gebruiker hoeft maar 1x in te loggen. Onze applicatie krijgt een Cookie binnen met o.a. de gebruikersnaam. Op basis van de gebruikersnaam roept onze applicatie een externe service aan om de rollen van de gebruiker op te halen. Hoe zetten we Spring Security in om dit te regelen? Het voorbeeld hierboven beschreven is in ieder geval niet toereikend.
De referentiehandleiding is weer een goed startpunt het heeft namelijk een hoofdstuk over pre authenticatie scenario’s. Dat zijn scenario’s waarin een ander systeem –dat aangeroepen wordt voor je eigen applicatie- verantwoordelijk is voor de authenticatie. Onze eigen applicatie is alleen verantwoordelijk voor de autorisatie. We moeten een eigen implementatie maken AbstractPreAuthenticatedProcessingFilter en van de UserDetailsService.
Het filter haalt o.a. de gebruikersnaam uit het request cookie.
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) { Cookie[] cookies = request.getCookies(); if (cookies != null) { for (int i = 0; i < cookies.length; i++) { Cookie cookie = cookies[i]; //TODO haal user principal uit cookie } } return "DUMMY_USER"; }
De service ziet er zo uit:
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException { //TODO check username against external system GrantedAuthority[] authorities = new GrantedAuthority[1]; authorities[0] = new GrantedAuthorityImpl("ROLE_ALL"); //authorities[0] = new GrantedAuthorityImpl("ROLE_NOACCESS"); //authorities[0] = new GrantedAuthorityImpl("ROLE_RESTRICTED"); return new User("DUMMY_USER", "DUMMY_PASSWORD", true, true, true, true, authorities); }
En in de Spring configuratie doe je het volgende:
<security:http auto-config="true"> <security:intercept-url pattern="/**" access="ROLE_ALL, ROLE_RESTRICTED"> </security:intercept-url><security:authentication-manager alias="authenticationManager"></security:authentication-manager> <security:authentication-provider user-service-ref="userDetailsService"></security:authentication-provider> <bean id="userDetailsService" class="MyUserDetailsService"></bean> <bean id="cookieFilter" class="CookiePreAuthenticatedProcessingFilter"> <security:custom-filter position="PRE_AUTH_FILTER"></security:custom-filter></bean> <property name="authenticationManager" ref="authenticationManager"></property> </security:http><bean id="preauthAuthProvider" class="org..preauth.PreAuthenticatedAuthenticationProvider"> <security:custom-authentication-provider></security:custom-authentication-provider></bean> <property name="preAuthenticatedUserDetailsService"> <bean id="userDetailsServiceWrapper" class="..UserDetailsByNameServiceWrapper"> <property name="userDetailsService" ref="userDetailsService"></property> </bean> </property>
Het is mij erg meegevallen om werkende configuratie te maken en aan te passen aan de specifieke eisen binnen ons project. De configuratie van security is in ieder geval met de release van Spring Security 2.0 een stuk eenvoudiger geworden!

5 July 2008 om 4:07 pm
Dat is idd zeer relaxed. Ik kan me nog herinneren dat ik flink heb moeten zoeken om Acegi aan de praat te krijgen. En dat is nog niet echt lang geleden.
Het was ook nogal foutgevoelig en bij security heb ik dat liever niet…
Maar goed, alles werkt beter dan JBoss SX…