如何使用Shiro的SaltedAuthenticationInfo?

时间:2012-09-10 15:54:27

标签: java salt shiro

我为我的应用程序开发了一个身份验证组件。我正在使用带有盐渍密码的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之间的联系。

创建密码盐时,是否必须通知凭据匹配器?

感谢您的帮助。

2 个答案:

答案 0 :(得分:7)

SaltedAuthenticationInfo

SaltedAuthenticationInfo是一个界面。为方便起见,Shiro API提供了许多默认实现。尽可能尝试使用其中一个默认实现;避免创建自己的 我建议SimpleAuthenticationInfo实现的不仅仅是SaltedAuthenticationInfo,但可能足以满足您的目的 有关详细信息,请参阅org.apache.shiro.authc.SimpleAuthenticationInfo

如果您绝对需要实施自己的SaltedAuthenticationInfo,请仔细阅读文档 有关详细信息,请参阅org.apache.shiro.authc.AuthenticationInfoorg.apache.shiro.authc.SaltedAuthenticationInfo

HashedCredentialMatcher

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。