消息摘要是把任意长度(每个人的密码的长度不一样)的输入揉和而产生长度固定的信息。

​ 消息摘要算法的主要特征是加密过程不需要密钥,并且经过加密的数据无法被解密只有输入相同的明文数据经过相同的消息摘要算法才能得到相同的密文。消息摘要算法不存在密钥的管理与分发问题,适合于分布式网络上使用

消息摘要的主要特点有:

  • 无论输入的消息有多长,计算出来的消息摘要的长度总是固定的

  • 消息摘要看起来是“随机的”。这些数据看上去是胡乱的杂凑在一起的。

  • 只要输入的消息不同,对其进行摘要后产生的摘要消息也必不相同;但相同的输入必会产生相同的输出

  • 只能进行正向的消息摘要,而无法从摘要中恢复出任何消息,甚至根本就找不到任何与原信息相关的信息。

  • 虽然“碰撞”是肯定存在的,但好的摘要算法很难能从中找到“碰撞”。即无法找到两条不同消息,但是它们的摘要相同。

常见的摘要算法:CRC、MD5、SHA等

一、用户注册是对密码进行加密

user.setPassword(MD5Utils.getSaltMD5(user.getPassword()));

getSaltMD5方法

/**
     * 加盐MD5算法
     * @param password
     * @return
     */
    public static String getSaltMD5(String password) throws Exception{
        StringBuilder sb = new StringBuilder(16); // 创建一个初始长度为16位的StringBuilder对象
        sb.append(random.nextInt(99999999)).append(random.nextInt(99999999)); // 将随机生成的随机数添加到StringBuilder对象中
        int len = sb.length(); 
        if(len < 18){ // 如果长度不够16位则补0
            int diffLen = 16 - len;
            for(int i = 0; i < diffLen; i ++){
                sb.append(0);
            }
        }
        String salt = sb.toString(); // 16为的StringBuilder转成字符串,该字符串即为盐
        password = md5Hex(password + salt);  // 将密码和盐进行字符串拼接后进行摘要算法得到加盐后的密码
        char[] cs = new char[48];
        for (int i = 0; i < 48; i += 3) {  
            cs[i] = password.charAt(i / 3 * 2);
            char c = salt.charAt(i / 3);
            cs[i + 1] = c;  // 将盐插入到加密后的密码变成最终的密码,盐所在的位置为1 4 7 10 13...
            cs[i + 2] = password.charAt(i / 3 * 2 + 1);
        }
        return String.valueOf(cs);
    }

加密的逻辑:

1)、随机生成16位的盐,

2)、将密码和盐进行字符串拼接后进行摘要算法得到加盐后的密码,

3)、最后再将16位的盐插入到加密后的密码中。

二、登录时对密码进行加盐后进行摘要算法并进行比较

if (MD5Utils.getSaltverifyMD5(password, user.getPassword()) && user.getUserStatus() == 2) {
                  ...
                }

getSaltVerifyMD5方法

/**
     * 验证加盐后是否和原文一致
     * @param password 原始密码
     * @param md5str md5后值
     * @return
     */
    public static boolean getSaltverifyMD5(String password, String md5str) throws Exception{
        char[] cs1 = new char[32];
        char[] cs2 = new char[16]; // 16位的盐
        for (int i = 0; i < 48; i += 3) {
            cs1[i / 3 * 2] = md5str.charAt(i);
            cs1[i / 3 * 2 + 1] = md5str.charAt(i + 2);
            cs2[i / 3] = md5str.charAt(i + 1); // 获取盐
        }
        String salt = new String(cs2);
        return md5Hex(password + salt).equals(String.valueOf(cs1)); // 将密码和盐进行字符串拼接后进行摘要算法,其中cs1为对密码进行加盐后的密码
    }

md5Hex方法:

/**
     * 使用Apache的Hex类实现Hex(16进制字符串和)和字节数组的互转
     * @param str 原明文字符串
     * @return 转换后的字符
     */
    private static String md5Hex(String str) throws Exception{
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] digest = md.digest(str.getBytes());  // 对密码和盐拼接后的字符串进行摘要digest算法,得到字节数组
        return new String(new Hex().encode(digest));  // 将字节数组转为字符串
    }

 密码比较的逻辑:

1)、对从数据库中获取的密码进行处理,得到加盐后的密码1和盐;

2)、将密码和盐进行拼接后进行摘要算法得到加密后的密码2;

3)、将加密后的密码2与从加盐后的密码1进行比较,从为判断密码是否正确;

三、将盐保存到数据库时的解决办法

上面的做法是将盐保存到密码中,我们也可以把盐保存到数据库中,如下所示:

 

posted on 2021-08-30 17:14  周文豪  阅读(364)  评论(0编辑  收藏  举报