我为我的应用程序开发了一个身份验证组件。我正在使用带有盐渍密码的Apache Shiro API。
我使用salt创建一个新用户,如下例所示:
ByteSource salt = randomNumberGenerator.nextBytes(32);
byte[] byteTabSalt = salt.getBytes();
String strSalt = byteArrayToHexString(byteTabSalt);
String hashedPasswordBase64 = new Sha256Hash(inPassword, salt, 512).toBase64();
但我不明白我是如何在doGetAuthenticationInfo方法中使用salt来验证用户的。我的方法必须返回SaltedAuthenticatedInfo,但我不明白我是如何创建它的。
我不明白Credential Matcher和SaltedAuthenticateInfo之间的联系。
创建密码盐时,是否必须通知凭据匹配器?
感谢您的帮助。
答案 0 :(得分:7)
SaltedAuthenticationInfo
是一个界面。为方便起见,Shiro API提供了许多默认实现。尽可能尝试使用其中一个默认实现;避免创建自己的
我建议SimpleAuthenticationInfo
实现的不仅仅是SaltedAuthenticationInfo
,但可能足以满足您的目的
有关详细信息,请参阅org.apache.shiro.authc.SimpleAuthenticationInfo。
如果您绝对需要实施自己的SaltedAuthenticationInfo
,请仔细阅读文档
有关详细信息,请参阅org.apache.shiro.authc.AuthenticationInfo和org.apache.shiro.authc.SaltedAuthenticationInfo。
boolean doCredentialsMatch(AuthenticationToken, AuthenticationInfo)
实际上处理了身份验证逻辑
此方法以AuthenticationToken
的形式获取用户提交的凭据,并将其与AuthenticationInfo
形式的先前存储的凭据进行比较。
您必须首先确保将所有必要信息传递给HashCredentialMatcher
(迭代,算法和SaltedAuthenticationInfo
中的盐)。
伪示例使用,
final int iterations = 50000;
AuthenticationToken authToken = ...;
SaltedAuthenticationInfo saltedAuthInfo = ...;
HashedCredentialsMatcher authenticator =
new HashedCredentialsMatcher(Sha256Hash.ALGORITHM_NAME);
authenticator.setHashIterations(iterations);
final boolean successfulAuthentication =
authenticator.doCredentialsMatch(authToken, saltedAuthInfo);
有关详细信息,请参阅org.apache.shiro.authc.credential.HashedCredentialsMatcher。
盐长
256位盐看起来不错。使用大的盐可以最大限度地降低任何两个用户共享相同盐的风险。在选择Birthday Paradox发挥作用的盐长度时请记住。
迭代次数
根据经验,您永远不会使用少于10,000。您目前使用512,
String hashedPasswordBase64 = new Sha256Hash(inPassword, salt, 512).toBase64();
大多数哈希算法都非常快(包括sha256),你不想做任何可能的黑客任何好处。您使用的迭代次数越多,验证速度越慢,但它也会直接减慢破解尝试的速度。
您需要尽可能高地设置迭代次数,同时仍然保持应用程序的可接受响应性。你可能会感到惊讶 我个人倾向于使用数百万;但我很偏执,不介意稍微延迟 有关详细信息,请参阅Key Stretching。
我个人会避免硬编码任何散列参数(散列算法,盐大小,迭代次数等)
通过对这些值进行硬编码,您可以限制您的即时适应和响应能力。
使用散列凭据存储这些值可以让您进行更加动态的身份验证,您可以在相对较少的工作量中配置和推出更强大的算法。
例如,您的默认哈希算法可能是sha256,使用50,000次迭代和256位盐。在未来,虽然50,000次迭代可能还不够 无需大惊小怪,您就可以将首选算法配置更改为所有新密码的100,000次迭代。您不必担心破坏旧密码,因为您没有更改使用现有凭据存储的算法参数。 您也可以使用它来更改salt-size甚至算法。
如果需要,您可以让每个人都更改密码;强迫用户选择新的(希望更强大的)首选算法设置 Unix操作系统已经使用/etc/shadow多年来完成了这项工作。
预先需要更多的努力,但值得投资。强认证控制至关重要。
答案 1 :(得分:0)
我的错误是无法正确创建与AuthenticationToken进行比较的AuthenticationInfo。所以在我自己领域的doGetAuthenticationInfo方法中我这样做:
Object principal = arg0.getPrincipal();
Object credentials = arg0.getCredentials();
String realmName = this.getName(); // to get the realm name
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(principal, credentials, realmName);
CredentialsMatcher credentialsMatcher = this.getCredentialsMatcher();
boolean successfulAuthentication = credentialsMatcher.doCredentialsMatch(arg0, simpleAuthenticationInfo);
因此boolean successfulAuthentication为true。 但是我不明白CredentialsMatcher和HashedCredentialsMatcher之间的区别是什么,因为那个是假的。我必须阅读Javadoc。