一般来说,实际项目中隐私数据没有在网络上明文跑路,都会采用不同的加密算法。Shiro中的认证凭据通常也会采用算法进行加密。
【1】CredentialsMatcher接口
该接口只有一个方法,doCredentialsMatch,就是用来进行密码比较的!
源码如下:
public interface CredentialsMatcher {
/**
* Returns {@code true} if the provided token credentials match the stored account credentials,
* {@code false} otherwise.
*
* @param token the {@code AuthenticationToken} submitted during the authentication attempt
* @param info the {@code AuthenticationInfo} stored in the system.
* @return {@code true} if the provided token credentials match the stored account credentials,
* {@code false} otherwise.
*/
boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info);
}
其实现类如下:
里面可以看到我们常用的MD5算法和SHA-X算法。
其中Md5CredentialsMatcher 和Sha1CredentialsMatcher 标注已经过时,通常我们会直接在容器中注册HashedCredentialsMatcher来使用!
如这里我们注册HashedCredentialsMatcher并指定算法为Md5,xml配置如下:
<!-- 自定义Realm -->
<bean id="customRealm" class="com.web.maven.shiro.CustomRealm">
<!-- 将凭证匹配器设置到realm中,realm按照凭证匹配器的要求进行散列 -->
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!-- 加密算法 -->
<property name="hashAlgorithmName" value="MD5"/>
<!-- 加密次数 -->
<property name="hashIterations" value="1"/>
</bean>
</property>
</bean>
【2】盐值加密
使用【1】中的配置情况下,如果两个人的原始密码一样,那么其加密后的密码也相同,同样存在风险。
在HashedCredentialsMatcher中有这样一个方法:
protected Hash hashProvidedCredentials(Object credentials, Object salt, int hashIterations) {
String hashAlgorithmName = assertHashAlgorithmName();
return new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
}
其中new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations)
就是根据提供的原始凭据,加密算法,盐值和加密次数进行加密并返回加密后的结果。盐值是一个唯一字符串,这里你可以使用loginName作为加密盐值。
步骤如下:
- 在 doGetAuthenticationInfo 方法返回值创建 SimpleAuthenticationInfo 对象的时候, 需要使用
SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName)
构造器; - 使用 ByteSource.Util.bytes() 来计算盐值;
- 盐值需要唯一, 一般使用随机字符串或 user id;
- 使用 new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations); 来计算盐值加密后的密码的值。
如果使用盐值加密,我们的doGetAuthenticationInfo修改如下:
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//获取页面传来的用户账号
String loginName = token.getUsername();
//根据登录账号从数据库查询用户信息
SysUser user = sysUserService.getUserByLoginCode(loginName);
System.out.println("从数据库查询到的用户信息 : "+user);
//一些异常新娘西
if (null == user) {
throw new UnknownAccountException();//没找到帐号
}
if (user.getStatus()==null||user.getStatus()==0) {
throw new LockedAccountException();//帐号被锁定
}
//其他异常...
//返回AuthenticationInfo的实现类SimpleAuthenticationInfo
// return new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());
//盐值加密
ByteSource credentialsSalt = ByteSource.Util.bytes(loginName);
return new SimpleAuthenticationInfo(user, user.getPassword(), credentialsSalt, this.getName());
}