Showing posts with label acegi. Show all posts
Showing posts with label acegi. Show all posts

Thursday, 30 August 2007

How to implement remember me using acegi

I have extended the example application showing how to do form based login using acegi.

You need to apply the following changes to the securityContext.xml:

securityContext.xml (download)

Remember me beans:

<!-- remember me processing filter -->
<bean id="rememberMeProcessingFilter"
class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter">
<property name="rememberMeServices" ref="rememberMeServices" />
<property name="authenticationManager" ref="authenticationManager" />
</bean>

<bean id="rememberMeServices"
class="org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService"> <ref local="memoryAuthenticationDao"/>
</property>
<property name="key" value="someTokenName"/>
</bean>

<bean id="rememberMeAuthenticationProvider"
class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider">
<property name="key" value="someTokenName"/>
</bean>
Apply the following changes to existing beans:
Add the rememberMeServices to the logoutfilter for the logout filter to invalidate the remember me cookie.
<bean id="logoutFilter"
class="org.acegisecurity.ui.logout.LogoutFilter">
<constructor-arg index="0" value="/index.jsp"/>
<constructor-arg index="1">
<list>
<ref local="securityContextLogoutHandler"/>
<ref local="rememberMeServices"/>
</list>
</constructor-arg>
</bean>

Add the rememberMeProcessingFilter to the filterchain for the remember me checks to be performed when a user hits a acegi protected URI:
<bean id="filterChainProxy"
class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,rememberMeProcessingFilter,authenticationProcessingFilter,logoutFilter,exceptionTranslationFilter,filterInvocationInterceptor
</value>
</property>
</bean>

Also remember to set the rememberMeServices at the authenticationProcessingFilter to overwrite the default NullRememberMeServices with your rememberMeServices. That will provide acegi with a service to handle remember me token validation (took me a bit of debugging to find this error)
<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"/>
<property name="rememberMeServices" ref="rememberMeServices"/>
</bean>


Finally extend your login form with a checkbox to let your end user be remembered. The default parameter name is _acegi_security_remember_me:
<tr>
<td>Remember me:</td>
<td><input type="checkbox" name="_acegi_security_remember_me"/></td>
</tr>
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/basicAcegiExample
  5. type user, password as credentials

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

Tuesday, 14 August 2007

How to implement basic authentication with acegi - no container dependency

I have always implemented basic authentication by depending on a specific container implementation - always with the drawback of not being able to easy switch between containers in development, test and production.
Now I stood in front of another implementation and decided to look into Acegi's possibilities. If Acegi could provide a basic authentication mechanism bundled in my application I could get rid of the container dependency.
I did use Acegi on one other project to do form based login, but at that time the API changed between each little minor release making it a pain to upgrade.
Now they promise to keep the API stable so I thought it was about time give it a new fair chance to improve the first impression I got.

And I must say it did!

Following is a simple securityContext.xml needed to perform basic authentication using spring and acegi.
I provided a very simple UserDetailsService for the sake of the example. You should probably be using the
org.acegisecurity.userdetails.jdbc.JdbcDaoImpl implementation instead - or any other implementation that suites your setup.

securityContext.xml (download)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

<!-- ======================== FILTER CHAIN ======================= -->

<bean id="filterChainProxy"
class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,basicProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
</value>
</property>
</bean>

<!-- responsible for setting up a security context holder for other
authentication mechanisms to work with -->
<bean id="httpSessionContextIntegrationFilter"
class="org.acegisecurity.context.HttpSessionContextIntegrationFilter">
<!-- no session should ever be created. We are processing basic
authentication credentials on each request -->
<property name="allowSessionCreation" value="false"/>
</bean>

<!-- Processes basic authentication headers and works together with the
provided authentication manager and
entry point to perform basic authentication -->
<bean id="basicProcessingFilter"
class="org.acegisecurity.ui.basicauth.BasicProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationEntryPoint"
ref="authenticationEntryPoint"/>
</bean>

<!-- filter responsible for access decisions. What urlrequests may be
processed by which roles -->
<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager">
<ref local="httpRequestAccessDecisionManager"/>
</property>
<property name="objectDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=USER
</value>
</property>
</bean>

<!-- filter responsible for translating exceptions and delegating to the
provided authenticationEntryPoint -->
<bean id="exceptionTranslationFilter"
class="org.acegisecurity.ui.ExceptionTranslationFilter">
<property name="authenticationEntryPoint"
ref="authenticationEntryPoint"/>
</bean>

<!-- ======================== AUTHENTICATION ======================= -->

<bean id="authenticationEntryPoint"
class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
<property name="realmName" value="Protected atlight"/>
</bean>

<bean id="authenticationManager"
class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref local="daoAuthenticationProvider"/>
</list>
</property>
</bean>

<bean id="daoAuthenticationProvider"
class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="simpleUserDetailsService"/>
</bean>

<!-- a simple simple UserDetailsService -->
<bean id="simpleUserDetailsService"
class="com.blogspot.ancientprogramming.security.SimpleUserDetailsService" />

<bean id="httpRequestAccessDecisionManager"
class="org.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value="false"/>
<property name="decisionVoters">
<list>
<ref local="roleVoter"/>
</list>
</property>
</bean>

<bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter">
<property name="rolePrefix" value=""/>
</bean>
</beans>


web.xml
Together with this spring, acegi configuration in your web.xml you are up and running.
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
<display-name>Archetype Created Web Application</display-name>

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/securityContext.xml</param-value>
</context-param>

<filter>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>org.acegisecurity.util.FilterChainProxy</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>

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/basicAcegiExample
  5. type user, password as credentials