At line 1 changed 1 line. |
[AppFuse] has used [Container-Managed Authentication|http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/Security4.html#67530] since it was first created. However, in version 1.8, this was replaced with the [Acegi Security Framework for Spring|http://acegisecurity.sf.net]. The main reasons for this can be found on [raibledesigns.com|http://raibledesigns.com/page/rd?anchor=re_j2ee_app_server_security]. |
This page is a starting point for [AppFuse] related security issues, tutorials and how to's. As of 1.8, AppFuse uses [Acegi Security|http://acegisecurity.sourceforge.net] for authentication and authorization. In 1.7 and prior, container-managed authentication (CMA) was used. You can revert to CMA if you prefer. |
At line 3 changed 172 lines. |
The purpose of this page is to describe what I did to integrate Acegi Security into 1.8. Hopefull 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. |
|
%%note __NOTE:__ This tutorial is ''in progress'' - please don't use it until this message is gone.%% |
|
!Table of Contents |
* [1] Add Acegi Security JARs to your project |
* [2] Create applicationContext-security.xml |
* [3] Configure filters and filter-mappings |
* [4] Remove web-security.xml from metadata/web |
* [5] Add an "enabled" variable to the User object |
* [6] Configure logging for Acegi Security |
* [7] Remove setting from LoginServlet.java to prevent duplicate logins |
|
!!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. The Acegi Security JAR listed below contains a [patch|http://forum.springframework.org/viewtopic.php?p=10396#10396] that allows you to easily exclude URLs. Before the 1.8 release, I hope to fix this so you don't need a patched version. |
|
* [acegi-security-0.7-SNAPSHOT.jar|https://appfuse.dev.java.net/source/browse/appfuse/lib/spring-1.1.3/acegi-security-0.7-SNAPSHOT.jar?rev=1.1] |
* [commons-codec.jar|https://appfuse.dev.java.net/source/browse/appfuse/lib/spring-1.1.3/commons-codec.jar?rev=1.1] |
|
!!Create applicationContext-security.xml [#2] |
Download [applicationContext-security.xml|https://appfuse.dev.java.net/source/browse/appfuse/web/WEB-INF/applicationContext-security.xml?rev=1.1] 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 filters to the top of metadata/web/filter.xml: |
|
{{{ |
<filter> |
<filter-name>Acegi Authentication Processing Filter</filter-name> |
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class> |
<init-param> |
<param-name>targetClass</param-name> |
<param-value>net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter</param-value> |
</init-param> |
</filter> |
<filter> |
<filter-name>acegiSessionFilter</filter-name> |
<filter-class>net.sf.acegisecurity.ui.webapp.HttpSessionIntegrationFilter</filter-class> |
</filter> |
<filter> |
<filter-name>Acegi HTTP Request Security Filter</filter-name> |
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class> |
<init-param> |
<param-name>targetClass</param-name> |
<param-value>net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter</param-value> |
</init-param> |
</filter> |
<filter> |
<filter-name>acegiRemoteUserFilter</filter-name> |
<filter-class>net.sf.acegisecurity.ui.wrapper.ContextHolderAwareRequestFilter</filter-class> |
</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>Acegi Authentication Processing Filter</filter-name> |
<url-pattern>/j_security_check</url-pattern> |
<!--dispatcher>REQUEST</dispatcher> |
<dispatcher>FORWARD</dispatcher--> |
</filter-mapping> |
<filter-mapping> |
<filter-name>acegiSessionFilter</filter-name> |
<url-pattern>/*</url-pattern> |
<!--dispatcher>REQUEST</dispatcher> |
<dispatcher>FORWARD</dispatcher--> |
</filter-mapping> |
<filter-mapping> |
<filter-name>Acegi HTTP Request Security Filter</filter-name> |
<url-pattern>*.html</url-pattern> |
<!--dispatcher>REQUEST</dispatcher> |
<dispatcher>FORWARD</dispatcher--> |
</filter-mapping> |
<filter-mapping> |
<filter-name>acegiRemoteUserFilter</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] |
In order to hook into Acegi Security without changing much code, I did have to add a "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. |
|
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. |
|
[{Java2HtmlPlugin |
|
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; |
} |
}] |
|
%%note 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.%% |
|
2. In your controller/page class that handles signup, enable the user before they're saved: |
|
[{Java2HtmlPlugin |
|
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. |
|
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. |
|
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=使能 |
|
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. |
|
* [Struts|https://appfuse.dev.java.net/source/browse/appfuse/web/pages/userForm.jsp?r1=1.12&r2=1.13] |
* [Spring MVC|] |
* [WebWork|] |
* [Tapestry|] |
* [JSF|] |
|
!!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 |
}}} |
|
!!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. |
|
[{Java2HtmlPlugin |
|
// 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; |
} |
}] |
|
* How to migrate your pre-AppFuse 1.8 application to [use Acegi Security for authentication|AppFuseAuthentication]. |
* How to use Acegi Security for [securing methods by role|AppFuseSecurityMethods]. |
* How to use Acegi Security to [control access to objects with ACLs|AppFuseAcegiACL]. |