Welcome

Delegating Authentication to JAAS Module in Acegi Security

We are currently using Acegi Security in our web project. At the moment we employ its form based authentication. In the future, we have to integrate our web application with an environment, in which JAAS based single sign on mechanism will be used for authentication. For this moment, as a first step, we tried to delegate authentication to a JAAS module using Acegi’s JaasAuthenticationProvider. Configuration process is very simple, and is explained in Acegi Reference Documentation.
There is an inbalance between JAAS and Acegi Security System. In JAAS everything, even roles, are represented as principals, but in Acegi there is Authentication object, in which there exists one named principal, which corresponds simply to username, and multiple GrantedAuthority objects, which simply correspond to roles.There must be a facility to map between JAAS Principal objects and Acegi GrantedAuthority objects. Acegi provides AuthorityGranter interface for this mapping. JaasAuthenticationProvider passes each principal fetched from login module to the AuthorityGranter object. AuthorityGranter object inspects that principal object and returns a string, as a role information, if current principal corresponds to a valid role. JaasAuthenticationProvider uses those role information and principal name to create JaasGrantedAuthority objects. Finally, Acegi Authentication object consists of those GrantedAuthority objects.
We have implemented a derivation of database based JAAS authentication module from Tagish JAAS Login Modules, and used a principal type similar to its TypedPrincipal. This contains information which means what type of principal it is, such as user or role. Our login module gets user information from specified database location for authentication. This user information contains username, password and its roles. Later JaasAuthenticationProvider passes each principal into our RoleNameBasedAuthorityGranter implementation.  RoleNameBasedAuthorityGranter checks if passed principal represents a role of current user. If it is, then it retuns role name string back to JaasAuthenticationProvider. Finally, JaasAuthenticationProvider uses those information to construct an Authentication object if authentication is successfull.
One drawback of JaasAuthenticationProvider in current Acegi distribution is that it isn’t able to cache authenticated user information. In order to remedy this problem, we have extended JaasAuthenticationProvider and added a UserCache object. Our CachingJaasAuthenticationProvider first looks into user cache for user details and if it finds any, uses it to perform authentication, otherwise it delegates authentication to its super. After successfull authentication, its caches user details in case it is needed for successive queries.
I would like to mention, as a footnote, that there is a nice blog entry in Thomas Dudziak’s Weblog, where he explains how to enable JAAS authentication and authorization for a Struts based web application step by step. It is very easy to adapt it for other types of web applications, too.
Finally, I appended source code of our RoleNameBasedAuthorityGranter, CachingJaasAuthenticationProvider and its spring bean definition below for future reference:

public class CachingJaasAuthenticationProvider extends
        JaasAuthenticationProvider {

    private UserCache userCache = new NullUserCache();
    
    public void setUserCache(UserCache userCache) {
        this.userCache = userCache;
    }
    
    public UserCache getUserCache() {
        return userCache;
    }
    
    public Authentication authenticate(Authentication auth)
            throws AuthenticationException {
        String username = "NONE_PROVIDED";
        
        if (auth.getPrincipal() != null) {
            username = auth.getPrincipal().toString();
        }

        if (auth.getPrincipal() instanceof UserDetails) {
            username = ((UserDetails) auth.getPrincipal()).getUsername();
        }

        boolean cacheWasUsed = true;
        UserDetails user = this.userCache.getUserFromCache(username);
        
        if(user != null && isPasswordCorrect(auth,user)) {
            publishSuccessEvent((UsernamePasswordAuthenticationToken)auth);
        } else {
            cacheWasUsed = false;
            
            auth = super.authenticate(auth);
            if(auth != null) {
                auth = createSuccessAuthentication(auth);
                user = (UserDetails)auth.getPrincipal();
                this.userCache.putUserInCache(user);
            }
        }
        
        return auth;
            
    }
    
    private Authentication createSuccessAuthentication(Authentication auth) {
  UserDetails userDetails = new JaasUser((String)auth.getPrincipal(),
auth.getCredentials().toString(),auth.getAuthorities());
        
        UsernamePasswordAuthenticationToken result = 
new UsernamePasswordAuthenticationToken(
                userDetails,auth.getCredentials(),auth.getAuthorities());
        
        return result;
    }
    
    private boolean isPasswordCorrect(
Authentication authentication, UserDetails user) {
        if(StringUtils.isNotEmpty(user.getPassword())) {
            return user.getPassword().equals(
authentication.getCredentials());
        }
        return false;
    }
}


public class RoleNameBasedAuthorityGranter implements AuthorityGranter {

    public String grant(Principal principal) {
        if(principal instanceof TypedPrincipal) {
            TypedPrincipal typedPrincipal = (TypedPrincipal)principal;
            if(typedPrincipal.getType() == TypedPrincipal.ROLE) {
                return principal.getName();
            }
        }
        return null;
    }

}
<bean id="jaasAuthenticationProvider" class="tbs.verisozlugu.guvenlik.jaas.CachingJaasAuthenticationProvider">
<property name="loginConfig">
            <value>VeriSozlugu.login</value>
      </property>
      <property name="loginContextName">
            <value>VeriSozlugu</value>
      </property>
      <property name="callbackHandlers">
            <list>
<bean class="net.sf.acegisecurity.providers.jaas.JaasNameCallbackHandler"/>
<bean class="net.sf.acegisecurity.providers.jaas.JaasPasswordCallbackHandler"/>
            </list>
      </property>
      <property name="authorityGranters">
            <list>
      <bean class="tbs.verisozlugu.guvenlik.jaas.RoleNameBasedAuthorityGranter"/>
            </list>
          </property>
    <property name="userCache">
        <ref bean="userCache"/>
    </property>
</bean>

 

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.