请确保至少有一个领域可以对这些令牌进行身份验证

时间:2014-03-12 13:43:49

标签: java authentication shiro

所以我设置了shiro有两个领域。用户名和密码域,使用标准UsernamePasswordToken。我还设置了一个自定义承载认证令牌,用于处理从用户传入的令牌。

如果我只是使用我的passwordValidatorRealm它可以找到,如果没有找到用户会抛出未知帐户,如果密码不匹配则抛出不正确的凭据,完美。但是只要我输入我的tokenValidatorRealm就会抛出一个

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

在这个例子中,我的tokenValidatorRealm返回null,因为没有提供令牌,因此它会移动到passwordValidatorRealm并且只是中断。

为什么引入第二个Realm的任何想法都会导致我的工作密码ValidatorRealm中断?

尝试过不同的身份验证策略,但没有运气。

使用shiro 1.2.2

修改

我有两个实现,一个用于密码,一个用于令牌

密码:

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;
    }

}
}

和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

这些已经被删除了一些,删除了日志记录和其他不必要的代码

BearerTokenAuthenticatingFilter,只是基本上检查标头中是否已提供令牌

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类

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");
}

}

2 个答案:

答案 0 :(得分:1)

看起来是预期的行为。

如果你看一下ModularRealmAuthenticator的javadoc:

 * @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 {

如果您遇到异常问题,则可能需要更改调用身份验证的代码以期望此异常。


留给其他搜索:

您的TokenAuthorizingRealm类中可能缺少支持方法。

这样的东西
@Override
public boolean supports(AuthenticationToken token) {
    return token instanceof BearerAuthenticationToken;
}

应该在场。

答案 1 :(得分:0)

这个讨论帮助我解决了类似的问题。我想通过应用程序本身验证用户,而不是使用任何Shiro默认实现。为此,我们必须继承AuthenticatingRealm,重写doGetAuthenticationInfo并将此域声明为验证域。

public class PasswordAuthorizingRealm extends AuthenticatingRealm {

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

在Shiro.ini:

passwordValidatorRealm = my.PasswordAuthorizingRealm