Monday, 27 August 2007

How to implement formbased authentication with acegi - no container dependencies

I couple of days ago I implemented a sample application illustration the minimal configuration needed for doing basic authentication using acegi. I have decided to take the next step and show a sample of a form based authentication solution - still without any container dependencies. Having no dependencies to a specific container seems to gain a bit of momentum in the Java community these days and Per wrote a interesting blog about keeping your deployment thin and lightweight.

Based on my last basic authentication example you only need to change the following:

See securityContext.xml (download)

ProcessingFilter
Switch the basicProcessingFilter with this one:

<!-- Processes formbased authentication.
The html form should contain to input fields: j_username and j_password.
The post of the form should point at the value of the "filterProcessesUrl"
in this case /j_acegi_security_check -->

<bean id="authenticationProcessingFilter"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationFailureUrl" value="/index.jsp?login_error=1"/>
<property name="defaultTargetUrl" value="/secure/securecontent.jsp"/>
<property name="filterProcessesUrl" value="/j_acegi_security_check"/>
</bean>

Also remember to change the use in your filterChainProxy.
...
/**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
...
AuthenticationEntryPoint
Switch authenticationEntryPoint with this:
<!-- changed to work with formbased login -->
<bean id="authenticationEntryPoint"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl" value="/index.jsp"/>
</bean>
Further I changed the UserDetailsService as my own implementation could be swapped with an org.acegisecurity.userdetails.memory.InMemoryDaoImpl and still be a lightweight in memory userdetails implementation for the sake of the example:
<!-- implemented memory dao -->
<bean id="memoryAuthenticationDao"
class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
<property name="userMap">
<value>user=password,USER</value>
</property>
</bean>
Logout
Contrary to basic authentication you are able to let the user logout without closing the browser.

Add the following logoutFilter to the filterChainProxy
...
/**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,logoutFilter,exceptionTranslationFilter,filterInvocationInterceptor
...
and create a logout handler like the SecurityContextLogoutFilter:
<bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter">
<constructor-arg index="0" value="/index.jsp"/>
<constructor-arg index="1">
<list>
<ref local="securityContextLogoutHandler"/>
</list>
</constructor-arg>
</bean>

<bean id="securityContextLogoutHandler"
class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler" />
This snippet will handle a logout when the user requests the /j_acegi_logout that is the default url. Can be changed as a parameter to the logout filter.

Actually I found this api documentation for the BasicProcessingFilterEntryPoint indicating that it is possible to logout using basic authentication without killing the browser:
/**
* Used by the <code>SecurityEnforcementFilter</code> to commence authentication via the {@link
* BasicProcessingFilter}.<P>Once a user agent is authenticated using BASIC authentication, logout requires that
* the browser be closed or an unauthorized (401) header be sent. The simplest way of achieving the latter is to call
* the {@link #commence(ServletRequest, ServletResponse, AuthenticationException)} method below. This will indicate to
* the browser its credentials are no longer authorized, causing it to prompt the user to login again.</p>
*
* @author Ben Alex
* @version $Id: BasicProcessingFilterEntryPoint.java 1877 2007-05-25 05:33:06Z benalex $
*/
I tried the suggested commence solution in my basic autentication example, but did not have success. If you are able to implement the commence solution let me know.

Sample (download)
I also provided a simple webapp demonstrating the implementation, just:
  1. download
  2. unzip
  3. type $mvn jetty:run
  4. go to: http://localhost:8080/formbasedAcegiExample
  5. type user, password as credentials
Next step
Using formbased login you can easy implement "remember me" functionality. In a couple of days I will have a sample for that as well. I will update this post with a link when the time comes. I have implemented a remember me example and it is available here.

Further documentation

2 comments:

GK said...

Hi,
Your article is good, but i want to overwrite LogoutFilter class. bcos i want to do some operation when i logout. I wrote a class also. But the problem is when i start my server it is automatically calls my UserDefinedLogoutFilter, but i want to call this class only when i logout, do you have any idea why it is happening like this. I wrote another class for AuthenticationProcess, tat one works fine.it call only when i click login button. only logout creating issue.

Jacob von Eyben said...

The logoutfilter already specified in the context.xml takes a list of logouthandlers.

Maybe you implement your own and add it to the list?

Currently only the securityContextLogoutHandler is registered, but you can add you own to the list.