请确保至少有一个领域可以验证这些令牌

Posted

技术标签:

【中文标题】请确保至少有一个领域可以验证这些令牌【英文标题】:Please ensure that at least one realm can authenticate these tokens 【发布时间】:2014-04-16 16:49:37 【问题描述】:

所以我已经将我的 shiro 设置为拥有两个 Realms。使用标准 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 并中断。

任何想法为什么引入第二个领域会导致我的工作密码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;
    



和不记名令牌

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 配置

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



【问题讨论】:

您能否与我们分享您的领域 doGetAuthenticationInfo 实现以及您如何调用登录? 已更新原始帖子并提供更多信息 【参考方案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;

应该在场。

【讨论】:

对不起,我忘了在上面的代码中包含它,我的两个实现中都有 support 方法。我也尝试在令牌实现构造函数中运行 setAuthenticationTokenClass(BearerAuthenticationToken.class);但这没有效果 您是否尝试过调试并看到实际上在两个领域实现上都调用了支持方法? Yip 都被调用了。令牌优先(在 ini 中指定为第一个)并返回 false。然后是返回 true 的密码。 好的,密码中 doGetAuthenticationInfo 方法的调试是否提供了任何额外的见解,是否被调用? Nope :( 它一直运行并返回我的 simpleAuthenticationInfo (不为空),然后仍然抛出“请确保至少一个领域可以验证这些令牌。”。如果我在我的 ini 中禁用 TokenAuthorizingRealm,它也会一直运行,但我得到了准确的异常(UnknownAccountException 或 IncorrectCredentialsException...)【参考方案2】:

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

public class PasswordAuthorizingRealm extends AuthenticatingRealm 

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException 

在 Shiro.ini 中:

passwordValidatorRealm = my.PasswordAuthorizingRealm

【讨论】:

以上是关于请确保至少有一个领域可以验证这些令牌的主要内容,如果未能解决你的问题,请参考以下文章

在 JavaScript 中,如何确保数组至少有一个特定元素,而其他元素满足另一个条件?

流畅的断言。如何验证两个列表至少有一个相同的元素

在没有身份验证令牌的情况下使用 Python 和 Twitter API

Javascript密码验证

csharp 验证至少有一个给定属性具有值(如果字符串也检查空字符串)

struts 2 checkboxlist验证问题