一般来说,实际项目中隐私数据没有在网络上明文跑路,都会采用不同的加密算法。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()); }