Search code examples
authenticationshiro

Apache Shiro: Exception-Handling with Multiple Realms


We are using two realms (one for hashed passwords, the other one for generated plaintext keys) - this is working as expected.

With a single realm, we could throw a DisabledAccountException exception in our realm's protected AuthenticationInfo doGetAuthenticationInfo(final AuthenticationToken authToken) and explicitly catch such an exception in our application.

Now that we have two realms, all exceptions are Shiro-internally caught; so if one realm fails, the second one can be tried as well. However, this kind of redirect will only throw generic AuthenticationExceptions to our application.

Is there any workaround with multiple realms so that we can have more specific exceptions (to know if an account is locked, the credentials are simply wrong,...)?


Solution

  • You need to specify your own AuthenticationStrategy in your ModularRealmAuthenticator. The ModularRealmAuthenticator uses AtLeastOneSuccessfulStrategy by default and AtLeastOneSuccessfulStrategy ignores the exceptions and keeps trying to login the users using all the realms available.

    We had a similar scenario on the tynamo project and to workaround this issue I've implemented my own AuthenticationStrategy, called FirstExceptionStrategy, that works with multiple realms and throws the first exception it gets. This approach works fine as long as there is only one Realm per Token type.

    The implementations is rather simple:

    /**
     * {@link org.apache.shiro.authc.pam.AuthenticationStrategy} implementation that throws the first exception it gets
     * and ignores all subsequent realms. If there is no exceptions it works as the {@link FirstSuccessfulStrategy}
     *
     * WARN: This approach works fine as long as there is ONLY ONE Realm per Token type.
     *
     */
    public class FirstExceptionStrategy extends FirstSuccessfulStrategy {
    
        @Override
        public AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t) throws AuthenticationException {
            if ((t != null) && (t instanceof AuthenticationException)) throw (AuthenticationException) t;
            return super.afterAttempt(realm, token, singleRealmInfo, aggregateInfo, t);
        }
    
    }
    

    I repeat, this only works if there is ONLY ONE Realm per Token type.

    For more info about my particular scenario see here: http://jira.codehaus.org/browse/TYNAMO-154