Integrating AppFuse - a Crowd-Spring Security Integration Tutorial

On this page

Still need help?

The Atlassian Community is here for you.

Ask the community

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.2+ applications relies on the modular and extensible Spring Security authentication framework. In this tutorial, we look at a basic integration of Crowd with Spring Security, using an application generated by AppFuse.

Spring Security was formerly known as Acegi

  • The Acegi security framework changed its name to Spring Security with its 2.0 release.
  • Appfuse 2.0.2 changed from Acegi to Spring Security for authentication. Earlier versions of Appfuse use Acegi.
  • If you are working with Acegi in an earlier version of Appfuse, we have a separate tutorial.
  • Crowd 1.6 and above provide support for both Spring Security and Acegi. Earlier versions of Crowd only supported Acegi.
  • We recommend all new projects use Spring Security as it is being actively maintained.

Prerequisites

This tutorial assumes you have installed Crowd 1.6 or later and are using Appfuse 2.0.2 or later.

Step 1. Get AppFuse

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.

  1. Create the project.

    mvn archetype:create -DarchetypeGroupId=org.appfuse.archetypes \
    -DarchetypeArtifactId=appfuse-basic-struts \
    -DremoteRepositories=http://static.appfuse.org/releases -DarchetypeVersion=2.0.2 \
    -DgroupId=com.mycompany.app -DartifactId=myproject
    
  2. Since we will be editing the core Spring Security configuration, we will need the full source code of the application.

    cd myproject
    mvn appfuse:full-source
    
  3. Build it.

    mvn clean install
    
  4. Run it.

    mvn jetty:run-war -Dmaven.test.skip
    
  5. Play with it.

    http://localhost:8080/
    
  6. Shut it down.

    ctrl+c
    

Step 2. Let Crowd Know about AppFuse

Add appfuse as an application via the Crowd Console. See Adding an Application for more information.

Step 3. Add the Crowd Spring Security Connector to AppFuse

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.6</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.

Step 4. Hook Up Centralized Authentication

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 myproject/src/main/webapp/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 Spring Security configuration in myproject/src/main/webapp/WEB-INF/security.xml. In order to get centralized authentication, we will need to set up Spring Security to use Crowd components for user information. Edit the beans in security.xml:

  1. Add the definition of the CrowdUserDetailsService:

    <beans:bean id="crowdUserDetailsService" class="com.atlassian.crowd.integration.springsecurity.user.CrowdUserDetailsServiceImpl">
            <beans:property name="authenticationManager" ref="crowdAuthenticationManager"/>
            <beans:property name="groupMembershipManager" ref="crowdGroupMembershipManager"/>
            <beans:property name="userManager" ref="crowdUserManager"/>
            <beans:property name="authorityPrefix" value="ROLE_"/>
    </beans:bean>
    
  2. Add the definition of the RemoteCrowdAuthenticationProvider that delegates Spring Security authentication requests to Crowd:

    <beans:bean id="crowdAuthenticationProvider" class="com.atlassian.crowd.integration.springsecurity.RemoteCrowdAuthenticationProvider">
    	<custom-authentication-provider />
    	<beans:constructor-arg ref="crowdAuthenticationManager"/>
            <beans:constructor-arg ref="httpAuthenticator"/>
            <beans:constructor-arg ref="crowdUserDetailsService"/>
    </beans:bean>
    
  3. Comment out the default authentication provider, as we've replaced it with Crowd:

    <!--
        <authentication-provider user-service-ref="userDao">
            <password-encoder ref="passwordEncoder"/>
        </authentication-provider>
    -->
    
  4. Now do a:

    mvn clean install
    

    This will pick up the configuration changes and add the Crowd client library into your app. Then run:

    mvn jetty:run-war -Dmaven.test.skip
    
  5. Head over to http://localhost:8080/.
    You should now be able to authenticate the users in your Crowd repository that meet all of the following conditions:
  • They are in a Crowd directory assigned to the AppFuse application in Crowd. See more information.
  • They are in Crowd groups named 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 Spring Security authorization roles defined in the AppFuse application.
  • They are allowed to authenticate with the AppFuse application because EITHER they are in a group allowed to authenticate with Crowd (click for details) OR their container directory allows all users to authenticate (click for details).

Congratulations. You have centralized authentication (smile)

Application-level centralized 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 centralized user management, AppFuse will need to delegate its calls to create, retrieve, update and delete users to Crowd using Crowd's remote API. This will prevent data redundancy and eliminate the hassle of data synchronization. This is beyond the scope of this short tutorial.

Step 5. Hook Up Single Sign-On

Enabling single sign-on (SSO) requires quite a bit more tweaking of the security.xml:

  1. Remove defaults from the <http/> element:
    1. Remove the auto-config attribute and add an entry-point-ref="crowdAuthenticationProcessingFilterEntryPoint" attribute to the http element.
    2. Remove the <form-login> element.
      You should end up with an http element similar to this:

      <http lowercase-comparisons="false" entry-point-ref="crowdAuthenticationProcessingFilterEntryPoint"> <!-- note: no auto-config attribute! -->
          <!--intercept-url pattern="/images/*" filters="none"/>
          <intercept-url pattern="/styles/*" filters="none"/>
          <intercept-url pattern="/scripts/*" filters="none"/-->
          <intercept-url pattern="/admin/*" access="ROLE_ADMIN"/>
          <intercept-url pattern="/passwordHint.html*" access="ROLE_ANONYMOUS,ROLE_ADMIN,ROLE_USER"/>
          <intercept-url pattern="/signup.html*" access="ROLE_ANONYMOUS,ROLE_ADMIN,ROLE_USER"/>
          <intercept-url pattern="/a4j.res/*.html*" access="ROLE_ANONYMOUS,ROLE_ADMIN,ROLE_USER"/>
          <!-- APF-737, OK to remove line below if you're not using JSF -->
          <intercept-url pattern="/**/*.html*" access="ROLE_ADMIN,ROLE_USER"/>
      <!--  <form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?error=true" login-processing-url="/j_security_check"/> -->
          <remember-me user-service-ref="userDao" key="e37f4b31-0c45-11dd-bd0b-0800200c9a66"/>
      </http>
      
  2. Change the default processing filter to Crowd's SSO filter by adding the following bean definitions:

    <authentication-manager alias="authenticationManager"/>
     
    <beans:bean id="crowdAuthenticationProcessingFilterEntryPoint" class="org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint">
    	<beans:property name="loginFormUrl" value="/login.jsp"/>
    </beans:bean>
    	
    <beans:bean id="crowdAuthenticationProcessingFilter" class="com.atlassian.crowd.integration.springsecurity.CrowdSSOAuthenticationProcessingFilter">
        <custom-filter position="AUTHENTICATION_PROCESSING_FILTER"/>       
        <beans:property name="httpAuthenticator" ref="httpAuthenticator"/>
        <beans:property name="authenticationManager" ref="authenticationManager"/>
        <beans:property name="authenticationFailureUrl" value="/login.jsp?error=true"/>
        <beans:property name="defaultTargetUrl" value="/"/>
        <beans:property name="filterProcessesUrl" value="/j_security_check"/>
    </beans:bean>
  3. Add the definition of the CrowdLogoutHandler and add in a LogoutFilter that references it:

    <beans:bean id="crowdLogoutHandler" class="com.atlassian.crowd.integration.springsecurity.CrowdLogoutHandler">
        <beans:property name="httpAuthenticator" ref="httpAuthenticator"/>
    </beans:bean>
    
    <beans:bean id="logoutFilter" class="org.springframework.security.ui.logout.LogoutFilter">
        <custom-filter position="LOGOUT_FILTER"/>
        <beans:constructor-arg value="/index.jsp"/>
        <beans:constructor-arg>
            <beans:list>
                <beans:ref bean="crowdLogoutHandler"/>
                <beans:bean class="org.springframework.security.ui.logout.SecurityContextLogoutHandler"/>
            </beans:list>
        </beans:constructor-arg>
        <beans:property name="filterProcessesUrl" value="/logout.jsp"/>
    </beans:bean>
    
  4. Now repeat:

    mvn jetty:run-war -Dmaven.test.skip=true
    

SSO will only work for users that are able to authenticate with both appplications and are authorized to use both applications. Try out the following:

  • Log in to Crowd – you should be logged in to AppFuse.
  • Log out of AppFuse – you should be logged out of Crowd.
  • Log in to AppFuse; log out of Crowd; log in to Crowd as another user; refresh AppFuse – you should be logged in as the new user.

Congratulations, you have SSO (smile)

Last modified on Sep 20, 2017

Was this helpful?

Yes
No
Provide feedback about this article
Powered by Confluence and Scroll Viewport.