Spring Framework Security (Acegi)
Acegi Security is a powerful, flexible security solution for enterprise software, with a particular emphasis on applications that use Spring. Acegi Security provides your applications with comprehensive authorization, instance-based access control, and human user detection capabilities.
Starting from the 1.6 release, JOSSO fully integrates with the Spring Framework Security infrastructure, better known as Acegi Security.
When JOSSO is integrated with Acegi, authorization requests are handled by Acegi, leaving JOSSO as the component responsible for the realizing the authentication concerns and providing the single sign-on experience. Once a user has been authenticated by the SSO infrastructure, the security context is propagated to the Acegi runtime for the authorization assertions to work based on the user identity.
The integration leverages both the JOSSO and Acegi pluggable architecture: JOSSO provides specific implementations of Acegi components to basically enable identity injection and delegating authentication requests to the SSO Gateway.
This guide describes the steps required to setup JOSSO within your Acegi-enabled Spring web application.
Prerequisites
You need a running JOSSO infrastructure to deploy your application. You can download one of the bundles or install JOSSO in your container from scratch . Check out the Setup documentation for specific instructions on how to do this.
Since you can setup JOSSO in order to retrieve users from arbitrary identity stores, you would probably prefer to access the repository that Acegi uses to store authorization-specific information. JOSSO provides the whole identity entry for the user (mainly user details and roles), therefore Acegi does not need to know where users are stored but only where authorization information is.
Introduction
This guide is based on the "contacts" sample application provided with Acegi.
Building
Once the prerequisites are satisfied you can start building JOSSO by invoking the build script for your OS from the project root.
For Unix systems you should execute :
$ ./build.sh
For Win32 systems you should execute :
build.bat
You should find the josso-1.7.jar file in the ./build/josso-1.7 directory .
Install JOSSO classes
Now that we've built JOSSO, we need to install the required files onto the Acegi-enabled web application.
First copy JOSSO classes and libs to the web application lib directory (WEB-INF/lib). The file can be found at $JOSSO_HOME/build/josso-1.7/josso-1.7.jar and other needed jars are located at $JOSSO_HOME/lib/core
Setup Acegi application context
Then you have to install the Acegi Application context configuration that registers JOSSO componentes within Acegi. The descriptor, provided with JOSSO distribution, can be found in the $JOSSO_HOME/src/resources/spring/acegi/applicationContext-acegi-security-JOSSO.xml directory. This file must be placed within the web application classes directory : WEB-INF/.
Lets have a look at a sample Spring context file that enables JOSSO as the Single Sign-On facility to be used for Acegi :
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!-- ======================== FILTER CHAIN ======================= --> <!-- if you wish to use channel security, add "channelProcessingFilter," in front of "httpSessionContextIntegrationFilter" in the list below --> <bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy"> <property name="filterInvocationDefinitionSource"> <value><![CDATA[ CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT /**=httpSessionContextIntegrationFilter,jossoProcessingFilter,logoutFilter,exceptionTranslationFilter,filterInvocationInterceptor ]]></value> </property> </bean> <!-- ======================== JOSSO BEANS ======================= --> <!-- A different gateway endpoint must be used if the if JOSSO gateway is not running in the same host as this application --> <bean id="jossoGatewayServiceLocator" class="org.josso.gateway.WebserviceGatewayServiceLocator"> <property name="endpoint"><value>localhost:8080</value></property> <!--property name="endpoint"><value>josso-gwy-server:8080</value></property--> </bean> <bean id="jossoProcessingFilterEntryPoint" class="org.josso.spring.acegi.JOSSOProcessingFilterEntryPoint"/> <bean id ="jossoProcessingFilter" class="org.josso.spring.acegi.JOSSOProcessingFilter"> <constructor-arg> <list> <bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/> </list> </constructor-arg> <property name="userDetailsService"><ref local="jossoUserDetailsService"/></property> </bean> <bean id="jossoUserDetailsService" class="org.josso.spring.acegi.JOSSOUserDetailsService"> <property name="gatewayServiceLocator"><ref local="jossoGatewayServiceLocator"/></property> </bean> <bean id="authenticationManager" class="org.josso.spring.acegi.JOSSOAuthenticationManager"> </bean> <!-- ======================== AUTHENTICATION ======================= --> <!-- Automatically receives AuthenticationEvent messages --> <bean id="loggerListener" class="org.acegisecurity.event.authentication.LoggerListener"/> <bean id="httpSessionContextIntegrationFilter" class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"> </bean> <!-- Configure JOSSO Logout URL after ACEGI logout ... --> <bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter"> <constructor-arg value="/josso_logout/"/> <!-- URL redirected to after logout --> <constructor-arg> <list> <bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/> </list> </constructor-arg> </bean> <!-- ===================== HTTP CHANNEL REQUIREMENTS ==================== --> <!-- You will need to uncomment the "Acegi Channel Processing Filter" <filter-mapping> in web.xml for the following beans to be used --> <bean id="channelProcessingFilter" class="org.acegisecurity.securechannel.ChannelProcessingFilter"> <property name="channelDecisionManager"><ref local="channelDecisionManager"/></property> <property name="filterInvocationDefinitionSource"> <value><![CDATA[ CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON \A/secure/.*\Z=REQUIRES_SECURE_CHANNEL \A/acegilogin.jsp.*\Z=REQUIRES_SECURE_CHANNEL \A/j_acegi_security_check.*\Z=REQUIRES_SECURE_CHANNEL \A.*\Z=REQUIRES_INSECURE_CHANNEL ]]></value> </property> </bean> <bean id="channelDecisionManager" class="org.acegisecurity.securechannel.ChannelDecisionManagerImpl"> <property name="channelProcessors"> <list> <ref local="secureChannelProcessor"/> <ref local="insecureChannelProcessor"/> </list> </property> </bean> <bean id="secureChannelProcessor" class="org.acegisecurity.securechannel.SecureChannelProcessor"/> <bean id="insecureChannelProcessor" class="org.acegisecurity.securechannel.InsecureChannelProcessor"/> <!-- ===================== HTTP REQUEST SECURITY ==================== --> <!-- Modified to work with JOSSO --> <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter"> <property name="authenticationEntryPoint"><ref local="jossoProcessingFilterEntryPoint"/></property> </bean> <bean id="httpRequestAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased"> <property name="allowIfAllAbstainDecisions"><value>false</value></property> <property name="decisionVoters"> <list> <bean class="org.acegisecurity.vote.RoleVoter"/> <bean class="org.acegisecurity.vote.AuthenticatedVoter"/> </list> </property> </bean> <!-- Note the order that entries are placed against the objectDefinitionSource is critical. The FilterSecurityInterceptor will work from the top of the list down to the FIRST pattern that matches the request URL. Accordingly, you should place MOST SPECIFIC (ie a/b/c/d.*) expressions first, with LEAST SPECIFIC (ie a/.*) expressions last --> <bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"> <property name="authenticationManager"><ref bean="authenticationManager"/></property> <property name="accessDecisionManager"><ref local="httpRequestAccessDecisionManager"/></property> <property name="objectDefinitionSource"> <value><![CDATA[ CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT /index.jsp=ROLE_ANONYMOUS,ROLE_USER /hello.htm=ROLE_ANONYMOUS,ROLE_USER /logoff.jsp=ROLE_ANONYMOUS,ROLE_USER /switchuser.jsp=ROLE_SUPERVISOR /j_acegi_switch_user=ROLE_SUPERVISOR /acegilogin.jsp*=ROLE_ANONYMOUS,ROLE_USER /**=ROLE_USER ]]></value> </property> </bean> </beans>
SSO Gateway Endpoint
| This is only required if the web application is not on the same server as the JOSSO Gateway. |
Edit the applicationContext-acegi-security-JOSSO.xml file and configure the jossoGatewayServiceLocator bean, setting the endpoint property to the specific SSO Gateway location, for example 192.168.1.1:8080 or myGateway:8080
<!--
A different gateway endpoint must be used if the if JOSSO gateway is not running in the same host as this application
-->
<bean id="jossoGatewayServiceLocator" class="org.josso.gateway.WebserviceGatewayServiceLocator">
<property name="endpoint"><value>localhost:8080</value></property>
<!--property name="endpoint"><value>josso-gwy-server:8080</value></property-->
</bean>
Modify your application descriptor
Now we need to let Spring know about the newly created application context descriptor. Since this is a Spring web application, the application context is referenced from the web.xml file. Consider that this guide is based on the contacts sample, therefore other descriptors defining business specific beans could be included as well.
Let's take a look at the updated web.xml file.
... <web-app> <!-- - Location of the XML file that defines the root application context - Applied by ContextLoaderListener. --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/applicationContext-acegi-security-JOSSO.xml classpath:applicationContext-common-business.xml classpath:applicationContext-common-authorization.xml </param-value> </context-param> ... </web-app>
Add the new partner application
The final setup step is to expose the Acegi web application as a JOSSO Partner applications. In order to do this we need to configure the JOSSO Agent by adding a new entry to the josso-agent-config.xml file. Locate the partner-apps section and add a new partner application for the /contacts web context :
<agent> ... <partner-apps> <partner-app> <context>/contacts</context> </partner-app> <partner-app> <context>/partnerapp</context> </partner-app> </partner-apps> </agent>
Deploy
Finally, we need to build and deploy the JOSSO-enabled Acegi web application.
Once the application is up and running, authentication requests will occur based on the enforcement of the active Acegi authorization policies. Consider that, while the user is bound to an active SSO session, his identity can be leveraged by other web applications as well, which do not have to necessarily be based on Acegi (e.g. J2EE applications).