Struts, Acegi, and JBoss Portal

You can easily find documentation for running struts applications as a JSR 168 portlet–for example, here. In fact, the JBoss Portal itself now ships with the apache portals-bridges-common library, so all you need to do is ship your application with the appropriate implementation from apache (in my case, portals-bridges-struts-1.2.7-1.0), and you can simply follow the standard instructions for getting any struts application running as a JSR 168 portlet. Those instructions say that you need a container specific implementation of org.apache.portals.bridges.common.ServletContextProvider, and fortunately, JBoss Portal ships with one–specifically, org.jboss.portal.portlet.bridge.JBossServletContextProvider, which the struts bridge expects you to declare in portlet.xml.

You can also find documentation for running applications requiring Acegi on the JBoss Portal. In fact, Acegi ships with a JBoss adaptor. This adaptor is in two parts–one that provides a JBoss Portal Login Module, and one that provides a filter that is meant to be used in place of the httpSessionContextIntegrationFilter. The Login Module returns an instance of Authentication, which JBoss Portal then makes available to all portlets via JNDI…specifically, it places a JAAS Subject at java:comp/env/security/Subject, and one of the principals associated with that subject will be the Authentication instance returned from the Login Module. It also makes the Principal available from the portletRequest object…just call portletRequest.getUserPrincipal(). If you look at the code of the filter, you can see that all it does is get the Subject from the jndi location, and then iterate through all of the associated JAAS Principals, looking for a Principal that is an instance of Authentication. It then places this Authentication in the SecureContext. Note that if you don’t have Acegi handle the login on the portal side of things, you won’t actually get a Principal that is an instance of Authentication, but in theory you can write your own filter which gets the active principal, calls getName() on the principal, (which returns the login name) and then looks up an Authentication instance in your database that corresponds to the name.

However, if you try to combine these two concepts–the Struts Bridge, and the filter, you will find that it doesn’t actually work. The issue is that when the acegi filter is running inside the struts bridge, there won’t actually be a subject at java:comp/env/security/Subject. If you cast the ServletRequest in the filter to an HttpServletRequest, and then call getUserPrincipal(), you won’t find anything there either. Oddly enough, if you put a scriptlet on the page that you want to display to the user, you will find that you can now access the Subject in JNDI, but by then it’s too late…you want the Authentication object in the SecureContext before the Acegi intercepting filter is run, not when you are displaying the page.

The solution is as follows: subclass the included JBossServletContextProvider, and then override the HttpServletRequest getHttpServletRequest(GenericPortlet genericPortlet, PortletRequest portletRequest) method. You’ll still want to get the HttpServletRequest to return, by calling super, but while you’re in this method, you have access to the Subject via JNDI, or, if you want, the Principal via portletRequest.getUserPrincipal. Once you have the Authentication instance, you have a couple of possibilities: You can place it in the session, and it will be available to you in an Acegi filter, where you can place it into the SecureContext. Alternatively, you can place it directly into the SecureContext. Note that placing it into the request won’t help…it won’t be available to you in the request object in the Acegi filter. If you use this method, don’t forget to update portlet.xml and put in the name of your subclass instead of the included JBossServletContextProvider.

In the application I am working on, we use a slight variant–we don’t use the Acegi JBoss Login module, and so when we get the Principal, we just get the Portal login name. So, our logic looks something like this: Get the Principal. See if there is an Authentication instance already in the session with a name that matches the login name. If there is no authentication instance, or if the authentication instance name does not match the portal login name, then get an authentication instance corresponding to the login name out of the database. Once we have the Authentication, either from the database or the session, we place that in the SecureContext and in the session. We then don’t have a httpSessionContextIntegrationFilter configured for Acegi, but rather proceed directly to the intercepting filter.


One thought on “Struts, Acegi, and JBoss Portal

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s