当前位置: 首页 > news >正文

MyMusic 重点实现

MyMusic 重点实现

  • 项目功能概述
    • 普通用户
    • 管理员
  • 注册密码加密
    • MD5 加密
    • BCrypt 加密
    • 两种加密的区别
  • 对 MP3 文件校验
    • 文件格式
    • ID3V1 和 ID3V2
      • ID3V1
      • ID3V2

项目功能概述

项目主要实现了:

  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 文件:
在这里插入图片描述
运行结果如下:
在这里插入图片描述

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

相关文章:

  • 云计算敏捷团队的 10 个最佳实践工具
  • 阿里面试官终于把多年总结的Java八股文PDF版分享出来了,帮我金九银十拿下4个offer
  • java毕业设计超市管理系统Mybatis+系统+数据库+调试部署
  • 计算机网络 第 1 章 计算机网络概述
  • 如何转换图片格式?教你三招一键轻松转换图片格式
  • 【从小白到大白04】Linux基本权限
  • Unity_飞机大战_防止单例随场景销毁和跨场景两个物体脚本问题_自动加载物体挂载脚本的两种方式
  • taro 兼容支付宝小程序和微信小程序<七>-- 上传图片及图片转base64
  • 【附源码】计算机毕业设计SSM汽车交易平台
  • 购买域名-腾讯云
  • 【Linux练习生】Linux多线程
  • JavavEE中网络编程Socket套接字Ⅱ(TCP)
  • Intel汇编-函数使用堆栈传递数据
  • 【Android程序开发】常用布局--线性布局LinearLayout
  • 基于Dijkstra、A*和动态规划的移动机器人路径规划(Matlab代码实现)
  • 【许晓笛】 EOS 智能合约案例解析(3)
  • 【译】理解JavaScript:new 关键字
  • Android系统模拟器绘制实现概述
  • angular2 简述
  • Gradle 5.0 正式版发布
  • JavaScript 是如何工作的:WebRTC 和对等网络的机制!
  • Linux快速复制或删除大量小文件
  • nginx 负载服务器优化
  • Python_网络编程
  • SpiderData 2019年2月16日 DApp数据排行榜
  • SQLServer插入数据
  • Swift 中的尾递归和蹦床
  • Webpack入门之遇到的那些坑,系列示例Demo
  • 基于Vue2全家桶的移动端AppDEMO实现
  • 新书推荐|Windows黑客编程技术详解
  • 一加3T解锁OEM、刷入TWRP、第三方ROM以及ROOT
  • 云栖大讲堂Java基础入门(三)- 阿里巴巴Java开发手册介绍
  • 正则与JS中的正则
  • 主流的CSS水平和垂直居中技术大全
  • 如何通过报表单元格右键控制报表跳转到不同链接地址 ...
  • ​决定德拉瓦州地区版图的关键历史事件
  • #图像处理
  • (二)Eureka服务搭建,服务注册,服务发现
  • (一) springboot详细介绍
  • (原创)boost.property_tree解析xml的帮助类以及中文解析问题的解决
  • (转)负载均衡,回话保持,cookie
  • ../depcomp: line 571: exec: g++: not found
  • .bat批处理(一):@echo off
  • .net MySql
  • .NET的数据绑定
  • .net之微信企业号开发(一) 所使用的环境与工具以及准备工作
  • .w文件怎么转成html文件,使用pandoc进行Word与Markdown文件转化
  • /proc/interrupts 和 /proc/stat 查看中断的情况
  • @GlobalLock注解作用与原理解析
  • [100天算法】-不同路径 III(day 73)
  • [2669]2-2 Time类的定义
  • [acm算法学习] 后缀数组SA
  • [AIGC codze] Kafka 的 rebalance 机制
  • [BZOJ1053][HAOI2007]反素数ant
  • [BZOJ2281][SDOI2011]黑白棋(K-Nim博弈)