Shiro异常源码解析java.lang.IllegalArgumentException: Odd number of characters

java.lang.IllegalArgumentException: Odd number of characters 字符长度奇数个异常
各位猿友可以自行尝试Hex.decode(),传入奇数个字符数组都会报错。

下面主要讲解引入shiro框架后出现此问题的缘由。

这个异常其实是Hex过程中抛出的异常,下面由我来分析一下起因经过

一般的,使用权限框架中为了加强密码强度,防止被恶意破解,我们会在使用加盐加密循环N次去强化密码,如下所示

/**
 * 配置凭证匹配器
 */
@Bean
public CredentialsMatcher hashedCredentialsMatcher(){
    HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
    hashedCredentialsMatcher.setHashIterations(3);   //  加密次数
    hashedCredentialsMatcher.setHashAlgorithmName("md5"); // 算法名称 md5   sha
    return hashedCredentialsMatcher;
}

看下HashedCredentialsMatcher构造器源码

public HashedCredentialsMatcher() {
    this.hashAlgorithm = null;
    this.hashSalted = false;
    this.hashIterations = 1;
    this.storedCredentialsHexEncoded = true;
}

注意关键字

storedCredentialsHexEncoded

这里shiro帮我们设置凭证匹配器的类型为hex,在看看下面源码
当我们进行用户密码登录校验的时候,shiro根据 AuthenticationToken 和 AuthenticationInfo 获取凭证进行比较校验的。

/**
* token,为登录时通过subject,login(xxx)传入的
* info,为自定义ralm中doGetAuthenticationInfo方法返回的对象
*
* tokenHashedCredentials,用户传入的用户密码进行加盐加密得到
* accountCredentials,数据库存储的密码(密文)
*/
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
    Object tokenHashedCredentials = this.hashProvidedCredentials(token, info);
    Object accountCredentials = this.getCredentials(info);
    return this.equals(tokenHashedCredentials, accountCredentials);
}
protected Object getCredentials(AuthenticationInfo info) {
    Object credentials = info.getCredentials();
    byte[] storedBytes = this.toBytes(credentials);
    if (credentials instanceof String || credentials instanceof char[]) {
        if (this.isStoredCredentialsHexEncoded()) {
            storedBytes = Hex.decode(storedBytes);
        } else {
            storedBytes = Base64.decode(storedBytes);
        }
    }

    AbstractHash hash = this.newHashInstance();
    hash.setBytes(storedBytes);
    return hash;
}

在调用getCredentials方法的时候,因为我们配置的是hex方式,所以会执行

storedBytes = Hex.decode(storedBytes);

由于storedBytes长度为奇数个,所以在进行解码的时候报错了Odd number of characters。

但真正的问题应该是AuthenticationInfo的凭证有问题导致的,MD5加密后的密文,长度一定是偶数个。主要确保我们生成AuthenticationInfo时,传入的凭证为加密凭证就对了。

/**
 * 自定义认证realm
 *
 * @param authenticationToken
 * @return
 * @throws AuthenticationException
 */
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    String username = String.valueOf(authenticationToken.getPrincipal());
    UserEntity user = userService.getUser(username);
    if (user == null) {
        log.error("找不到用户信息,请重新登录");
        return null;
    }
    // 生成AuthenticationInfo时,凭证应该为数据库中存储的密码
//        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, authenticationToken.getCredentials(), ByteSource.Util.bytes(username), getName());
    SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, user.getPassword(), ByteSource.Util.bytes(username), getName());
    return simpleAuthenticationInfo;
}

总结,如若出现以上问题,大概是对shiro认证环节不了解导致的。在认证过程中,程序员需要告诉shiro用户、密码和加密后的密码三个条件,shiro会在内部组装成两个对象进行校验的。

你可能感兴趣的:(java,shiro,java)