All,
I finally got Siteminder integrated with Acegi in my appfuse 1.9 application. It
is deployed on JBoss 4.0 running on Linux 3.0 and j2sdk1.4.2_06.
Has it turns out I did not have to make much changes. Here is what I had to do:-
1. Create a new filter class that extends SiteminderAuthenticationProcessingFilter.
2. Override the requiresAuthentication() method which is found on the grandparent
of the SiteminderAuthenticationProcessingFilter class the AbstractProcessingFilter
class.
Why did I need to override the requiresAuthentication() method?
========================================
This method normally attempts authentication when it receives a j_security_check
url from the login page. In the case of Siteminder a j_security_check is never issued
form a login page so the method is modified to attempt authentication if it receives a
a j_security_check from the login page or if the getDefaultTargetUrl() is received and
the user is NOT already authenticated. In this case the getDefaultTargetUrl() is
/mainMenu.html.
Method requiresAuthentication() is overridden on new filter class:-
=============================================
protected boolean requiresAuthentication(HttpServletRequest request,
HttpServletResponse response) {
String uri = request.getRequestURI();
int pathParamIndex = uri.indexOf(';');
if (pathParamIndex > 0) {
// strip everything after the first semi-colon
uri = uri.substring(0, pathParamIndex);
}
//attempt authentication if j_secuity_check is present or if the getDefaultTargetUrl()
//is present and user is not already authenticated.
boolean bAuthenticated = false;
SecurityContext context = (SecurityContext)request.getSession().getAttribute(HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY);
if (context != null) {
Authentication auth = context.getAuthentication();
if (auth != null && auth instanceof UsernamePasswordAuthenticationToken) {
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken)auth;
bAuthenticated = token.isAuthenticated();
}
}
//if true is returned then authentication will be attempted.
boolean bAttemptAuthentication = (uri.endsWith(request.getContextPath() + getFilterProcessesUrl())) ||
((uri.endsWith(getDefaultTargetUrl()) && !bAuthenticated));
if (logger.isDebugEnabled()) {
logger.debug(
"Authentication attempted for the following URI ==> " + uri + " is " + bAttemptAuthentication);
}
return bAttemptAuthentication;
}
Original requiresAuthentication() on the AbstractProcessingFilter class:-
=============================================
protected boolean requiresAuthentication(HttpServletRequest request,
HttpServletResponse response) {
String uri = request.getRequestURI();
int pathParamIndex = uri.indexOf(';');
if (pathParamIndex > 0) {
// strip everything after the first semi-colon
uri = uri.substring(0, pathParamIndex);
}
return uri.endsWith(request.getContextPath() + filterProcessesUrl);
}
3. Changes to the application-security.xml:-
<bean id="authenticationProcessingFilter" class="org.appfuse.webapp.filter.GESiteminderAuthenticationProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationFailureUrl" value="/login.jsp?error=true"/>
<property name="defaultTargetUrl" value="/mainMenu.html"/>
<property name="filterProcessesUrl" value="/j_security_check"/>
<property name="siteminderUsernameHeaderKey" value="SM_USER"/>
<property name="siteminderPasswordHeaderKey" value="SM_USER"/>
<property name="rememberMeServices" ref="rememberMeServices"/>
</bean>
If there is an easier way to achieving the solution or a question to the approach I took please don't
hesitate to comment.
Thanks,
- Paul
