Documentation for Crowd 2.2. Documentation for other versions of Crowd is available too.
AppFuse provides a sweet starting point for developing web applications. You choose the frameworks, AppFuse generates the skeleton application.
At its core, the web security of AppFuse 2.0.1 and earlier applications relies on the modular and extensible Acegi authentication framework. In this tutorial, we look at a basic integration of Crowd with Acegi, using an application generated by AppFuse.
If you're working with AppFuse 2.0.2 or later, it uses Spring Security instead of Acegi. Please see our separate tutorial.
This tutorial assumes you have installed Crowd 1.5.1 or later.
In this tutorial, we will be using the Struts2-basic archetype to create the project, but the other types should be similar. For more information, consult the AppFuse quickstart guide. In particular, it outlines the database requirements for AppFuse.
mvn archetype:create -DarchetypeGroupId=org.appfuse.archetypes \ -DarchetypeArtifactId=appfuse-basic-struts \ -DremoteRepositories=http://static.appfuse.org/releases -DarchetypeVersion=2.0 \ -DgroupId=com.mycompany.app -DartifactId=myproject
cd myproject mvn appfuse:full-source
mvn clean install
mvn jetty:run-war -Dmaven.test.skip
http://localhost:8080/
ctrl+c
Add appfuse
as an application via the Crowd Console. See Adding an Application for more information.
Open up the pom.xml
and add the Crowd client libraries as a project dependency:
<dependencies> <dependency> <groupId>com.atlassian.crowd</groupId> <artifactId>crowd-integration-client</artifactId> <version>1.5.1</version> </dependency> ... </dependencies>
You will also need to create the file myproject/src/main/resources/crowd.properties
:
application.name appfuse application.password password application.login.url http://localhost:8095/crowd/ crowd.server.url http://localhost:8095/crowd/services/ session.isauthenticated session.isauthenticated session.tokenkey session.tokenkey session.validationinterval 0 session.lastvalidation session.lastvalidation
In particular, the application name and password must match the values defined for the application added in Step 2.
Finally, copy the STANDALONE/client/conf/crowd-ehcache.xml
to myproject/src/main/resources/crowd-ehcache.xml
. This file defines the cache properties, such as cache timeouts, used when accessing data from the Crowd server.
Before modifying the security configuration, you will need to add the Spring configuration file to wire up the Crowd client beans. Add the applicationContext-CrowdClient.xml
configuration file to the list of contextConfigLocations
in WEB-INF/web.xml
:
<context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:/applicationContext-resources.xml classpath:/applicationContext-dao.xml classpath:/applicationContext-service.xml classpath*:/applicationContext.xml classpath:/applicationContext-CrowdClient.xml /WEB-INF/applicationContext*.xml /WEB-INF/xfire-servlet.xml /WEB-INF/security.xml </param-value> </context-param>
AppFuse neatly stores all the Acegi configuration in myproject/src/main/webapp/WEB-INF/security.xml
. In order to get centralised authentication, we will need to set up Acegi to use the wrapped authenticator class we just created. Edit the Acegi beans in security.xml
:
<bean id="crowdUserDetailsService" class="com.atlassian.crowd.integration.acegi.user.CrowdUserDetailsServiceImpl"> <property name="authenticationManager" ref="crowdAuthenticationManager"/> <property name="groupMembershipManager" ref="crowdGroupMembershipManager"/> <property name="userManager" ref="crowdUserManager"/> <property name="authorityPrefix" value="ROLE_"/> </bean>
<bean id="crowdAuthenticationProvider" class="com.atlassian.crowd.integration.acegi.RemoteCrowdAuthenticationProvider"> <constructor-arg ref="crowdAuthenticationManager"/> <constructor-arg ref="httpAuthenticator"/> <constructor-arg ref="crowdUserDetailsService"/> </bean>
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"> <property name="providers"> <list> <ref local="crowdAuthenticationProvider"/> <!--ref local="daoAuthenticationProvider"--> <ref local="anonymousAuthenticationProvider"/> <ref local="rememberMeAuthenticationProvider"/> </list> </property> </bean>
mvn jetty:run-war -Dmaven.test.skip
http://localhost:8080/
.USER
and ADMIN
. You will need to add these groups and assign the user as a member of the groups. These Crowd group names map to the Acegi authorisation roles defined in the AppFuse application.Congratulations. You have centralised authentication
Application-level centralised user management
One quirk you may notice is that you can't view the profile details of users who exist in Crowd, but did not exist in AppFuse prior to the Crowd integration. Although it's possible to authenticate a Crowd user 'dude' and still run AppFuse as 'dude', 'dude' will not be in AppFuse's local database. AppFuse makes use of a database-backed user management system. In order to achieve application-level centralised user management, AppFuse will need to delegate its calls to create, retrieve, update and delete users to Crowd via Crowd's remote API. This will prevent data redundancy and eliminate the hassle of data synchronisation. This is beyond the scope of this short tutorial.
Enabling single sign-on (SSO) requires a little more tweaking of the security.xml
:
<bean id="authenticationProcessingFilter" class="com.atlassian.crowd.integration.acegi.CrowdSSOAuthenticationProcessingFilter"> <property name="httpAuthenticator" ref="httpAuthenticator"/> <property name="authenticationManager" ref="authenticationManager"/> <property name="authenticationFailureUrl" value="/login.jsp?error=true"/> <property name="defaultTargetUrl" value="/"/> <property name="filterProcessesUrl" value="/j_security_check"/> <property name="rememberMeServices" ref="rememberMeServices"/> </bean>
<bean id="crowdLogoutHandler" class="com.atlassian.crowd.integration.acegi.CrowdLogoutHandler"> <property name="httpAuthenticator" ref="httpAuthenticator"/> </bean>
<bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter"> <constructor-arg value="/index.jsp"/> <constructor-arg> <list> <ref bean="rememberMeServices"/> <ref bean="crowdLogoutHandler"/> <bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/> </list> </constructor-arg> <property name="filterProcessesUrl" value="/logout.jsp"/> </bean>
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy"> <property name="filterInvocationDefinitionSource"> <value> ... /**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor </value> ...
mvn jetty:run-war -Dmaven.test.skip=true
SSO will only work for users that are able to authenticate with both appplications and are authorised to use both applications. Try out the following:
Congratulations, you have SSO