Delegating Authentication to Web Services in Acegi

By Kenan Sevindik

What I like most about Acegi Security Framework is its configurability and extensibility. I think those two features are very crucial for any framework to be successful. Recently, I have come across a requirement of authenticating users via a web service and giving more detailed authentication failure messages according to result codes returned from that service. Well, it is very easy to develop a custom authentication provider and inject it into the related part in Acegi Security Framework. Let’s look at the details.

First, we code our custom authentication provider. We need to query the web service by giving the social security number and user password as input. As a result of our query, we receive a return code. At the end of the authentication process, Acegi should fetch the user from our own database and use it as a UserDetails object during the rest of the authentication and authorization process. Acegi already provides org.acegisecurity.providers.dao.DaoAuthenticationProvider, in which it first retrieves the user with its userDetailsService object and then performs additional authentication in which encrypted password comparison occurs. Therefore, the order of retrieving the user from the database and querying authentication web service won’t matter in our case. Hence, we can extend DaoAuthenticationProvider and reuse most of its code. We only need to override its additionalAuthenticationChecks method to perform the web service query.

protected void additionalAuthenticationChecks(UserDetails userDetails, 
		UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
	try {
		Kullanici kullaniciFromDB = (Kullanici) userDetails;
		//run your web service client code here to perform auth.
		//and check result code to throw AuthenticationExceptions
		//or do nothing if result code indicates success auth.
	} catch (RemoteException e) {
		throw new AuthenticationServiceException ("Problem with accessing ws endpoint :"
			+ getAuthServiceUrl(), e);
	}
}

After implementing our custom authentication provider, it is simply a matter of creating a Spring managed bean from it and injecting that bean into Acegi’s ProviderManager bean as a candidate provider.

<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
    <property name="providers">
        <list>
            <ref bean="webServiceAuthenticationProvider" />
            <ref bean="anonymousAuthenticationProvider" />
            <ref bean="testingAuthenticationProvider" />
        </list>
    </property>
</bean>

What about displaying more detailed authentication failure messages? As you see above, we receive different return codes from the authentication web service and throw an appropriate AuthenticationException accordingly. Acegi provides us with the ability to map AuthenticationExceptions with different URLs. When authentication fails, it first checks for those exception mappings, and if it finds one, it redirects the application to that URL; otherwise, to the default failure URL.

We need to configure Acegi to search for those exception mappings and inject them into the authenticationProcessingFilter bean.

<bean id="exceptionMappings" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
    <property name="location">
        <value>classpath:/resources/acegi.authExceptionMappings.properties</value>
    </property>
</bean>

<bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
    <property name="authenticationManager">
        <ref bean="authenticationManager" />
    </property>

    <property name="authenticationFailureUrl">
        <value>${security.auth.authenticationFailureUrl}</value>
    </property>

    <property name="defaultTargetUrl">
        <value>${security.auth.defaultTargetUrl}</value>
    </property>

    <property name="exceptionMappings">
        <ref local="exceptionMappings"/>
    </property>
</bean>

If we look at our acegi.authExceptionMappings.properties, as you see below, we give each different type of AuthenticationException a different URL. If Acegi cannot find a corresponding entry in this list, it will redirect the application to the authenticationFailureUrl, which is set into the authenticationProcessingFilter bean.

```properties org.acegisecurity.concurrent.ConcurrentLoginException=/controller.faces?_flowId=login&loginError=true&cause=concurrentLogin org.acegisecurity.AuthenticationServiceException=/controller.faces?_flowId=login&loginError=true&cause=authServiceException org.acegisecurity.CredentialsExpiredException=/controller.faces?_flowId=login&loginError=true&cause=credentialsExpired ``

Share: X (Twitter) LinkedIn