MyMusic 重点实现

该项目实现了用户注册和登录,其中密码使用BCrypt加密确保安全。MD5加密因易受彩虹表攻击而不安全,而BCrypt的加盐和随机性提供了更高的安全性。此外,项目还涉及MP3文件的格式校验,检查ID3V1和ID3V2标签来确认文件真实性。
摘要由CSDN通过智能技术生成

项目功能概述

项目主要实现了:

  1. 用户登录之后可以进行音乐的播放。通过拦截器,如果用户未登录,就不可访问相应的资源。
  2. 用户注册的时候,通过 BCrypt 加密,防止密码被破解。
  3. 可以对音乐进行喜欢,对于喜欢的音乐也可以进行删除。
  4. 管理员可以对音乐进行单个删除,也可以进行多个删除。
  5. 当音乐被删除之后,喜欢列表的音乐也会随之被删除。
  6. 可以对音乐进行搜索,也可以自己上传音乐。

普通用户

注册

普通用户在使用时,需要先进行注册,注册之后才可以登录使用。注册的时候通过 BCrypt 加密,保证用户密码的安全性:
在这里插入图片描述

登录之后的列表页面
在这里插入图片描述
因为是普通用户,所以不能删除音乐,只能喜欢、播放、搜索和添加音乐。

喜欢列表
在这里插入图片描述

搜索功能
在这里插入图片描述

添加音乐
在这里插入图片描述

管理员

注册
管理员可以通过管理员注册页面进行注册:
在这里插入图片描述

音乐列表
管理员可以对音乐进行单个删除和批量删除。
在这里插入图片描述

其它
管理员在其他方面的功能和普通用户是一样的。

注册密码加密

主要就是对注册密码进行加密,防止在登陆的时候,别人一抓包,就抓到我们的账号和密码。加密的时候,有 MD5 和 BCrypt 两种加密方式。

MD5 加密

MD5是一个安全的散列算法。

  1. 输入两个不同的明文不会得到相同的输出值,根据输出值,不能得到原始的明文,就是它的过程不可逆。
  2. 虽然说是不可逆,但也不是说就是安全的。因为自从出现彩虹表后,这样的密码也"不安全"。
  3. 彩虹表,就是一个庞大的,针对各种可能的字母组合预先计算好的哈希值的集合,不一定是针对MD5算法的,各种算法的都有。
  4. 有了彩虹表可以快速的破解各类密码。越是复杂的密码,需要的彩虹表就越大,现在主流的彩虹表都是100G以上。
  5. 而 MD5加密出来的密码是固定,比如: 123 在加密之后,为 dhadhadhad。只要有一个 “密码库” 记录了这一个组合,就可以根据 加密之后的密码,获取到 原本密码。
  6. 也就是说:彩虹表 天克 MD5 加密。

MD5 不安全的原因:

  1. 暴力攻击速度很快
  2. 字典表很大
  3. 容易碰撞

让 MD5 更安全的方法: 主要就是通过加盐或者使用长密码等算法,让整个加密字符串变得更长,破解时间变慢。如果破解密码时间足够长,就说明密码是安全的。

加盐的过程

  1. 添加 MD5 依赖:

    <!-- md5 依赖 -->
    <dependency>
    	<groupId>commons-codec</groupId>
    	<artifactId>commons-codec</artifactId>
    </dependency>
    <dependency>
    	<groupId>org.apache.commons</groupId>
    	<artifactId>commons-lang3</artifactId>
    	<version>3.9</version>
    </dependency>
    
  2. 创建一个 MD5 类:

    public class MD5Util {
        //定义一个固定的盐值
        private static final String salt = "Lockey123";
        public static String md5(String src) {
            return DigestUtils.md5Hex(src);
        }
        /**
         * 第一次加密 :模拟前端自己加密,然后传到后端
         * @param inputPass
         * @return
         */
        public static String inputPassToFormPass(String inputPass) {
            String str = ""+salt.charAt(1)+salt.charAt(3) + inputPass
                    +salt.charAt(5) + salt.charAt(6);
            return md5(str);
        }
        /**
         * 第2次MD5加密
         * @param formPass 前端加密过的密码,传给后端进行第2次加密
         * @param salt 用户数据库当中的盐值
         * @return
         */
        public static String formPassToDBPass(String formPass, String salt) {
            String str = ""+salt.charAt(0)+salt.charAt(2) + formPass +salt.charAt(5)
                    + salt.charAt(4);
            return md5(str);
        }
        /**
         * 上面两个函数合到一起进行调用
         * @param inputPass
         * @param saltDB
         * @return
         */
        public static String inputPassToDbPass(String inputPass, String saltDB) {
            String formPass = inputPassToFormPass(inputPass);
            String dbPass = formPassToDBPass(formPass, saltDB);
            return dbPass;
        }
        public static void main(String[] args) {
            System.out.println("对用户输入密码进行第1次加密:"+inputPassToFormPass("123456"));
            System.out.println("对用户输入密码进行第2次加密:"+formPassToDBPass(inputPassToFormPass("123456"),
                    "Lockey123"));
            System.out.println("对用户输入密码进行第2次加密:"+inputPassToDbPass("123456", "Lockey123"));
        }
    }
    

    进行两次加密是为了说明,MD5 的两次分开加密和一次加密两次的结果是一样的:
    在这里插入图片描述
    发现密码不一样,也就说明了加密的不安全性。

  3. 当每次都可以生成一个随机的盐值,就可以有效防止这样的问题的产生。所以也就用到了 BCrypt 加密。

BCrypt 加密

BCrypt 加密是一种比 MD5 刚安全的一种加密方式,也就是加的盐是随机的,这样的话,破解的时间成本就会大很多。

BCrypt 生成的密文是 60 位的,而 MD5 是 32 位的,所以 BCrypt 的破解难度更大。

  1. 导入依赖:

    <!-- security依赖包 (加密)-->
    <dependency>
    	<groupId>org.springframework.security</groupId>
    	<artifactId>spring-security-web</artifactId>
    </dependency>
    <dependency>
    	<groupId>org.springframework.security</groupId>
    	<artifactId>spring-security-config</artifactId>
    </dependency>
    
  2. 在 SpringBoot 启动类当中,加上一个 exclude :exclude = {org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class} 如果不加的话,会导致项目报错。

  3. 创建一个类来测试:

    public class BCryptUtil {
        public static void main(String[] args) {
            //模拟从前端获得的密码
            String password = "123456";
            BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
            String newPassword = bCryptPasswordEncoder.encode(password);
            System.out.println("加密的密码为: "+newPassword);
            //使用matches方法进行密码的校验
            boolean same_password_result = bCryptPasswordEncoder.matches(password,newPassword);
            //返回true
            System.out.println("加密的密码和正确密码对比结果: "+same_password_result);
            boolean other_password_result = bCryptPasswordEncoder.matches("987654",newPassword);
            //返回false
            System.out.println("加密的密码和错误的密码对比结果: " + other_password_result);
        }
    }
    

    BCrypt 的使用比 MD5 简单很多,encode 方法就直接完成了加密,加密的运行结果如下:
    在这里插入图片描述
    再加密一次,结果如下:
    在这里插入图片描述
    虽然原始密码一样,但是每次加密的结果都不一样,所以 BCrypt 更安全。也就是每次加密的时候,都是随即加盐处理。密码安全的原因就是,破解的成本比收益高,就说明密码是安全的。

两种加密的区别

BCrypt 加密:

是一种加盐的单向 Hash,不可逆的加密算法,同一种明文(plaintext),每次加密后的密文都不一样,而且不可反向破解生成明文,破解难度很大。

MD5 加密:

是不加盐的单向 Hash,不可逆的加密算法,同一个密码经过 hash 的时候生成的是同一个 hash 值,在大多数的情况下,有些经过 md5 加密的方法将会被破解。

目前来说,MD5 和 BCrypt 都比较流行。相对来说,BCrypt 比 MD5 更安全,但加密更慢。虽然 BCrpyt 也是输入的字符串+盐,但是与 MD5+盐 的主要区别是:每次加的盐不同,导致每次生成的结果也不相同。无法比对!

对 MP3 文件校验

主要就是对 MP3 后缀名的文件进行校验,看是不是真的 MP3 文件。

文件格式

MP3 文件大体分为三部分:TAG_V2(ID3V2),Frame, TAG_V1(ID3V1) :
在这里插入图片描述
也就是说,每个Frame都由帧头和数据部分组成。那么每个帧头的数据格式如下,如图片所示:使用字符 A 到 M 表示不同的区域。在表格中可以看到每一区域的详细内容(只截取部分,详细参考点这里

在这里插入图片描述

ID3V1 和 ID3V2

提到 ID3V1 和 ID3V2 就不得不提 MPEG 音频标签 。ID3V1 和 ID3V2 是 MPEG标签 的两种。

ID3V1

这种是存在于 文件尾部,长度是 128字节。用来描述 MPEG 音频文件,包含艺术家,标题,唱片集,发布年代和流派,还有额外的注释空间,位于音频文件的最后。固定为 128 字节。可以读取该文件的最后这 128 字节,来获得标签,结构如下:

AAABBBBB BBBBBBBB BBBBBBBB BBBBBBBB
BCCCCCCC CCCCCCCC CCCCCCCC CCCCCCCD
DDDDDDDD DDDDDDDD DDDDDDDD DDDDDEEE
EFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFG

在这里插入图片描述
所以看了上面那张表,是不是就可以认为:我们只需要查看文件尾部的128个字节里面,有没有 TAG 字样就可以了。事实上就是这样的,如果三个字节中有 “TAG” 字样(标记)。就说明它是一个 音频文件。反之,如果不包含,则说明它不是一个 音频文件。存放“ TAG ”字符,表示 ID3 V1.0 标准 ,根据是否包含这个字符,判断是不是音频文件。

ID3V2

是对 ID3V1 的一种扩展,存在文件头部,长度不定。所以就很难判断文件是否是音频文件。

  1. ID3V2 到现在一共有 4 个版本,但流行的播放软件一般只支持第 3 版,既 ID3v2.3。由于 ID3V1 记录在 MP3 文件的末尾。ID3V2 就只好记录在 MP3 文件的首部了(如果有一天发布 ID3V3,真不知道该记录在哪里)。
  2. 也正是由于这个原因。对 ID3V2 的操作比 ID3V1 要慢。而且 ID3V2 结构比 ID3V1 的结构要复杂得多,但比前者全面且可以伸缩和扩展。
  3. 每个 ID3V2.3 的标签都一个标签头和若干个标签帧或一个扩展标签头组成。关于曲目的信息如标题、作者等都存放在不同的标签帧中,扩展标签头和标签帧并不是必要的,但每个标签至少要有一个标签帧。标签头和标签帧一起顺序存放在 MP3 文件的首部。

参考资料:
https://www.cnblogs.com/ranson7zop/p/7655474.html
https://blog.csdn.net/sunshine1314/article/details/2514322

代码如下:

import lombok.Data;
import java.io.*;
import java.util.HashMap;

@Data
public class GetFileType {

    private static HashMap<String,String> fileHeadMap = new HashMap<>();

    static {
        //检测音频,分别是 两种的 ID3 类型
        fileHeadMap.put("494433030000","mp3");
        fileHeadMap.put("494433040000","MP3");
    }

    /**
     * 获取文件类型
     * @param filePath 文件路径
     * @return  文件类型
     */
    public static String getFileHead(String filePath)throws IOException {
        //获取文件头
        File file = new File(filePath);
        FileReader reader = new FileReader(file);
        BufferedReader br = new BufferedReader(reader);
        StringBuilder fileHead = new StringBuilder();
        for(int i = 0 ; i<=5;i++ ){
            int flag = br.read();
            if(Integer.toHexString(flag).length()==1){
                fileHead.append(0);
            }
            fileHead.append(Integer.toHexString(flag));
        }
        br.close();
        //查询文件类型
        return fileHeadMap.get(fileHead.toString());
    }

    public static void main(String[] args) throws IOException {
        System.out.println(getFileHead("D:/下载/Crush.mp3"));
    }
}

测试 MP3 文件:
在这里插入图片描述
运行结果如下:
在这里插入图片描述

测试改后缀名的文件:
在这里插入图片描述
运行结果如下:
在这里插入图片描述

  • 17
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lockey-s

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值