SourceForge.net Logo

Integrating JBoss Portal with Acegi Security Framework

 

Our solution applies to JBoss portal, too. As it is stated before, with a small effort, Acegi Security Framework can be easily integrated with any JSR-168 compatible portal. Let’s walk over the steps that are needed for JBoss Portal 2.6.1.

JBoss Authentication and Creating a Valid Acegi Authentication Token

 

 

JBoss Portal uses web container security to perform its authentication. It uses form based authentication. You can configure LDAP as authentication backend and as user realm. Acegi Security Framework provides container adapters to delegate its authentication to web containers. However, this approach has several drawbacks, including necessity for copying acegi-security.jar and several of its dependents into common library folders in container.

 

 

In our solution we let JBoss perform its own authentication using web container security. It will use LDAP as authentication backend. After a user authenticated, JBoss places a valid Subject entity into JNDI with name “env/security/subject”.

 

We will look up JNDI for the Subject, create a valid Acegi Authentication using that Subject entity, and place that Authentication token into SecurityContextHolder.

 

Configuring JBoss Portal to use LDAP

 

Steps to configure LDAP are as follows.

 

Please be careful that, LDAP entry which corresponds to admin role must have cn=”Admin” value. First letter “A” must be upper case here.

Configuring Acegi Security in JBoss Portal-Server Web Application

 

This part is actually optional. It is not necessary to propagate SecurityContext to portlet web applications. You may need this part when you need to configure Acegi in portal web application of JBoss.

 

In order to run Acegi Security Framework in portal-server web application, we first need to define Spring’s ContextLoaderListener to create application context during portal startup. Just add following part into WEB-INF/web.xml file.

 

<context-param>

      <param-name>contextConfigLocation</param-name>

      <param-value>

            classpath:/appcontext/spring-beans.acegi-portlet-jboss.xml

      </param-value>

</context-param>

 

<listener>

      <listener-class>

org.springframework.web.context.ContextLoaderListener

</listener-class>

</listener>

 

After that configuration, we need to add filter definition into web.xml. And add its filter mapping as the first mapping in the web.xml file.

 

      <filter>

            <filter-name>AcegiSecurityFilter</filter-name>

            <filter-class>

org.acegisecurity.util.FilterToBeanProxy

</filter-class>

            <init-param>

                  <param-name>targetBean</param-name>

                  <param-value>filterChainProxy</param-value>

            </init-param>

      </filter>

 

<filter-mapping>

            <filter-name>AcegiSecurityFilter</filter-name>

            <url-pattern>/*</url-pattern>

</filter-mapping>

 

 

FilterToBeanProxy just delegates calls to targetBean defined in ApplicationContext. By that way we are able to define Filter instances as Spring managed beans.

 

Let’s look at spring-beans.acegi-portlet-jboss.xml file. I have developed org.acegisecurity.portlet.jboss.auth.JbossIntegrationFilter to query JNDI for a valid Subject entity, and use it to form a valid Acegi Authentication token. By that way, Acegi doesn’t involve in authentication process at first place, and only waits for a valid Subject to be placed into JNDI tree.

 

      <bean id="filterChainProxy"

            class="org.acegisecurity.util.FilterChainProxy">

            <property name="filterInvocationDefinitionSource">

                  <value>

                        CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON

                        PATTERN_TYPE_APACHE_ANT

                        /**=jbossIntegrationFilter,anonymousProcessingFilter

                  </value>

            </property>

      </bean>

     

      <bean id="jbossIntegrationFilter"

            class="org.acegisecurity.portlet.jboss.auth.JbossIntegrationFilter">

            <property name="securityContextPopulator">

                  <ref local=" securityContextPopulator"/>

            </property>

      </bean>

     

      <bean id="securityContextPopulator" class="org.acegisecurity.portlet.jboss.context.SecurityContextPopulator">

            <property name="subjectToAuthenticationTransformer">

                  <ref local="subjectToAuthenticationTransformer"/>

            </property>

      </bean>

     

<bean id="subjectToAuthenticationTransformer"  class="org.acegisecurity.portlet.jboss.context.SubjectToAuthenticationTransformer">

</bean>

 

As you see there are only two filters configured in filterChainProxy. First is jbossIntegrationFilter, and the second is anonymousProcessingFilter. jbossIntegrationFilter uses securityContextPopulator and subjectToAuthenticationTransformer beans to perform its job. If there isn’t any valid Authentication token placed into SecurityContextHolder, anonymousProcessingFilter puts an AnonymousAuthentication token into it.

 

Add following jars into WEB-INF/lib folder.

 

acegi-portlet.jar

acegi-security.jar

cglib-nodep.jar

commons-codec.jar

commons-digester.jar

commons-discovery.jar

commons-lang.jar

commons-logging.jar

jakarta-oro.jar

log4j.jar

spring.jar

 

Finally, don’t forget to add those JVM parameters -Dtarget.platform=dev -Duser.language=en -Duser.country=US appropriate point in bin\run.bat file.

Propagating Acegi SecurityContext from JBoss Portal to Portlet Web Applications

 

JBoss portal uses PortletContainerImpl class to wrap any deployed portlet instance and invokes it later using that PortletContainer instance. If we are able to intercept just before this portlet invocation, obtain a valid Authentication using JNDI registered Subject, and then populate SecurityContextHolder with that Authentication object.

 

The important point here is class loading issues. We dont want to copy acegi-security and any of its related jars into shared folders of JBoss. Each portlet web application, and portal application (portal-server.war) itself should posses their copies of those jars. By that way, we will have complete isolation between jars versions of those applications. However, when we keep jars at web application classloader level, then our aspect which intercepts PortletContainerImpl.dispatch(..), which is contained by portal-portlet-lib.jar located in \server\default\deploy\jboss-portal.sar\lib folder, will have problem at accessing those classes such as Acegi’s Authentication, and SecurityContextHolder classes in portal web application’s WEB-INF/lib folder. We need to load them, using web application’s classloader, and execute necessary logic to propagate SecurityContext into portlet application.

 

      PortletInvocationResponse around(PortletInvocation invocation) :

            execution(public PortletInvocationResponse org.jboss.portal.portlet.impl.jsr168.PortletContainerImpl.dispatch(..)) && args(invocation) {

           

            PortletContainer portletContainer = (PortletContainer)thisJoinPoint.getTarget();

           

            try {

                  propagateSecurityContextToPortlet(

portletContainer.getApplication());

            } catch(Exception ex) {

                  //just ignore to let execution of target continue...

            }

           

            PortletInvocationResponse response = proceed(invocation);

           

            try {

                  clearSecurityContextToPortlet(

portletContainer.getApplication());

            } catch(Exception ex) {

                  //just ignore...

            }

           

            return response;

      }

 

As you see from above code piece, we have an around advice for PortletContainerImpl.dispatch(..) method. Before letting target proceed, we propagate SecurityContext from portal to portlet web application’s SecurityContextHolder. When the execution of method completes, we need to clear SecurityContextHolder of that portlet web application.

 

You need to replaced weaved portal-portlet-lib.jar with old one in server\default\deploy\jboss-portal.sar\lib folder, and put aspectjrt.jar into that folder as well.

 

Caution: If it is suitable for you to place related jars into common library  folder (server\default\deploy\jboss-portal.sar\lib) of JBoss Portal, then you don’t need this aspect. All you need to enable Acegi Security in your portal-server.war web application as explained above. If you choose this approach you must remove acegi-security.jar from your portlet web applications own WEB-INF/lib folder, and JBoss portal web application’s WEB-INF/lib folder. Otherwise, SecurityContext couldn’t be transferred from one SecurityContextHolder (loaded by web app classloader of portal-server.war) to another one (loaded by a portlet web app classloader). There must be only one copy of acegi-security.jar in shared lib folder.