跳到主要内容
JackSparrow414
返回

shiro使用(密码加密以及加盐,附:加盐或者加密之后认证不通过的靠谱解决方案)

本文导航

展开本文导航

正文

既然要做,就做的细致一点,对得起自己!

一个应用最基本的职责就应该保护用户信息的安全,至于为什么登录密码或者相关的密码要加密,不再赘述。

前台新增用户时,用户设置登录名和密码,shiro对用户输入的密码进行加密处理,由于最近在搞Springboot,所以以后shiro相关的配置都会以在springboot中的形式展示。

前言 : 如何在数据库中存储密码?下图来自ByteByteGo官方Github仓库

密码加盐哈希后存储以及登录时验证密码的流程图为什么要加盐?

  1. 防止彩虹表攻击,降低被暴力破解的概率

  2. 每个用户的密码的hash值应该是唯一的,即使用户密码是一样的,那么经过hash之后的值应该是唯一的。

详细解释可参考上述GitHub仓库内容。

如何验证密码?

由于hash不可逆的,所以使用用户输入的密码和数据库保存的slat值重新计算一次,将计算结果和数据库表中的hash值进行对比,如果一致,则代表密码正确。

常见问题:1.如果在加密时,发现数据库里的密码还是没有加密成功,还是明文形式,请看第一步

                   2.如果加密,加盐之后,认证报错,报错如果为Submitted credentials for token did not match the expected credentials

                       请看第三步,看代码片段的注释

具体实现思路:加密--->查看数据库中的密码是否已经是加密过的了,已经加密过--------->登录验证,验证是否通过,不通过后台报的什么错-------->完成

第一步:在保存用户信息时,在Java代码里对当前密码进行加密。

 @Transactional(rollbackFor = Exception.class)
    public void insert(User user) {
      //对用户密码进行加密处理
      user.setPassWord(new SimpleHash("SHA-256",user.getPassWord(),null,20))
      //第二种写法
      user.setPassWord(new Sha256Hash(user1.getPassword(),null,20).toHex())
      this.save(user1);
    }

new SimpleHash()一共四个参数,第一个加密算法名字(MD5,SHA-256等),第二个参数是用户的明文密码,第三个参数为盐值,这里暂且不加盐,第四个参数为加密次数。

也可以用第二种方法直接使用Sha256Hash,三个参数分别对应第一种写法的后三个参数。

加密基本完成,去数据库查看,发现密码不再是明文。

第二步:登录时解密

PS:我相信有很多文章都是告诉在第一步应该配置下面这些,其实保存密码加密的部分是上面的几句代码就完成了,跟下面的配置没关系的。

登录时,输入明文密码,还要告诉shiro相关解密的规则,shiro配置类部分信息如下。

@Bean("credentialsMatcher")
public HashedCredentialsMatcher credentialsMatcher(){
       HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher()
       //加密算法的名字,也可以设置MD5等其他加密算法名字
       credentialsMatcher.setHashAlgorithmName("SHA-256")
       //加密次数
       hashedCredentialsMatcher.setHashIterations(20);
       //加密为哈希
       hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);

       return credentialsMatcher;
 }

@Bean("shiroRealm")
public ShiroRealm shiroRealm(){
       ShiroRealm shiroRealm = new ShiroRealm();
       //设置加密算法
       shiroRealm.setCredentialsMatcher(credentialsMatcher());

       return shiroRealm;

请注意:这里我正文用的是解密规则这个词,而在代码中注释的是加密规则,其实这是从不同的角度看这段代码,从用户角度看,就是系统拿到我的明文密码去解密,而在程序员角度看,就是将用户的明文加密去对比。不要搞混了。

第三步:在shiro自定义的realm中去调用下就可以了。调用之后,如果没有异常则通过shiro验证,如果存在异常则在具体业务逻辑的service层进行处理。

@Override
protected AuthenticationInfo doGetAuthenticationInfo(
     AuthenticationToken authenticationToken)throws AuthenticationException{

     //根据用户名字去数据库查询
     UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
     String username = token.getUsername();
     //这里表示的是数据库查询
     User user = service.getOne();
     //把数据库的密码和用户登录用的明文密码加密之后的形式进行对比,就是认证过程
     //第二个参数传的是查询数据库之后的数据库里的密码,而不是用户登录的明文密码
     SimpleAuthenticationInfo simpleAuthenticationInfo = new
                SimpleAuthenticationInfo(user,user.getPassword(),getName());

    //返回认证结果
    return simpleAuthenticationInfo;

到此为止,简单的密码加密就完成了。通常我们需要加盐值,理由:

1.如果不同用户的密码相同,假如都是123456,即使经过加密,那么数据库里存储的password也是相同的。为了避免这种情况出现,就要加盐,来保证这种情况不会发生。

第四步:加盐。在数据库表和表对应的POJO类中新增salt字段,用来保存密码对应的盐值和认证的时候用。

保存的时候加盐,设置盐值即可:

在第一步中的代码设置:这里使用Apache的commons-lang包中的方法,生产一个随机的数字,数字长度为20

String salt = RandomStringUtils.randomAlphabetic(20);
将第一步中的null参数替换为salt即可

在第三步设置盐值:同样,这里的第三个参数盐值也是数据库查询之后对应的盐值

SimpleAuthenticationInfo simpleAuthenticationInfo = new 
                SimpleAuthenticationInfo(user,user.getPassword(),user.getSalt(),getName());

到此为止,shiro的加密解密和加盐就完成了。很简单的四步。如果你是个小白,可以照着这四步来,很轻松的。

如果你还想稍微的深入了解一下shiro如何对比密码的,你可以继续看。

写在后面:shiro拿到密码之后是如何认证的?

在第三步最后我们传给shiro一个SimpleAuthenticationInfo对象,shiro会对这个对象进行验证操作,一个最核心的方法就是在HashedCredentialsMatcher类下的doCredentialsMatch()方法,如下:请注意两个参数对应的对象,

HashedCredentialsMatcher.doCredentialsMatch 对比 token 与已存储凭据的源码

第一行,将用户输入的明文密码根据配置里的加密规则和数据库里的盐值进行加密,具体方法如下:

HashedCredentialsMatcher.hashProvidedCredentials 读取身份信息中的盐值

根据第三步里返回的对象,拿到在数据库里存储的盐值,并设置进去。接下来如下:

HashedCredentialsMatcher 根据算法、盐与迭代次数创建 SimpleHash

可以看到,这里就是根据相应的加密算法,加密次数和盐值返回了一个SimpleHash,这里和第一步的第一种写法是一样的。

后面两步就很简单了。

第二行,得到用户数据库里密码

第三行,比较二者的值是否相同,返回true或者false

到此为止,shiro中关于密码加密和解密的相关就结束了。

切勿眼高手低!


分享本文:

继续阅读本系列

Shiro 使用

  1. shiro使用(简单登录流程)
  2. shiro使用(简单记住我/下次自动登录流程)附:cookie内容为deleteme的最终解决方法
  3. shiro使用(简单sesiion使用)
  4. shiro使用(密码加密以及加盐,附:加盐或者加密之后认证不通过的靠谱解决方案)正在阅读
  5. shiro使用(使用token预热,为什么要使用token)
  6. shiro使用(使用token)
  7. shiro使用(增强版token,JWT和shiro结合使用)
  8. shiro使用(最终集成springboot代码合集)

评论

欢迎提问、纠错和分享经验。发表评论需要登录 GitHub;中英文版本共享评论。

评论仅在正式网站开放。