2021年你还不会Shiro?----4.使用MD5+盐+hash散列进行密码加密

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: 上一篇文章里介绍了使用自定义的Realm来实现数据的获取,不过,数据的获取依然不是来源于真实的数据库或者是nosql,只是包装了一个方法,假装从这个方法里获取了数据库中的用户信息,然后我们返回了一个SimpleAccount,这个对象携带了用户名与密码,当时我们是明文返回的密码。这样做很显然是不安全的,一旦数据来源被攻破,所有的用户信息都会被泄露。所以这里我们介绍下,常用的密码加密策略。

一.前言



上一篇文章里介绍了使用自定义的Realm来实现数据的获取,不过,数据的获取依然不是来源于真实的数据库或者是nosql,只是包装了一个方法,假装从这个方法里获取了数据库中的用户信息,然后我们返回了一个SimpleAccount,这个对象携带了用户名与密码,当时我们是明文返回的密码。这样做很显然是不安全的,一旦数据来源被攻破,所有的用户信息都会被泄露。所以这里我们介绍下,常用的密码加密策略。


二.常用加密策略



我们在日常的开发中可能都是用过不同的加密算法,那么这些算法到底有哪些优势呢,他们的特点又是什么呢?这里简单介绍下几种常用的加密算法。


1.MD5


1.什么是MD5


MD5与我们常用的SM2等算法有很大区别,通常我们使用的加密算法都是可以将密文破解的,比如基于对称算法与非对称算法实现的加密方式,但MD5则不行,MD5的典型特点就是不可逆,也就是说我们加密的信息就不能再反向解密出来了,那么可能有人会说网络上有很多MD5在线解密的网站,注意这些并不是真的解密了MD5加密后的密文,而是使用穷举法,对密文进行一一匹配,来实现的破解,这种暴力破解其实很好规避,不过这样牵扯出了MD5的另一个特点,那就是固定的明文多次加密的结果都是一样的使用MD5算法生成的始终是一个16进制的32位(256bit)的字符串


2.MD5的应用场景


①应用于加密场景,比如登录中的密码加密。
②应用于签名,用来判定两个文件是否是同一个文件,同一个文件使用MD5加密后的密文肯定是一致的,这样可以防止文件被篡改。


2.AES


3.什么是AES


AES是一种常用的加密算法,该算法是一种对称加密算法,何为对称加密算法呢,就是加密与解密都需要同一个秘钥,我们常用的AES有AES128,AES192,AES256,这些是什么意思呢,这三种AES其实就是指不同的秘钥的长度,三种AES对应的秘钥长度分别是128位,192位,256位,换算成字节就是16,24,32。比如这样一个秘钥:String str= “1234567812345678”,那么使用这个秘钥的AES自然就是,用的AES128了。


4.AES的使用场景


在实际的项目中通常前后端数据的交互都是采用AES加密,这样就可以规避敏感信息的泄露,不过值得说的是通常都不是仅仅对AES进行简单的加密,加密完以后还会对密文进行Base64加密,然后才会进行交互,这样就保证了信息传输的安全性,不过没有百分百的安全,只要是密文能被解出来,就会存在破解的风险,风险都是相对的,我们能做的就是相对安全。


三.使用MD5+盐+hash散列实现登录



我们已经都知道了,使用MD5加密,我们依然可以使用穷举法来实现暴力破解,这样就需要用户设置密码时不能使用简单的密码,简单的密码很容易就会被穷举到,但是用户在使用时是没有这样的意识的,所以就需要我们为用户的密码加点“盐”,让密码变得更咸一点,使密码不会这么容易被破解出来。所谓的这个盐呢,其实就是为密码拼接一串我们随机生成的字符串。此外我们还可以为MD5+盐生成的16进制的值进行hash散列,这样就会让密码更加的安全,即使数据库被攻破,盐与密码都被获取了,散列次数一样还是不会被发现,当然了这样情况也是极少碰到的。


1.如何使用Shiro的MD5,对数据进行加密


那么如何使用Shiro提供的MD5呢,我们看下下面的代码就明白了了

public class Test {
    public static void main(String[] args) {
        Md5Hash md5Hash = new Md5Hash("123");
        System.out.println(md5Hash.toHex());
        Md5Hash md5Hash1 = new Md5Hash("123","12345&8");
        System.out.println(md5Hash1.toHex());
        Md5Hash md5Hash2 = new Md5Hash("123","12345&8",1024);
        System.out.println(md5Hash2.toHex());
    }
}


上面的程序输出结果如下所示:

202cb962ac59075b964b07152d234b70
19d11f5191d12b1c224a7d535e1cb650
cc24303e7e1827dbb9742d1f4efeb0ec
Process finished with exit code 0


根据上面的例子,我们可以很形象的看出哈,第一个例子是只使用了MD5进行加密的数据,第二步部分是加上了“盐”的MD5加密,第三部分则是在第二部分的基础上对值进行散列,其实根据这样思路我们可以对密码进行很多种加密,比如对盐进行散列,对盐也进行MD5加密,或者对盐使用Base64,或者是AES等,都可以,我们可以组合出很多的加密,不过其实是没有必要的,没有绝对的安全,但是一般我们使用MD5+“盐”+hash散列就已经可以了。


2.使用MD5+盐+hash散列实现登录


下面我们就来看下使用Shiro实现的登录场景中是如何使用这种加密的方式呢,前面的文章已经介绍过,在密码验证的地方有个密码匹配器,默认的密码匹配器就是做equals比较密码的,既然我们不想使用equals直接比较,那么就需要更换这个密码匹配器,那么我们怎么更换这个密码匹配器呢?


1.密码匹配器在哪里


前面我们已经说过密码的校验是在这里完成的,如下图


image.png


该方法的第一行就是密码匹配器了,密码匹配器是该类的一个属性,所以我们想要更换密码匹配器就为这个属性set一个我们想要的密码匹配器就可以实现更换。


2.匹配匹配器在哪里换


我们已经知道密码匹配器在哪里了,那我们想要更换难道是在这个密码匹配器所属的类中更换吗,当然不是我们写的,真正的更换地方应该使我们写的自定义realm中去进行更换,我们通常会为安全管理器设置realm,在设置realm之前我们需要更换密码匹配器,还需要告诉这个realm我们使用哪种加密算法。那么我们对应的代码就应该是下面这个样子了


public class TestAuthenticator2 {
    public static void main(String[] args) throws Exception{
        //定义安全管理器
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        //定义一个支持MD5+盐+hash散列的密码匹配器
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        //告诉密码匹配器密文是哪种加密方式
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        //告诉密码匹配器,密码被散列的次数,这个一般定义为1024的倍数,默认一次
        hashedCredentialsMatcher.setHashIterations(1024);
        //定义自己的realm
        SecondRealm secondRealm = new SecondRealm();
        //为realm设置密码匹配器
        secondRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        //为安全管理器设置realm
        defaultSecurityManager.setRealm(secondRealm);
        //模拟用户登录场景
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("zhaoyun","123");
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        try {
            Subject subject = SecurityUtils.getSubject();
            subject.login(usernamePasswordToken);
            System.out.println("登录成功");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


既然我们已经告诉了安全管理器,我数据库存储的是MD5+盐+hash散列实现的密文,那么我们的realm再像以前那么写就不正确了。


3.自定义realm


在我们不对密码进行加密时我们模拟的realm获取的都是明文的用户名与密码返回给了密码匹配器,那么我们想使用MD5+盐+hash散列的方式去实现密码加密,以前的信息返回肯定会报错,那我们该如何写呢,请看下面的代码


public class SecondRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken)authenticationToken;
        String userName = usernamePasswordToken.getUsername();
        //获取数据库中的用户信息
//        SimpleAccount simpleAccount = new SimpleAccount("zhaoyun","202cb962ac59075b964b07152d234b70","df",this.getName());
        SimpleAuthenticationInfo simpleAuthenticationInfo = getSimpleInfo();
        //验证用户名与token用户名是否相同
        if(simpleAuthenticationInfo.getPrincipals().asList().contains(userName)){
            return simpleAuthenticationInfo;
        }else{
            return  null;
        }
    }
    private SimpleAuthenticationInfo getSimpleInfo(){
        Md5Hash md5Hash = new Md5Hash("123");
        Md5Hash md5Hash1 = new Md5Hash("123","12345&8");
        Md5Hash md5Hash2 = new Md5Hash("123","12345&8",1024);
        return new SimpleAuthenticationInfo("zhaoyun",md5Hash2, ByteSource.Util.bytes("12345&8"),this.getName());
    }
}


有一点要说下在上一个例子中我们实现自定义realm,是去继承AuthenticatingRealm,这个例子里我们是去继承的AuthorizingRealm,这两个Realm一个是做认证的,一个是授权的(第一个认证),而且用作授权的AuthorizingRealm是AhthenticatingRealm的子类,所以我们可以直接继承AuthorizingRealm也是正确的,就像上方的代码,继承了这个类,就要求我们重写认证和授权两块的代码了。授权这部分还未涉及到,我们暂时先不说,还继续看认证的部分。


根据这个方法SimpleAuthenticationInfo(),我们可以看到里面写了三个Md5Hash的对象,第一个是只用了Md5,第二个使用了随机盐,随机盐即使一组随机的字符串,第三个则是使用了MD5+盐+散列1024次生产的密文,


细心的同学可能会发现哈,我们在为realm设置密码匹配器时并没有告诉ream随机盐是多少,只告诉了密码匹配器密文是何种加密方式,需要散列多少次。


4.密码匹配器怎么知道随机盐是多少的


其实很简单,我们在完成用户名验证时,就会返回一个账户信息也就是SimpleAuthenticatingInfo该类的对象,这个对象包含了密码,已经盐,在到达密码匹配器时,会根据是否传了盐来去动态获取盐的值,而不需要我们在用户登录时直接携带者盐,况且用户登录携带着盐也是极不安全的,所以我们并不需要为密码匹配器设置盐。


5.使用MD5+盐+hash散列成功登录


运行上方的代码我们可以得到结果如下:


登录成功
Process finished with exit code 0


感兴趣的大佬可以多试试,这里就不罗列其他的几种密码使用场景了。


四.总结



这篇文章主要介绍了MD5在使用Shiro登录时的的应用场景,我们使用MD5+盐+hash散列的方式,可以很大程度上保证密码的安全性,在日常的开发中其实,密码加你也是这种,就比如我前阵子在做的项目,前后端交互在对参数进行加密时就是使用AES+Base64进行双重加密的。

相关文章
|
2月前
|
存储 Java 数据库
密码专辑:对密码加盐加密,对密码进行md5加密,封装成密码工具类
这篇文章介绍了如何在Java中通过加盐和加密算法(如MD5和SHA)安全地存储密码,并提供了一个密码工具类PasswordUtils和密码编码类PasswordEncoder的实现示例。
73 10
密码专辑:对密码加盐加密,对密码进行md5加密,封装成密码工具类
|
2月前
|
NoSQL Java Redis
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
这篇文章介绍了如何使用Spring Boot整合Apache Shiro框架进行后端开发,包括认证和授权流程,并使用Redis存储Token以及MD5加密用户密码。
42 0
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
|
2月前
|
存储 安全 Java
shiro学习二:shiro的加密认证详解,加盐与不加盐两个版本。
这篇文章详细介绍了Apache Shiro安全框架中密码的加密认证机制,包括不加盐和加盐两种加密方式的实现和测试。
142 0
|
2月前
|
数据安全/隐私保护 Python
Python中的MD5加密“解密”
Python中的MD5加密“解密”
112 0
|
1天前
|
安全 算法 网络协议
【网络原理】——图解HTTPS如何加密(通俗简单易懂)
HTTPS加密过程,明文,密文,密钥,对称加密,非对称加密,公钥和私钥,证书加密
|
21天前
|
存储 SQL 安全
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
随着互联网的普及,网络安全问题日益突出。本文将介绍网络安全的重要性,分析常见的网络安全漏洞及其危害,探讨加密技术在保障网络安全中的作用,并强调提高安全意识的必要性。通过本文的学习,读者将了解网络安全的基本概念和应对策略,提升个人和组织的网络安全防护能力。
|
22天前
|
SQL 安全 网络安全
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
随着互联网的普及,网络安全问题日益突出。本文将从网络安全漏洞、加密技术和安全意识三个方面进行探讨,旨在提高读者对网络安全的认识和防范能力。通过分析常见的网络安全漏洞,介绍加密技术的基本原理和应用,以及强调安全意识的重要性,帮助读者更好地保护自己的网络信息安全。
44 10
|
24天前
|
SQL 安全 网络安全
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
在数字化时代,网络安全和信息安全已成为我们生活中不可或缺的一部分。本文将介绍网络安全漏洞、加密技术和安全意识等方面的内容,并提供一些实用的代码示例。通过阅读本文,您将了解到如何保护自己的网络安全,以及如何提高自己的信息安全意识。
48 10
|
24天前
|
SQL 安全 网络安全
网络安全漏洞、加密技术与安全意识的知识分享
随着互联网的普及,网络安全问题日益严重。本文将介绍网络安全漏洞的概念、类型和防范措施,以及加密技术的原理和应用。同时,强调提高个人和企业的安全意识对于防范网络攻击的重要性。
|
23天前
|
存储 安全 网络安全
网络安全的盾与剑:漏洞防御与加密技术的实战应用
在数字化浪潮中,网络安全成为保护信息资产的重中之重。本文将深入探讨网络安全的两个关键领域——安全漏洞的防御策略和加密技术的应用,通过具体案例分析常见的安全威胁,并提供实用的防护措施。同时,我们将展示如何利用Python编程语言实现简单的加密算法,增强读者的安全意识和技术能力。文章旨在为非专业读者提供一扇了解网络安全复杂世界的窗口,以及为专业人士提供可立即投入使用的技术参考。

热门文章

最新文章