Back Forum Reply New

AuthenticationTrustResolverImpl and ROLE_ANONYMOUS

Using Spring Security 2.0.6
--------------------------

A little background, I'm attempting to provide an automatic subscriber role (ROLE_SUBSCRIBER) to users who are coming from certain IP ranges.  These IP ranges are liable to change at any time as new customers subscribe etc.  In order to do this I've created a SpringSecurityFilter which takes the user's current authentication and wraps it in a custom Authentication which adds ROLE_SUBSCRIBER to the existing collection of roles.  The filter looks up the IP address in a subscriber service to determine if the role should be applied.  The idea for the authentication wrapper can be seen at questions/2...tion-to-anonym

So I now have role based subscription access working as expected.  If a user accesses the home page they are assigned a subscriber role (assuming their IP address matches).  The AuthenticationWrapper takes the AnonymousAuthenticationToken and wraps it thus providing both ROLE_ANONYMOUS and ROLE_SUBSCRIBER. All good so far, but if the user then tries to sign in (to access their profile etc) they are required to have ROLE_USER in their authentication.  The ExceptionTranslationFilter handles the AccessDeniedException in handleException(), which in turn delegates to AuthenticationTrustResolver to determine if the current authentication is anonymous.  The default implementation (AuthenticationTrustResolverImpl) only checks a type hierarchy to see if the given authentication is deemed to be anonymous.  Which in the case of a wrapped authority fails and the user is not deemed to be anonymous.  This results in the AccessDeniedHandler.handle() being called instead of sendStartAuthentication() which results in the target ucl not being stored as a SavedRequest in the session.  Which means once the user successfully signs in they are redirected to the default login success ucl (homepage) instead of the original page they requested (their profile).Code:
public class AuthenticationTrustResolverImpl implements AuthenticationTrustResolver {
   private Class anonymousClass = AnonymousAuthenticationToken.class;
   ...
   public boolean isAnonymous(Authentication authentication) {       if ((anonymousClass == null) || (authentication == null)) {return false;       }
       return anonymousClass.isAssignableFrom(authentication.getClass());   }
   ...
   public void setAnonymousClass(Class anonymousClass) {       this.anonymousClass = anonymousClass;   }
}
So the problem I'm having it that even through the Authentication object's granted authorities include ROLE_ANONYMOUS, AuthenticationTrustResolverImpl.isAnonymous() is returning false.  I realise I could simply subclass AuthenticationTrustResolverImpl and override isAnonymous() to check for these roles, but this would involve migrating away from the simple lt;fromgt; XML element style security configuration.

The lt;anonymousgt; element inside of lt;fromgt; has an attribute called granted-authority which defaults to ROLE_ANONYMOUS.

What I'm proposing is an enhancement to AuthenticationTrustResolverImpl.isAnonymous() to actually check the granted authorities of the Authentication passed to it.  If the Authentication has a granted authority that matches that defined in the granted-authority attribute of lt;anonymousgt; then isAnonymous should return true.

Does this sound like a logical solution? Or am I barking up the wrong tree?

I've managed to work out a solution to this problem that has no impact on the convenient lt;fromgt; security config.

First up I created a subclass of AuthenticationTrustResolverImpl which explicitly checks for the existence of ROLE_ANONYMOUS instead of type AnonymousAuthenticationToken.Code:
package xyz;

import org..security.Authentication;
import org..security.AuthenticationTrustResolver;
import org..security.AuthenticationTrustResolverImpl;
import org..security.GrantedAuthority;
import org..security.providers.anonymous.AnonymousAuthenticationToken;
import org..stereotype.Component;

/*** An implementation of {@link AuthenticationTrustResolver} that checks on granted authorities* rather than a type hierarchy.*/
@Component
public class SubscriberAsAnonymousAuthenticationTrustResolver extends AuthenticationTrustResolverImpl
{

/*** The anonymous role to check for is currently hard coded, however using the amp;lt;fromamp;gt; * style security configuration it is possible to set the 'granted-authority' assigned when an* anonymous role is created.  If that value were to change this would have to change also.*/
private static final String AUTHORITY_TO_CHECK_FOR = quot;ROLE_ANONYMOUSquot;;

/*** The default implementation only checks a type hierarchy, and assumes an authentication is not* anonymous if it isn't of type {@link AnonymousAuthenticationToken}.* This class overrides this functionality with an implementation that checks the granted* authorities for the occurrence of ROLE_ANONYMOUS.  This allows for more flexible situations* such as an authentication having both an ANONYMOUS role and a SUBSCRIBER role at the same* time.      */
@Override
public boolean isAnonymous(Authentication authentication)
{
if (authentication == null || authentication.getAuthorities() == null) {
return false;// No authentication or authorities means authorisation has not yet completed
}
for (GrantedAuthority authority : authentication.getAuthorities())
{
if (AUTHORITY_TO_CHECK_FOR.equals(authority.getAuthority())) {
return true;
}
}
return false;
}

}
To wire up this non-default implementation of AuthenticationTrustResolver I created a small throw away class that simply gets hold of the ExceptionTranslationFilter by type from the Spring context sets the AuthenticationTrustResolver implementation to the one I created.Code:
package xyz;

import org.apache.log4j.Logger;
import org..beans.factory.annotation.Autowired;
import org..security.ui.ExceptionTranslationFilter;
import org..stereotype.Component;

import xyz.SubscriberAsAnonymousAuthenticationTrustResolver;

/*** A throw away object that simply wires up a custom AuthenticationTrustResolver to the* ExceptionTranslationFilter. */
@Component
public class SetAuthenticationTrustResolver
{

private static final Logger logger = Logger.getLogger(SetAuthenticationTrustResolver.class);

@Autowired
public SetAuthenticationTrustResolver(ExceptionTranslationFilter filter, SubscriberAsAnonymousAuthenticationTrustResolver resolver) {
logger.debug(quot;Wiring up quot; + resolver.getClass().getName() + quot; to quot; + filter.getClass().getName());
filter.setAuthenticationTrustResolver(resolver);
}

}
Ensure both SubscriberAsAnonymousAuthenticationTrustResolver  and SetAuthenticationTrustResolver are in your context component-scan base-package and you are away laughing.
¥
Back Forum Reply New