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进行双重加密的。

相关文章
|
1月前
|
存储 NoSQL 数据库
认证服务---整合短信验证码,用户注册和登录 ,密码采用MD5加密存储 【二】
这篇文章讲述了在分布式微服务系统中添加用户注册和登录功能的过程,重点介绍了用户注册时通过远程服务调用第三方服务获取短信验证码、使用Redis进行验证码校验、对密码进行MD5加密后存储到数据库,以及用户登录时的远程服务调用和密码匹配校验的实现细节。
认证服务---整合短信验证码,用户注册和登录 ,密码采用MD5加密存储 【二】
|
17天前
|
存储 安全 算法
RSA在手,安全我有!Python加密解密技术,让你的数据密码坚不可摧
【9月更文挑战第11天】在数字化时代,信息安全至关重要。传统的加密方法已难以应对日益复杂的网络攻击。RSA加密算法凭借其强大的安全性和广泛的应用场景,成为保护敏感数据的首选。本文介绍RSA的基本原理及在Python中的实现方法,并探讨其优势与挑战。通过使用PyCryptodome库,我们展示了RSA加密解密的完整流程,帮助读者理解如何利用RSA为数据提供安全保障。
33 5
|
20天前
|
安全 数据安全/隐私保护 Python
情书也能加密?Python AES&RSA,让每一份数据都充满爱的密码
【9月更文挑战第8天】在这个数字化时代,情书不再局限于纸笔,也可能以电子形式在网络中传递。为了确保其安全,Python提供了AES和RSA等加密工具,为情书编织爱的密码。首先,通过安装pycryptodome库,我们可以利用AES对称加密算法高效保护数据;接着,使用RSA非对称加密算法加密AES密钥和IV,进一步增强安全性。即使情书被截获,没有正确密钥也无法解读内容。让我们用Python为爱情编织一张安全的网,守护每份珍贵情感。
30 2
|
1天前
|
数据安全/隐私保护 Python
Python中的MD5加密“解密”
Python中的MD5加密“解密”
|
1月前
|
存储 算法 Java
在Java中使用MD5对用户输入密码进行加密存储、同时登录验证。
这篇文章详细介绍了在Java项目中如何使用MD5算法对用户密码进行加密存储和登录验证,包括加入依赖、编写MD5工具类、注册时的密码加密和登录时的密码验证等步骤,并通过示例代码和数据库存储信息展示了测试效果。
在Java中使用MD5对用户输入密码进行加密存储、同时登录验证。
|
1月前
|
安全 Nacos 数据安全/隐私保护
【技术干货】破解Nacos安全隐患:连接用户名与密码明文传输!掌握HTTPS、JWT与OAuth2.0加密秘籍,打造坚不可摧的微服务注册与配置中心!从原理到实践,全方位解析如何构建安全防护体系,让您从此告别数据泄露风险!
【8月更文挑战第15天】Nacos是一款广受好评的微服务注册与配置中心,但其连接用户名和密码的明文传输成为安全隐患。本文探讨加密策略提升安全性。首先介绍明文传输风险,随后对比三种加密方案:HTTPS简化数据保护;JWT令牌减少凭证传输,适配分布式环境;OAuth2.0增强安全,支持多授权模式。每种方案各有千秋,开发者需根据具体需求选择最佳实践,确保服务安全稳定运行。
91 0
|
1天前
|
安全 算法 网络安全
网络安全的盾牌与矛:漏洞、加密与安全意识
【9月更文挑战第26天】在数字时代的浪潮中,网络安全如同一场没有硝烟的战争。本篇文章将带领读者穿梭于网络安全的攻防之间,揭示网络漏洞的成因与对策,探讨加密技术如何保护数据的安全,以及提升个人和组织的安全意识的重要性。从基础概念到实际应用,我们将一步步构建起对网络安全的全面认识,为抵御网络威胁提供知识武装。
|
1天前
|
存储 SQL 安全
网络安全与信息安全:守护数字世界的坚盾在这个高度数字化的时代,网络安全和信息安全已经成为个人、企业乃至国家安全的重要组成部分。本文将深入探讨网络安全漏洞、加密技术以及安全意识的重要性,旨在为读者提供一个全面的网络安全知识框架。
随着互联网技术的飞速发展,网络安全问题日益凸显。从个人信息泄露到企业数据被盗,再到国家安全受到威胁,网络安全事件层出不穷。本文将从网络安全漏洞的定义与分类入手,探讨常见的网络攻击手段;随后深入解析加密技术的原理及其在保护信息安全中的作用;最后强调提升公众与企业的安全意识的重要性,并提出具体的建议。通过综合运用这些知识点,我们可以更好地构建起一道道坚固的防线,守护我们的数字世界。
|
2天前
|
存储 安全 网络安全
网络安全的屏障与钥匙:漏洞、加密与意识
【9月更文挑战第25天】在数字时代的洪流中,网络安全成为保护数据和隐私的关键盾牌。本文将深入探讨网络安全中的三大支柱:漏洞防御、加密技术和安全意识。我们将从基础概念出发,逐步分析它们的重要性,并揭示如何通过这些手段构建坚固的网络安全防线。文章旨在为非技术读者提供易于理解的安全知识,帮助他们在日益复杂的网络世界中保持警惕和安全。
|
2天前
|
存储 供应链 安全
守护网络前线:漏洞、加密与安全意识的全方位解析
在这个数字时代,网络安全已成为我们不可忽视的重要议题。本文深入探讨了网络安全中的三大关键领域:安全漏洞、加密技术以及安全意识。通过具体案例和实用策略,旨在为读者提供一个全面而深入的视角,以更好地理解和应对网络安全挑战。