Raible's Wiki

Raible Designs
Wiki Home
News
Recent Changes

AppFuse

Homepage
  - Korean
  - Chinese
  - Italian
  - Japanese

QuickStart Guide
  - Chinese
  - French
  - German
  - Italian
  - Korean
  - Portuguese
  - Spanish
  - Japanese

User Guide
  - Korean
  - Chinese

Tutorials
  - Chinese
  - German
  - Italian
  - Korean
  - Portuguese
  - Spanish

FAQ
  - Korean

Latest Downloads

Other Applications

Struts Resume
Security Example
Struts Menu

Set your name in
UserPreferences

Edit this page


Referenced by
AppFuseSecurity
AppFuseSecurityMetho...
AppFuseSecuritySetti...




JSPWiki v2.2.33

[RSS]


Hide Menu

AppFuseAuthentication


AppFuse has used Container-Managed Authentication since it was first created. However, in version 1.8, this was replaced with the Acegi Security Framework for Spring. The main reasons for this can be found on raibledesigns.com.

The purpose of this page is to describe what we did to integrate Acegi Security into 1.8. Hopefully you can use this to change your pre-1.8 applications to Acegi Security if you like. The nice thing about migrating to Acegi Security is your application becomes more portable and you don't have to worry about configuring your application server. Also, there weren't many code changes involved in the integration process - which proves Acegi Security works very well with all the existing code that talks to the security methods of the Servlet API (i.e. request.isUserInRole(), etc.)

Part II of this tutorial shows you how to remove Acegi Security from AppFuse and revert back to Container-Managed Authentication (CMA). However, this might not be a necessary step if you want to use CMA because Acegi Security has a number of Container Adapters available.

WARNING: It's likely that AppFuse will use more of Acegi Security features (i.e. Remember Me and Password Encryption) in 1.9+. Therefore, the instructions in Part II only apply to AppFuse 1.8.

Table of Contents - Part I

  • [1.1] Add Acegi Security JARs to your project
  • [1.2] Create applicationContext-security.xml
  • [1.3] Configure filter and its filter-mapping
  • [1.4] Remove web-security.xml from metadata/web
  • [1.5] Add an "enabled" variable to the User object (optional)
  • [1.6] Configure logging for Acegi Security
  • [1.7] Remove setting from LoginServlet.java to prevent duplicate logins
  • [1.8] Add code to logout.jsp so logout succeeds

Add Acegi Security JARs to your project [#1]

Download the following two JARs and put them in the lib/spring-1.1.3 directory of your project. If you're on an older version of Spring, that shouldn't matter.

Create applicationContext-security.xml [#2]

Download applicationContext-security.xml and put it in your web/WEB-INF directory. Make sure your metadata/web/web-settings.xml file loads this file as part of the ContextLoaderListener. For AppFuse 1.7+, this should look as follows:
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext-*.xml</param-value>
    </context-param>

Configure filters and filter-mappings [#3]

Add Acegi Security's filter to the top of metadata/web/filters.xml:
    <filter>
        <filter-name>securityFilter</filter-name>
        <filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
        <init-param>
            <param-name>targetClass</param-name>
            <param-value>net.sf.acegisecurity.util.FilterChainProxy</param-value>
        </init-param>
    </filter>

Add filter-mappings for each of these filters. Put the following XML at the top of the metadata/web/filter-mappings.xml file:

    <filter-mapping>
        <filter-name>securityFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

Remove web-security.xml from metadata/web [#4]

Delete the file metadata/web/web-security.xml. This is no longer needed since the security settings are now controlled by the applicationContext-security.xml file.

Add an "enabled" variable to the User object [#5]

NOTE: If you don't care about enabling/disabling users, you can skip this section and just change the "usersByUsernameQuery" property in applicationContext-security.xml to be:
SELECT username,password,enabled as 'true' FROM app_user WHERE username = ?

One nice feature in Acegi Security is the ability to enable/disable users. In order to use this feature, you'll have to add an "enabled" property to the User object. This affects several areas of an AppFuse application. You will need to modify your code in the following areas to handle this.

Step 1. Add a "enabled" variable to the User.java class, including getters and setters. Also add this variable to the equals(), hashCode() and toString() methods.


    protected Boolean enabled;

    /**
     @return Returns the enabled.

     * @hibernate.property column="enabled"
     */
    public Boolean getEnabled() {
        return enabled;
    }

    /**
     @param enabled The enabled to set.
     */
    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }

If you're using iBATIS, you will need to add the "enabled" column to the SQL in UserSQL.xml. You will also need to add "enabled bit" as a column definition for the "app_user" table in metadata/sql/mysql-create-tables.sql.

Step 2. In your controller/page class that handles signup, enable the user before they're saved (Spring example diff):


user.setEnabled(Boolean.TRUE);

Another thing you could do is leave them disabled and change the e-mail that's sent go to an administrator. Then the administrator can enable them and send an e-mail. The above behavior is to keep consistency with current AppFuse functionality.

Step 3. In metadata/sql/sample-data.xml, add an "enabled" <column> to the "app_user" table. This should have a <value> of "1" for all users - so they're enabled and can login.

Step 4. For all the languages/ResourceBundles your application supports, you need to add a label key to display this property on the UI. Below are the translations for the default ResourceBundles. If you're using Struts, this key should be "userForm.enabled" instead of "user.enabled".

  • ApplicationResources_en.properties: user.enabled=Enabled
  • ApplicationResources_es.properties: user.enabled=Permitido
  • ApplicationResources_fr.properties: user.enabled=Permis
  • ApplicationResources_nl.properties: user.enabled=Toegelaten
  • ApplicationResources_pt_BR.properties: user.enabled=Permitido
  • ApplicationResources_zh_CN.properties: user.enabled=使能
NOTE: I translated "enabled" with Google, so please let me know if there's a more accurate translation.

Step 5. When editing a user's record, an administrator should be able to enabled/disable a user. When a user is editing their own profile, the disabled field should be hidden, so they cannot edit it. Below are links to the CVS diffs for the various frameworks to show you what you need to change.

Configure logging for Acegi Security [#6]

In web/WEB-INF/classes/log4j.properties, add a setting to control the logging for Acegi Security.
log4j.logger.net.sf.acegisecurity=WARN
log4j.logger.net.sf.acegisecurity.intercept.event.LoggerListener=WARN

Remove setting from LoginServlet.java to prevent duplicate logins [#7]

In LoginServet.java, remove the initial if statement at the beginning of the execute method. This involves deleting the following lines of code.


    // if user is already authenticated, it means they probably bookmarked
    // or typed in the URL to login.jsp directly, route them to the main
    // menu is this is the case
    if (request.getRemoteUser() != null) {
        if (log.isDebugEnabled()) {
            log.debug("User '" + request.getRemoteUser() +
                      "' already logged in, routing to mainMenu");
        }

        response.sendRedirect(request.getContextPath() "/mainMenu.html");

        return;
    }

Add code to logout.jsp sp logout succeeds [#8]

In web/logout.jsp, add code to invalidate the session (before the redirect). Invalidating the session in LoginFilter.java should work, but it doesn't.
<% session.invalidate(); %>

<c:redirect url="/mainMenu.html"/>

Also, Acegi can print out more informative error messages when login fails. To see these messages, add the following to web/pages/loginForm.jsp - right after the password.mismatch message.

<c:out value="${sessionScope.ACEGI_SECURITY_LAST_EXCEPTION.message}"/>

 


Part II - Removing Acegi and reverting to CMA

Use the instructions below to change your AppFuse 1.8 application to use CMA. We don't expect many folks will need to do this, but more documentation is better than less. ;-)

Table of Contents - Part II

  • [2.1] Remove Acegi Security files from your project
  • [2.2] Remove filter and its filter-mapping
  • [2.3] Add web-security.xml to metadata/web
  • [2.4] Add logic to detect disabled users (optional)
  • [2.5] Remove Acegi Security's logging settings (optional)
  • [2.6] Add code to LoginServlet.java to prevent duplicate logins

Remove Acegi Security files from your project [#20]

The first step in reverting back to CMA is removing Acegi-specific files. Delete acegi-security-*.jar and commons-codec.jar from lib/spring-* and applicationContext-security.xml from web/WEB-INF.

Remove securityFilter and its filter-mapping [#21]

Modify metadata/web/filters.xml to remove the "securityFilter" definition and its associated mapping from metadata/web/filter-mappings.xml.

Add web-security.xml to metadata/web [#23]

Download the old web-security.xml file from AppFuse CVS's Attic and put it in your metadata/web directory.

Add logic to detect disabled users (optional) [#24]

As part of integrating Acegi Security, an option to disable users was added to the User Profile screen. If you'd like to honor this feature in your application after removing Acegi Security, you can do so by modifying ActionFilter.java in the src/web/org/appfuse/webapp/filter directory.


        UserManager mgr = (UserManagerctx.getBean("userManager");
        user = mgr.getUser(username);
            
        if (user.getEnabled() == Boolean.FALSE) {
            request.getSession().invalidate();
            response.sendRedirect(request.getContextPath() "/loginError.jsp");
            return;
        }
            
        session.setAttribute(Constants.USER_KEY, user);

Remove Acegi Security's logging settings (optional) [#25]

The following lines can be removed from web/WEB-INF/classes/log4j.properties since they'll no longer be relevant. However, leaving them in won't hurt anything.

log4j.logger.net.sf.acegisecurity=WARN
log4j.logger.net.sf.acegisecurity.intercept.event.LoggerListener=WARN

Add code to LoginServlet.java to prevent duplicate logins [#26]

In LoginServet.java, add the following if statement at the beginning of the execute method. This prevents duplicate logins that can occur with CMA.


    // if user is already authenticated, it means they probably bookmarked
    // or typed in the URL to login.jsp directly, route them to the main
    // menu is this is the case
    if (request.getRemoteUser() != null) {
        if (log.isDebugEnabled()) {
            log.debug("User '" + request.getRemoteUser() +
                      "' already logged in, routing to mainMenu");
        }

        response.sendRedirect(request.getContextPath() "/mainMenu.html");

        return;
    }

Issues or problems with these instructions? If so, please send your questions to users-AT-appfuse.dev.java.net.



Go to top   Edit this page   More info...   Attach file...
This page last changed on 06-Nov-2006 13:52:59 MST by MattRaible.