javaauthenticationshiro

Please ensure that at least one realm can authenticate these tokens


So I have set up my shiro to have two Realms. A Username and Password Realm, using the standard UsernamePasswordToken. I have also set up a Custom Bearer Authentication Token that works off a token passed in from the user.

If i just use my passwordValidatorRealm it works find, if no user is found throws unknown account, if password doesn’t match throws incorrect credentials, perfect. But as soon as i put in my tokenValidatorRealm it throws a

org.apache.shiro.authc.AuthenticationException: Authentication token of type [class org.apache.shiro.authc.UsernamePasswordToken] could not be authenticated by any configured realms.  

In this instance my tokenValidatorRealm returns null as no token was provided, so it moves on to the passwordValidatorRealm and just breaks.

Any ideas why introducing a second Realm will cause my working passwordValidatorRealm to break?

Have tried with different authentication strategies, and no luck there.

Using shiro 1.2.2

EDIT

I have two implementations, one for password and one for token

Password:

public class PasswordAuthorizingRealm extends AuthenticatingRealm {

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

    if (authenticationToken instanceof UsernamePasswordToken) {
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
        String username = usernamePasswordToken.getUsername();
        char[] password = usernamePasswordToken.getPassword();

        if (username == null) {
            throw new AccountException("Null usernames are not allowed by this realm!");
        }
        //Null password is invalid
        if (password == null) {
            throw new AccountException("Null passwords are not allowed by this realm!");
        }

        UserService userService = new UserServiceImpl();
        User user = userService.getUserByUsername(username);

        if (user == null) {
            throw new UnknownAccountException("Could not authenticate with given credentials");
        }

        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, user.getPassword(), "passwordValidatorRealm");

        return simpleAuthenticationInfo;

    } else {
        return null;
    }

}
}

and Bearer Token

public class TokenAuthorizingRealm extends AuthorizingRealm {

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    if (authenticationToken instanceof BearerAuthenticationToken) {

        BearerAuthenticationToken bearerAuthenticationToken = (BearerAuthenticationToken) authenticationToken;

        String username = "" + bearerAuthenticationToken.getPrincipal();
        User user = userService.getUserByUsername(username);
        //User with such username has not found
        if (user == null) {
            throw new UnknownAccountException("Could not authenticate with given credentials");
        }
        BearerAuthenticationInfo bearerAuthenticationInfo = new BearerAuthenticationInfo(user);
        return bearerAuthenticationInfo;

    }

 }

Shiro config

[main]

hashService = org.apache.shiro.crypto.hash.DefaultHashService
hashService.hashIterations = 500000
hashService.hashAlgorithmName = SHA-256
hashService.generatePublicSalt = true

hashService.privateSalt = ****

passwordService = org.apache.shiro.authc.credential.DefaultPasswordService
passwordService.hashService = $hashService

passwordMatcher = org.apache.shiro.authc.credential.PasswordMatcher
passwordMatcher.passwordService = $passwordService

authc = my.BearerTokenAuthenticatingFilter

tokenValidatorRealm = my.TokenAuthorizingRealm
passwordValidatorRealm = my.PasswordAuthorizingRealm

passwordValidatorRealm.credentialsMatcher = $passwordMatcher

securityManager.realms = $tokenValidatorRealm,$passwordValidatorRealm

These have been stripped out a bit, removed logging and other unnecessary code

The BearerTokenAuthenticatingFilter, just basically checks if a token has been supplied in the header if has

private void loginUser(ServletRequest request, ServletResponse response) throws Exception {

    BearerAuthenticationToken token = (BearerAuthenticationToken) createToken(request, response);

    if (token == null) {
        String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken "
                + "must be created in order to execute a login attempt.";
        throw new IllegalStateException(msg);
    }

    try {
        Subject subject = getSubject(request, response);
        subject.login(token);
        onLoginSuccess(token, subject, request, response);
    } catch (AuthenticationException e) {
        HttpServletResponse httpResponse = WebUtils.toHttp(response);
        httpResponse.sendRedirect("login");
    }
}

BearerAuthenticationInfo class

public class BearerAuthenticationInfo implements AuthenticationInfo {

private final PrincipalCollection principalCollection;
private final User user;

public BearerAuthenticationInfo(User user) {
    this.user = user;
    this.principalCollection = buildPrincipalCollection(user);
}

public PrincipalCollection getPrincipals() {
    return principalCollection;

}

public Object getCredentials() {
    return user.getUsername();
}

private PrincipalCollection buildPrincipalCollection(User user) {
    Collection<String> principals = new ArrayList<String>();
    principals.add(user.getUsername());
    return new SimplePrincipalCollection(principals, "tokenValidatorRealm");
}

}

Solution

  • Looks like it is expected behavior.

    If you look at the javadoc for ModularRealmAuthenticator:

     * @throws AuthenticationException if the user could not be authenticated or the user is denied authentication
     *                                 for the given principal and credentials.
     */
    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
    

    If you are having problems with the exception, you might need to change the code that calls the authentication to expect this exception.


    Left for other searches:

    You might have a missing supports method in your TokenAuthorizingRealm class.

    Something like

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof BearerAuthenticationToken;
    }
    

    should be present.