【发布时间】:2016-04-04 18:12:15
【问题描述】:
我面临的问题是 Shiro 在转换字节时表现出一些奇怪的行为 数组到盐。
我开始将流程中涉及的所有类实现到我的应用程序中,它们是:
- org.apache.shiro.realm.AuthenticatingRealm
- org.apache.shiro.authc.credential.HashedCredentialsMatcher
在创建用户时,用户密码使用生成的盐进行哈希处理,然后存储在我的数据库中:
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.crypto.RandomNumberGenerator;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
RandomNumberGenerator rng = new SecureRandomNumberGenerator();
Object salt = rng.nextBytes();
String hashedPasswordBase64 = new Sha256Hash(password, salt, 1024).toBase64();
shiro.ini 看起来像这样:
# SALTED JDBC REALM
saltedJdbcRealm=com.mycompany.ssp.SaltedJdbcRealm
dataSource = org.postgresql.ds.PGSimpleDataSource
dataSource.databaseName = Self-Service-Portal
dataSource.serverName = localhost
dataSource.portNumber = 5432
dataSource.user = postgres
dataSource.password = admin
saltedJdbcRealm.dataSource = $dataSource
saltedJdbcRealm.authenticationQuery = SELECT umgmt_users.password, umgmt_users.salt FROM umgmt_users WHERE umgmt_users.user = ?
sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
# base64 encoding, not hex in this example:
sha256Matcher.storedCredentialsHexEncoded = false
sha256Matcher.hashIterations = 1024
saltedJdbcRealm.credentialsMatcher = $sha256Matcher
################################################################################
# SECURITY MANAGER #
securityManager.realms = $saltedJdbcRealm
strategy = org.apache.shiro.authc.pam.FirstSuccessfulStrategy
securityManager.authenticator.authenticationStrategy = $strategy
################################################################################
我的自定义 saltedJdbcRealm 只是覆盖了 doGetAuthenticationInfo。这段代码来自这个博客->
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//identify account to log to
UsernamePasswordToken userPassToken = (UsernamePasswordToken) token;
String username = userPassToken.getUsername();
if (username == null) {
log.debug("Username is null.");
return null;
}
// read password hash and salt from db
PasswdSalt passwdSalt = getPasswordForUser(username);
if (passwdSalt == null) {
log.debug("No account found for user [" + username + "]");
return null;
}
// return salted credentials
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, passwdSalt.password, getName());
info.setCredentialsSalt(new SimpleByteSource(passwdSalt.salt));
return info;
}
return info 之后的调试如下:
- AuthenticatingRealm.java:方法:assertCredentialsMatch()
- HashedCredentialsMatcher.java:方法:doCredentialsMatch()
- HashedCredentialsMatcher.java:方法:hashProvidedCredentials()
寻找错误我最终在这里找到了 org.apache.shiro.authc.credential.HashedCredentialsMatcher.java:
protected Object hashProvidedCredentials(AuthenticationToken token, AuthenticationInfo info) {
Object salt = null;
if (info instanceof SaltedAuthenticationInfo) {
// STOP HERE AND SEE BELOW PART 1!!!
salt = ((SaltedAuthenticationInfo) info).getCredentialsSalt();
// STOP HERE AND SEE BELOW PART 2!!!
} else {
//retain 1.0 backwards compatibility:
if (isHashSalted()) {
salt = getSalt(token);
}
}
return hashProvidedCredentials(token.getCredentials(), salt, getHashIterations());
}
第 1 部分:
让我们看一下变量信息:
完整的字节数组如下:
57 109 102 43 65 87 118 88 70 76 105 82 116 104 113 108 116 100 101 108 79 119 61 61
正确表示我数据库中的盐:
9mf+AWvXFLiRthqltdelOw==
代码中的下一步是从 info 变量中提取 Salt 并将其存储在 Object 类型的变量 salt 中。
第 2 部分:
查看这一行之后的变量 salt:
salt = ((SaltedAuthenticationInfo) info).getCredentialsSalt();
执行我得到这个结果:
OW1mK0FXdlhGTGlSdGhxbHRkZWxPdz09
编辑:
我做了另一个例子,向您展示了 2 种方法:1)对提交的密码进行哈希处理 2)从数据库中获取密码以进行比较并且它们不是 一样的:
我从 2 个变量开始,token(提交的密码)和 info(存储的密码信息):
存储的凭据:
凭证:
d5fHxI7kYQYtyqo6kwvZFDATIIsZThvFQeDVidpDDEQ
解码前的存储字节数:
100 53 102 72 120 73 55 107 89 81 89 116 121 113 111 54 107 119 118 90 70 68 65 84 73 73 115 90 84 104 118 70 81 101 68 86 105 100 112 68 68 69 81 61
解码后的存储字节数:
119 -105 -57 -60 -114 -28 97 6 45 -54 -86 58 -109 11 -39 20 48 19 32 -117 25 78 27 -59 65 -32 -43 -119 -38 67 12 68
哈希:
7797c7c48ee461062dcaaa3a930bd9143013208b194e1bc541e0d589da430c44
提交的凭据:
char[] 凭据:
[0] = 1
[1] = 2
[2] = 3
字节[]字节:
50 69 81 77 57 55 80 53 53 112 89 52 122 69 78 54 57 98 53 56 82 65 61 61
这是2EQM97P55pY4zEN69b58RA==,这是数据库中的内容
cachedBase64:
MkVRTTk3UDU1cFk0ekVONjliNThSQT09
返回值是这个哈希:
af9a7ef0ea9fa4d93eae1ca5d16c03c516f4822ec3e9017f14f694175848a6ab
由于 2 个哈希值不相同,我明白为什么我的应用程序告诉我密码错误但我使用上面的代码(第一个代码块)使用密码 123 创建了这个用户
编辑结束
那么有谁知道为什么哈希计算没有为相同的密码给出相同的哈希值???或者我可能做错了什么(我怀疑 shiro 代码是错误的,所以在生成密码 hash/salt 或 shiro.ini 配置时我的代码可能有问题?)
【问题讨论】:
-
looking at the variable salt I get this result:这是什么意思?变量salt,它应该是调试屏幕截图中#944 的SimpleByteSource。不是这样吗?SimpleAuthenticationInfo的代码在这里:shiro.apache.org/static/1.2.1/apidocs/src-html/org/apache/shiro/… -
我的意思是这条线:
salt = ((SaltedAuthenticationInfo) info).getCredentialsSalt();在 Stop Part1 和 Part 2 部分之间。该变量被分配在那里,但值错误。我更新了上面的部分以便更好地理解 -
但变量
salt是什么?它应该与您在Part 1中看到的SimpleByteSource相同。 -
好吧,官方的“shiro-code”将其设置为对象类型
Object salt = null;,但我会尝试检查这是否可能是原因 -
它被声明为Object,但在运行时它是什么?