作者@ Autumn60
欢迎关注:点赞收藏✍️留言
目录
编辑
一、项目介绍
二、项目的创建
2.1、社区版本的IDEA项目的创建
三、数据库的设计
3.1、数据库表的设计
3.1.1、User表的设计
3.1.2、Music表的设计
1.3-LoveMusic表的设计
3.2、数据库的配置信息
四、实现登录的准备工作
4.1、创建User类
4.2、创建对应的Mapper接口和Controller
五、实现登录
5.1.实现登录请求和响应的设计
5.2.创建UserController 类
5.2.1、根据逻辑实现代码
5.2.2、UserMapper中的操作
5.2.3、数据库当中的操作;
5.2.4、验证功能是否正常
5.3、优化代码
5.3.1、将登录的用户写入到Session中
编辑5.3.2、验证优化后的代码是否成功
六、Bcrypt加密
6.1、注册功能的实现
编辑编辑 6.2、MD5加密
6.3、Bcrypt加密
6.3.1、Bcrypt加密第一种
6.3.2、第二种直接注入的方式调用;
6.3.3、验证注册加密功能
七、上传音乐模块
7.1、上传音乐的接口设计
7.2、创建MusicController类与对应的MusicMapper 编辑
7.3、在Mybatis下面创建对应的XML文件
7.4实现功能
7.5上传验证:
7.6如何判断上传的文件是mp3(功能扩展未写)
7.7实现数据库上传
1.实现Mapper的 与 对应 Mybatis 的设计
调用 启动!!编辑
测试 启动!!
Mapper类的实现 :
数据库的实现 :
Controller类的实现:
验证 :
八、播放音乐模块的设计
8.1请求与响应的设计
8.2、ResponseEntity类
8.3、实现对应的功能类
九、删音乐模块设计
9.1删除单个音乐
9.1.2、设计对应的Mapper和Mybatis;
9.2批量删除音乐
十、查询音乐模块的设计
10.1、请求与响应的设计
10.2、Mapper与XML的设计
10.3、实现具体类的功能
10.4、对实现的功能进行验证
十一、收藏音乐模块的实现
11.1、设计请求与响应
编辑11.2、设计对应的Mapper与XML
11.3、实现对应的功能;
11.4、验证 启动!
十二、移除喜欢的音乐模块设计
12.1、请求与响应的设计
12.2、对应Mapper与XML的设计
12.3、Controller具体功能的实现
一、Controller包的源码
1.LoveMusicController的实现;
2.MusicController类的实现
3.UserController类的实现
二、Mapper包的源码
1.LoveMusic的源码
2.MusicMapper
3.UserMapper
三、model包的源码
1.Music的实现
2.User实现
四、tools包的源码(工具包)有的不用
1.BcryptTest(加密类)
2.Constant(用来存储登录信息)
3.MD5Util加密
4.ResponseBodyMessage (返回响应体的设计)
五、启动类的源码
六、Mybatis的三个XML文件
1.LoveMusicMapper.xml
2.MusicMapper.xml
3.UserMapper.xml
七、配置项
至此结束
在实现整个项目中用的技术栈SpringBoot+Mybatis+Jquery+前端部分等
项目整体主要是实现的功能:
- 登录
- 查询
- 播放歌曲
- 删除
- 选中歌曲进行删除
- 喜欢
- 喜欢列表
- 添加歌曲
- 目前先实现这些功能,后面会根据项目人易用性进行功能的扩展
项目实现技术栈 :
- Spring Boot
- MyBatis
- 前端(HTML,CSS,JS,jQuery)
因为在3.2版本后面下载SpringBoot插件需要收费,所以明显白嫖更香一点;
创建项目 :
项目创建好之后的设置
在做这个项目的过程中,需要对数据库来进行设计,在整个项目的过程中,分为两个部分,
一个是客户端,对于Web项目来说这里也就是浏览器,浏览器会发起请求给后端(服务器),假设要登录,服务器传给数据库一个用户名 和 密码,然后数据库来进行查询的操作,把查询得到的结果返回给服务器来进行一个校验,账号和密码是否正确
根据需求来分析设计数据库的表和字段
既然要登录,就要有User表(用户表)
要听音乐就要有Music表
两者间的关系 :
一个用户可以收藏多个音乐,一个音乐也可以被多个用户听,或者收藏,多对多关系
想要实现对音乐的收藏就要有一张中间表,所以又创建一张表 LoveMusic表 (收藏表)
先创建一个用来存储的数据库
create database onlinemusic character set utf8;
用户例如:张三,李四,用户不一样就用ID字段来进行区分
-------------------------------------------------------------------------------------------------------------------------
use onlinemusic;
drop talbe if exists `user`;
create table user (
`id` int primary key auto_increment,
`username` varchar(20) not null,
`password` varchar(255) not null
);
id用来区分每首音乐
title则为歌名
singer为歌手
time上传歌曲的时间
url在哪里加载音乐并且听
userid用来连接用户表的外键
-------------------------------------------------------------------------------------------------------------------------
drop talbe if exists `music`;
create Table `music` (
`id` int primary key auto_increment,
`title` varchar(50) not null,
`singer` varchar(30) not null,
`time` varchar(13) not null,
`url` varchar(1000) not null,
`userid` varchar(11) not null
);
drop talbe if exists `lovemusic`;
create Table `lovemusic` (
`id` int primary key auto_increment,
`user_id` varchar(11) not null,
`music_id` varchar(11) not null
);
把配置信息添加到里面去
#数据库的配置,这里的数据库配置只是在本地的localhost上面配置的本地mysql数据库;
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/online_music? characterEncoding=utf8&serverTimezone=UTC #这里的online_music 是自己的数据库名称
spring.datasource.username=root #此处填写的是自己的数据库账户(默认是root)
spring.datasource.password= #此处填写的是自己的数据库密码
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#配置XML
mybatis.mapper-locations=classpath:mybatis/**Mapper.xml
#配置SpringBoot上传文件的大小,每个文件默认是15MB,单次最大不超过100MB
spring.servlet.multipart.max-file-size = 15MB
spring.servlet.multipart.max-request-size = 100MB
#配置SpringBoot 日志调试模式是否开启
debug=true
#设置日志的级别,以及打印sql语句
#日志级别 : trace,debug,info,warn,error
#基本日志
logging.level.root=INFO
logging.level.com.example.online_music.mapper=debug
#扫描的包 : druid.sql.Statement类和frank包
logging.level.druid.sql.Statement=DEBUG
logging.level.com.example=DEBUG
在根目录下面创建一个model 包,然后根据数据库User表来创建对应的类,并添加@data注解
下面配置信息中,com.example.online_music.mapper.UserMapper 这段代码为自己的路径
前段输入账号和密码后,传递给服务器,服务器再从数据库中进行查找,然后校验,最后再返回给前段,判断登录是否正常
先建包再建类
package com.example.online_music.controller;
import com.example.online_music.mapper.UserMapper;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
private UserMapper userMapper;
@RequestMapping("/login")
//当前端请求这个路径时传递过来两个参数一个是Username,一个是Password
public void login (@RequestParam String username,@RequestParam String password) {
//当创建好这个类后,拿到username 和 password 之后就能去数据库中进行查找了
}
}
当创建好类后,实现登录逻辑
先创建一个UserMapper 对象,通过写@Autowired注解来进行注入,然后创建User对象,对前端传递过来的 username 和 password 写入到 User类 当中对应属性中,通过创建好的UserMapper对象进行调用登录方法,进行对数据库的执行,执行的结果是User类型,所以在创建一个User对象来进行接收,接收之后进行if判断,如果 user 不为空 则 登录成功,否则登录失败(这里可以进行优化,可以把返回的结果写入到Session中,方便后续的调用);
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserMapper userMapper;
//用来标识 http 请求和地址与 RestController 类的方法之间的映射
@RequestMapping("/login")
public void login (@RequestParam String username,@RequestParam String password) {
User userLogin = new User();
//当请求过来的参数,就要传参给username 和 password然后传递给数据库来进行操作;
userLogin.setUsername(username);
userLogin.setPassword(password);
User user = userMapper.login(userLogin);
//如果查到用户了,返回的user中存储的是账户和密码,如果错误,则是null
if(user != null) {
System.out.println("登录成功");
//这里优化,如果登录成功之后可以把用户写入到session当中方便后面的调用
} else {
System.out.println("账户或密码错误");
}
}
}
对应的UserMapper,这里接受到对象loginUser 就是上面设置好账户密码的User对象然后执行数据库,进行响应操作;
因为数据库当中是没有任何一条账户信息的,所以可以手动插入一条,然后通过使用Postman来构造一个响应验证登录的操作
设计出完整的登录初步模块没问题
当请求之后,最好给前段返回一个响应,最好用josn来返回数据;,所以新建一个响应的类,有可能做任何操作都要有想应所以创建一个tools包,然后再创建一个响应的工具类
既然写好了响应的类,再返回到登录的实现类当中优化代码
优化点 : 另外,登录成功后,我们还需要创建一个会话。
就是下次用户自来访问的时候,就不用在登陆的。所以再创建一个变量写入到Session当中即可,创建好的对象,在任何地方都可以使用;
但是这种写法有个问题,那就是登录应该是和前端传递过来的密码来进行和账户进行对比,所以写一个根据账户在数据中查找密码的功能,查到了再决定是否登录
设计请求与响应部分
如果要写一个注册功能的话,也就是说: 用户输入username 和password 自动调用加密,然后把加密后的密码写入到数据库当中去,此处需要写一个判断,判断你的账户和密码中是否包含空格,如果包含空格,就不能注册,目前先写一个返回false即可,后面再进行优化;
总结:
1.根据用户输入过来的账户和密码,判断合法性
2.调用相对应的Mapper;
3.插入到数据库当中
完事具备,只欠写代码,直接在UserController再写一个注册的类;最后再写加密
/**
*
* TODO[未写完]在前端页面上面再补充一个跳转功能,注册成功之后跳转到登录页面
* 注册功能
* @param username
* @param password
* @return
*/
@RequestMapping("/enroll")
public ResponseBodyMessage enroll (@RequestParam String username,
@RequestParam String password) {
、//实现对应的注册功能,然后对密码进行加密
}
2.设计对应的Mapper与Mybatis;
我们传递过去的密码不能是这种形式的,这种一抓包就能知道账户和密码,所以就需要进行加密
那么,我们能不能对密码 进行 加密操作,让对方即使获取到我们的请求,也无法获取到重要信息。
我们先来了解 MD5 加密。
MD5是一个安全的散列算法。
输入两个不同的明文不会得到相同的输出值,根据输出值,不能得到原始的明文,即其过程不可逆; 但是虽然不可逆,但是不是说就是安全的。因为自从出现彩虹表后,这样的密码也"不安全"。彩虹表:
彩虹表就是一个庞大的、针对各种可能的字母组合预先计算好的哈希值的集合,不一定是针对MD5算法的,各种算法的都有。
有了它可以快速的破解各类密码。越是复杂的密码,需要的彩虹表就越大,现在主流的彩虹表都是100G以上。
而 MD5加密出来的密码是固定,比如: 123 在加密之后,为 dhadhadhad。
只要有一个“密码库”记录了这一个组合,就可以根据 加密之后的密码,获取到 原本密码。
所以说:彩虹表 天克 MD5 加密。不安全的原因:
1、 暴力攻击速度很快
2、 字典表很大
3、 碰撞
参考链接:https://md5.cc/news1.aspx
更安全的做法是加盐或者长密码等做法,让整个加密的字符串变的更长,破解时间变慢。密码学的应用安全,是建立在破解所要付出的成本远超出能得到的利益上的 。
比如 :现在有一个条数据支持1万块,但是破解起来需要3年的时间,等到破解的时候,这个数据恐怕已经不值钱了!这种费力不讨好的事情,人家不会去做。
需要添加依赖:
commons-codec
commons-codec
org.apache.commons
commons-lang3
3.9
2.Bcrypt加密:
在MD5加密的过的密码上面,再此进行添加盐值,进行加密
首先先定义一个盐值,然后随机抽取盐值和传递过来的密码来进行拼接加密,前端加密了一次,传递给后端后,后端拿到加密后的值,再根据自定义的盐值来进行二次加密,就会得到加密后的密码
package com.example.online_music.tools;
import org.apache.commons.codec.digest.DigestUtils;
public class MD5Util {
//定义一个固定的盐值加密(可以自定义)
private static final String salt = "1b2i3t4e";
public static String md5(String src) {
//传入一个 src 来进行_MD5加密
return DigestUtils.md5Hex(src);
}
/**
* 第一次加密, 模拟前段自己加密,然后传递到后端
* 此处为模拟前端加密
* 把自己定义的盐值,和用户在前端输入的密码,任意抽取盐值的下标,和盐值拼接起来进行加密
* @param inputpass 前端输入的密码
* @return
*/
public static String inputPassToFormPass(String inputpass) {
//此处的salt是自定义的盐值,来取出自定义盐值的下标,任意抽取盐值的下标,和盐值拼接起来进行加密
String str ="" + salt.charAt(1) + salt.charAt(3) + inputpass
+salt.charAt(5) + salt.charAt(6);
return md5(str);
}
/**
* 第二次 md5加密
* 此处为模拟后端加密
* 也就是模拟在传递到后端后,把此刻传递过来的str再此随机抽取盐值,和str进拼接完成第二次加密
* @param formPass 前端加密过的密码(str),传给后端进行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("第一次进行加密: " + inputPassToFormPass("123456"));
System.out.println("第二次进行加密: " + formPassToDBPass(inputPassToFormPass("123456"),
"4e5r6v7f8n"));
System.out.println("一次性两次加密: " + inputPassToDbPass("123456","4e5r6v7f8n"));
}
}
不管运行多少次,这个密码是固定的。因为这里没有用随机盐值。当密码长度很大,盐值也是随机的情况下,密码 的强度也加大了。破解成本也增加了(下方会优化)
直接调用写好的Bcrypt类来进行加密
Bcrypt就是一款加密工具,可以比较方便地实现数据的加密工作。你也可以简单理解为它内部自己实现了随机加盐 处理 。我们使用MD5加密,每次加密后的密文其实都是一样的,这样就方便了MD5通过大数据的方式进行破解。Bcrypt生成的密文是60位的。而MD5的是32位的。Bcrypt破解难度更大。
第一步添加依赖
org.springframework.security
spring-security-web
org.springframework.security
spring-security-config
在启动类注解中添加参数
@SpringBootApplication(exclude =
{org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})
两次都是对 123465 来进行加密,可以明显的看出来两次加密后得到的密码是不一样;
测试的代码类
package com.example.online_music.tools;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class BcryptTest {
public static void main(String[] args) {
//模拟从前端拿到密码
String password = "123456";
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
//通过调用 BCryptPasswordEncoder 类对象的 encode 方法来对密码进行加密;
String newpassword = bCryptPasswordEncoder.encode(password);
System.out.println(newpassword);
}
}
可以看出来每次加密后的值都是不一样的,那么他是如何实现数据库对比的呢
//此处可以通过 bCryptPasswordEncoder 类下的matches 方法来对原密码和加密后的密码进行对比; boolean same_password_result = bCryptPasswordEncoder.matches(password,newpassword); System.out.println("加密后的密码和原密码进行对比 :" + same_password_result); boolean other_password_result = bCryptPasswordEncoder.matches("987654",newpassword); System.out.println("错误的密码和原密码进行对比" + other_password_result);
实现注册功能的代码写入
/**
*
* TODO[未写完]在前端页面上面再补充一个跳转功能,注册成功之后跳转到登录页面
* 注册功能
* @param username
* @param password
* @return
*/
@RequestMapping("/enroll")
public ResponseBodyMessage enroll (@RequestParam String username,
@RequestParam String password) {
User userLogin = new User();
//拿到前端传递过来的数据之后,判断账户名和密码是否包含空格;
System.out.println("xxxxx");
if (username.contains(" ") || password.contains(" ")) {
System.out.println("注册失败,账户名或密码不能包含空格");
return new ResponseBodyMessage(-1,"注册失败,账户名或密码不能包含空格",false);
} else {
//对密码进行Bcrypt加密操作
//String newPassword = bcryptTest.encrypt(password);//调用工具类进行加密
String newPassword = bCryptPasswordEncoder.encode(password);//写入Bean对象,直接调用类,来进行加密
//这里如果不在配置类中添加Bean对象注入的话,会造成空指针异常的问题
//设置账户名和密码
userLogin.setUsername(username);
userLogin.setPassword(newPassword);
//2.调用对应的Mapper来执行对数据库的操作;因为insert 操作返回的是整形(也就是插入成功,共修改1行数据)所以判断即可;
int ret = userMapper.enroll(userLogin);
//调用完后注册成功与否的判断
if(ret == 1 ) {
System.out.println("注册成功");
return new ResponseBodyMessage(0,"注册成功",true);
}
System.out.println("注册失败!");
return new ResponseBodyMessage(-1,"注册失败",true);
}
}
第二种写法如果不在配置类中添加Bean对象注入的话,会造成空指针异常的问题
所以需要使用这个参数过滤掉其他不用的东西的,如果不过滤掉,默认的 Spring-Security 是生效了的,此时的接口都是被保护起来的,我们需要通过验证才能正常的访问,通过上述的配置,就可以禁用默认的登录验证
所以在创建一个包
前端
再看数据库当中
可以看到注册成功了
总结:
1. 密码学的应用安全,是建立在破解所要付出的成本远超出能得到的利益上的 。
2. 使用BCrypt相比于MD5加密更好的一点在于,破解的难度上加大。
3. BCrypt的破解成本增加了,导致系统的运行成本也会大大的增加 。
4. 回到本质的问题,你的数据库中的数据价值如何?如果你是银行类型的,那么使用BCrypt是不错的,一般情 况使用MD5加盐,已经够用了。
两者的区别 :
1.BCrypt加密:
一种加盐的单向Hash,不可逆的加密算法,同一种明文(plaintext),每次加密后的密文都不一 样,而且不可反向破解生成明文,破解难度很大。
2.MD5加密:
是不加盐的单向Hash,不可逆的加密算法,同一个密码经过hash的时候生成的是同一个hash值,在 大多数的情况下,有些经过md5加密的方法将会被破解。
3.Bcrypt生成的密文是60位的。而MD5的是32位的。 目前,MD5和BCrypt比较流行。相对来说,BCrypt比MD5更安全,但加密更慢。
4.虽然BCrpyt也是输入的字符串 +盐,但是与MD5+盐的主要区别是:每次加的盐不同,导致每次生成的结果也不相同。无法比对!
上传音乐需要用到MultipartFile 类 ,在org.springframework.web.multipart包当中,是Spring框架中处理文件上传的主要类。
主要方法介绍:
实现服务器上传的功能
/**
* 上传文件
* 1.上传到服务器
* 2.上传到数据库
* @param file
* @param singer
* @param request
* @return
*/
@RequestMapping("/upload")
public ResponseBodyMessage upload(@RequestParam("filename") MultipartFile file,
@RequestParam String singer,
HttpServletRequest request) {
// TODO[未优化]上传必须登录,不登录可以听音乐,把登录注册和音乐列表页写在一个页面上面
//1.先判断是否登录
if(request.getSession() != null) {
System.out.println("已登录");
//2.通过_MultipartFile_类来获取前端上传的文件的信息;
String fileNameAndType = file.getOriginalFilename();//获取到上传文件的完整名;
//3.写一个自己想要上传的路径,和文件名进行拼接,判断路径的合法性
String path = SAVE_PATH + fileNameAndType; //将文件与路径拼接起来,就构成了上传文件的路径
File dest = new File(path);
//exists()判断路径是否存在
if(!dest.exists()) {
//mkdir()创建路径;
dest.mkdir();
}
//走到这说明此路径必然存在,所以现在只需要上传即可
//4.上传
try {
file.transferTo(dest);
return new ResponseBodyMessage(0,"服务器上传成功",false);
} catch (IOException e) {
e.printStackTrace();
return new ResponseBodyMessage(-1,"服务器上传失败",false);
}
} else {
System.out.println("请登录后上传");
return new ResponseBodyMessage(-1,"请登录后上传",false);
}
}
看一看到服务器端已经上传成功了
到这里,一个上传文件 的代码逻辑就出不多了。
注意!是差不多!就是还差一点才能算是完成。
因为我们还没有和数据库进行交互。
没有将数据存入到数据库中的 music 表里面!!!
这里存在着一个问题:我们怎么知道它上传的是一个 MP3 文件,万一,它上传错了呢?
上传了一个图片文件,那能当作音乐文件来播放嘛?
很显然是不行的!
那么,接下来,我们就来看看 如何去判断上传的文件是 MP3 格式的。
每个文件都有自己的构成方式(格式)MP3的结构里面就有一个
【不能通过后缀名判断,因为后缀是篡改的!】
我们判断的是文件本身的结构 和 组成。我不能说:你上传了一个图片文件,我把它当音频文件来播放吧?
这显然是不科学,也是不可能的。
由上图结构可知,每个Frame都由帧头和数据部分组成。我们来看每个帧头的数据格式
看完这张表,我们可不可以这样认为:
我们只需要查看 音频文件尾部的 128 个字节 的 前三个字节里面,有没有 TAG 字样 就行了?
如果前三个字节中有 “TAG” 字样(标记)。就说明它是一个 音频文件。
反之,如果不包含,则说明它不是一个 音频文件。
存放“ TAG ”字符,表示 ID3 V1.0 标准 ,根据是否包含这个字符,判断是不是音频文件,而 ID3V2的长度是不固定的,所以通过它是很难判断文件是否是音频文件。
而 ID3V2的长度是不固定的,所以通过它是很难判断文件是否是音频文件。
参考链接:
https://blog.csdn.net/ffjffjffjffjffj/article/details/99691239
https://www.cnblogs.com/ranson7zop/p/7655474.html
https://blog.csdn.net/sunshine1314/article/details/2514322
如果在上传数据库的时候,上传失败了,但是此时服务器当中已经上传成功了,这时候就要对服务器中的文件进行一个删除,保证上传失败,服务器和数据库中是空的情况,所以在正式实现之前,对保存文件的操作进行一个优化,
1.实现Mapper的 与 对应 Mybatis 的设计
再上传音乐类中实现相应的功能;
要上传就要获取对应的信息;这里的信息分别是 :
1. title(歌名) :前面上传服务器时
因为一个文件名包括两部分,一个是后缀名,一个前面的名称,只需要截取就可以获取当歌名了;
2. singer : 前端会传递过来一个歌手
3. time : 通过使用这个类(SimpleDateFormat)来设置 获取的时间格式;
这个类通过网上查即可,此处的 new Date 底层也是常规的 System.current这种来实现的;
4. url : 播放音乐的路径 ->
5. userid : 因为之前已经登录过了,所以直接通过创建好的变量来获取当前的登录信息并以User类型存储,通过 user.getId(),来直接获取用户id;
此时信息都有了万事俱备只欠东风
查看日志修改BUG,从日志中可以看出来SQL语句写错了,但是有个问题就是,咱们写SQL语句的初心是吧音乐信息添加到数据库当中去,所以SQL语句出错了,和添加到服务器当中去有什么关系
优化上传操作为一个事务;
在Mybatis的MusicMapper.XML中查找SQL语句中查找BUG
修改之后再次运行
但是这里有个逻辑问题,如果是同一首歌不同的人唱,是否可以上传呢,如果在上传的时候,数据库当中已经存在这首歌了,是否还要上传呢 ?既然想到了就实现对应的合法性判断;
播放音乐这里会用到几个类,File,Files(用来读取文件的信息
ResponseEntity对象,是Spring对请求响应的封装。
它继承了HttpEntity对象,包含了Http的响应码(httpstatus)、响应头(header)、响应体(body)三个部分。
ResponseEntity类继承自HttpEntity类,被用于Controller层方法 。ResponseEntity.ok 方法有2个方法,分别是有参数和没有参数。
/**
* 前段传递过来的是一个路径
* 整体逻辑
* 1.通过前端传递path的值
* @param path
* @return
*/
@RequestMapping("/get")
public ResponseEntity func(String path) throws IOException {
File file = new File(SAVE_PATH + path);
//通过给定的路径来读取指定的文件
byte[] a = Files.readAllBytes(file.toPath());
if(a == null) {
System.out.println("播放音乐失败");
//badRequest这个方法返回的状态码是400
return ResponseEntity.badRequest().build();
}
//如果走到这说明读取成功
System.out.println("播放音乐成功");
//此OK方法的状态码是200,而a在这里读取的mp3文件,以二进制输出;
return ResponseEntity.ok(a);
}
请求与响应的设计
在 MusicMapper 接口中定义两个方法。
1、deleteMusicById(根据歌曲id 删除 对应的音乐)
2、selectMusicById(根据歌曲id,查询对应的音乐信息)
Mybatis:
实现具体的功能
逻辑 : 既然是删除已经上传了的音乐,那么删除其实是分为两步的,第一步就是删除数据库当中的音乐,第二步就是删除服务器当中的音乐
第一步的实现逻辑 : 想要删除数据库中的音乐,这时候是根据音乐ID来进行删除的,而ID是用户给的,所以先创建一个类传递过来的参数是ID
第二步的逻辑实现 : 删除服务器中的音乐,怎么删除 ?
1.找到音乐名称 (因为上面进行数据库删除的时候,查询的返回结果Music对象接受,直接.title获取即可)
2.把已有上传到服务器的路径拿过来,进行拼接(也就是上传的时候的SAVE_PATH)
3.调用对应的Mapper删除
实现完整的删除类
/**
* 删除音乐的总逻辑
* 1.删除数据库
* 2.删除服务器
* @param id
* @return
*/
@RequestMapping("/delete")
public ResponseBodyMessage delete(@RequestParam String id) {
//1.删除数据库
int iid = Integer.parseInt(id);
Music music = musicMapper.selectMusicById(iid);
if(music == null) {
System.out.println("没找到你想要删除的音乐");
return new ResponseBodyMessage<>(-1,"没找到你想要删除的音乐",false);
} else {
//不为空直接把id传递过去进行删除即可
int ret = musicMapper.deleteMusicById(iid);
if(ret == 1) {
System.out.println("数据库删除成功");
//2.服务器删除
File file = new File(SAVE_PATH + music.getTitle() + ".mp3");
System.out.println("要删除音乐的文件绝对路径是 : " + file);
file.delete();
System.out.println("服务器删除成功");
return new ResponseBodyMessage<>(0,"删除成功",true);
} else {
System.out.println("数据库删除失败");
return new ResponseBodyMessage<>(-1,"删除失败",false);
}
}
}
测试启动!
批量删除,本质上是和单个删除没什么区别,只不过是把单个删除放入到循环当中了,id也是通过数组的方式来传递了
/**
* 批量进行删除
* 和单个删除没什么区别,不过是用数组存起来了,本质上还是一个一个删,用for循环即可解决;
* @param id
* @return
*/
@RequestMapping("/deleteSel")
public ResponseBodyMessage deleteSelMusic(@RequestParam("id[]") List id) {
System.out.println("所有的要删除的ID :" + id);
int sum = 0;//用来计数,删除1个加1,直到删完
for (int i = 0; i < id.size(); i++) {
int musicId = id.get(i);
Music music = musicMapper.selectMusicById(musicId);
if (music == null) {
System.out.println("没有个id的音乐");
return new ResponseBodyMessage<>(-1, "没有你想删除的歌", false);
}
int ret = musicMapper.deleteMusicById(musicId);
if (ret == 1) {
sum += ret;
//2.2删除服务器本身文件'
//获取文件名
System.out.println("数据库删除成功");
//第二种直接通过获取title获取id的文件名;
String filename = music.getTitle();
File file = new File(SAVE_PATH + filename + ".mp3");//路径删除需要手动写一个.MP3
System.out.println("当前的路径" + file.getPath());
} else {
System.out.println("数据库删除失败");
return new ResponseBodyMessage<>(-1,"数据库删除失败",false);
}
}
if(sum == id.size()) {
System.out.println("整体删除成功");
return new ResponseBodyMessage<>(0,"音乐删除成功",true);
} else {
System.out.println("整体删除失败");
return new ResponseBodyMessage<>(-1,"音乐删除失败",false);
}
}
此处查询需要满足几个功能:
1、 支持模糊查询
2、 支持传入参数为空
关于XML中sql语句有一个concat函数,可以参考以下连接;
https://blog.csdn.net/qq_34292479/article/details/86500185
/**
* 查询音乐
* @param musicName
* @return
*/
@RequestMapping("/findmusic")
//这里RequestParam 默认为 true,改为false后,传递参数可以为空;也就是不传递参数;
public ResponseBodyMessage> findMusic(@RequestParam(required = false) String musicName) {
List musicList = null;
if(musicName != null) {
System.out.println("根据歌名查询");
musicList = musicMapper.findMusicByName(musicName);
} else {
System.out.println("查询所有");
musicList = musicMapper.findMusic();
}
System.out.println("查询到的歌曲 :" + musicList);
System.out.println("Xxxxxxxxxx");
return new ResponseBodyMessage<>(0,"查询到所有的音乐",musicList);
}
到此为止,这个功能也就写结束了
我们需要注意一个个问题:
服务器在收到请求之后:
在添加音乐到喜欢列表中,需要先检测这首歌是否已经被收藏。
收藏过,就是不能再收藏了
反之,没收藏过,就可以进行收藏。
因为根据逻辑写,已经收藏过了,那么就进行取消收藏; 设计对应的Mapper与XML
这个类写好之后,回过头来继续来根据逻辑来写
至此收藏音乐模块已实现完毕
这里唯一需要注意的是:这移除的 lovemusic 里面的信息。
与 music 表 无关。
也就是说:删除 lovemusic 表中的信息,不会影响到 music表中的数据。但是删除music中,这里的lovemusic中会不会删除呢 ?所以要回到之前普通的musicController中设计删除模块;
所以退回到Music删除模块进行补充
在到对应的LoveMusic中写一个只根据音乐Id删除接口
这里的Mapper与XML用的其实上面上传功能时插入到里面一遍设计了,这里直接调用即可使用,也就是11.3目录的取消收藏功能;
和之前一样,根据用户id和音乐id来进行移除即可,这里就不讲了,直接删除即可
源码启动!
/** * 移出收藏 * @param id * @param request * @return */ @RequestMapping("/deletelovemusic") public ResponseBodyMessage
deleteLoveMusic(@RequestParam("id") String id, HttpServletRequest request) { int musicId = Integer.parseInt(id); //检查是否登录 HttpSession httpSession = request.getSession(false); if(httpSession == null || httpSession.getAttribute(Constant.USERINFO_SESSION_KEY) == null ) { System.out.println("未登录"); return new ResponseBodyMessage<>(-1,"请登录后操作",false); } User user = (User) httpSession.getAttribute(Constant.USERINFO_SESSION_KEY); int userId = user.getId(); int ret = loveMusicMapper.deleteLoveMusic(userId,musicId); if(ret == 1) { System.out.println("取消收藏成功"); return new ResponseBodyMessage<>(0, "取消收藏成功", true); } else { System.out.println("取消收藏失败"); return new ResponseBodyMessage<>(-1,"取消收藏失败", false); } }
验证启动
没什么问题,此模块也就实现了
package com.example.online_music.controller;
import com.example.online_music.mapper.LoveMusicMapper;
import com.example.online_music.model.Music;
import com.example.online_music.model.User;
import com.example.online_music.tools.Constant;
import com.example.online_music.tools.ResponseBodyMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@RestController
@RequestMapping("/loveMusic")
public class LoveMusicController {
@Autowired
private LoveMusicMapper loveMusicMapper;
@RequestMapping("/likeMusic")
public ResponseBodyMessage likeMusic(@RequestParam("id") String id,
HttpServletRequest request) {
int musicId = Integer.parseInt(id);
System.out.println("musicId : " + musicId);
//检查是否登录 TODO[不强制跳转]后面会做拦截
HttpSession httpSession = request.getSession(false);
if(httpSession == null || httpSession.getAttribute(Constant.USERINFO_SESSION_KEY) == null ) {
System.out.println("未登录");
return new ResponseBodyMessage<>(-1,"请登录后上传",false);
}
User user = (User) httpSession.getAttribute(Constant.USERINFO_SESSION_KEY);
int userId = user.getId();
System.out.println("userId : " + userId);
Music music = loveMusicMapper.findLoveMusicByMusicIdAndUserId(userId,musicId);
if(music != null) {
//之前收藏过了,那么点击后就取消收藏
System.out.println("因为之前收藏过,取消收藏了");
int ret = loveMusicMapper.deleteLoveMusic(userId,musicId);
if(ret == 0) {
System.out.println("取消失败");
} else {
return new ResponseBodyMessage<>(-1, "因为之前收藏过,取消收藏了", false);
}
}
//添加收藏
boolean effect = loveMusicMapper.insertLoveMusic(userId,musicId);
if(effect) {
return new ResponseBodyMessage<>(0,"收藏成功",true);
} else {
return new ResponseBodyMessage<>(-1,"收藏失败",false);
}
}
/**
* 移出收藏
* @param id
* @param request
* @return
*/
@RequestMapping("/deletelovemusic")
public ResponseBodyMessage deleteLoveMusic(@RequestParam("id") String id,
HttpServletRequest request) {
int musicId = Integer.parseInt(id);
//检查是否登录
HttpSession httpSession = request.getSession(false);
if(httpSession == null || httpSession.getAttribute(Constant.USERINFO_SESSION_KEY) == null ) {
System.out.println("未登录");
return new ResponseBodyMessage<>(-1,"请登录后操作",false);
}
User user = (User) httpSession.getAttribute(Constant.USERINFO_SESSION_KEY);
int userId = user.getId();
int ret = loveMusicMapper.deleteLoveMusic(userId,musicId);
if(ret == 1) {
System.out.println("取消收藏成功");
return new ResponseBodyMessage<>(0, "取消收藏成功", true);
} else {
System.out.println("取消收藏失败");
return new ResponseBodyMessage<>(-1,"取消收藏失败", false);
}
}
}
package com.example.online_music.controller;
import com.example.online_music.mapper.LoveMusicMapper;
import com.example.online_music.mapper.MusicMapper;
import com.example.online_music.model.Music;
import com.example.online_music.model.User;
import com.example.online_music.tools.Constant;
import com.example.online_music.tools.ResponseBodyMessage;
import org.apache.ibatis.binding.BindingException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
@RestController
@RequestMapping("/music")
public class MusicController {
@Value("${music.local.path}")
private String SAVE_PATH;
// private String SAVE_PATH = "C:/work/local/music1/";
@Autowired
private MusicMapper musicMapper;
@Autowired
private LoveMusicMapper loveMusicMapper;
/**
* 上传文件
* 1.上传到服务器
* 2.上传到数据库
* 如果能上传,说明已经登录过了,所以只需要获取当前的用户信息,存储到user当中,通过user.getId来获取当前userid;
*
* @param file
* @param singer
* @param request
* @return
*/
@RequestMapping("/upload")
public ResponseBodyMessage upload(@RequestParam("filename") MultipartFile file,
@RequestParam String singer,
HttpServletRequest request) {
// TODO[未优化]上传必须登录,不登录可以听音乐,把登录注册和音乐列表页写在一个页面上面
//1.先判断是否登录
//前面在登录的时候,登录成功的话会创建 Session, 并在里面设置用户属性
HttpSession session = request.getSession(false);
if (session != null && session.getAttribute(Constant.USERINFO_SESSION_KEY) != null) {
System.out.println("已登录");
//2.通过_MultipartFile_类来获取前端上传的文件的信息;
String fileNameAndType = file.getOriginalFilename();//获取到上传文件的完整名;
String title = fileNameAndType.substring(0, fileNameAndType.lastIndexOf("."));
Music music = musicMapper.select(title, singer);
if (music == null) {
//3.写一个自己想要上传的路径,和文件名进行拼接,判断路径的合法性
String path = SAVE_PATH + fileNameAndType; //将文件与路径拼接起来,就构成了上传文件的路径
File dest = new File(path);
//exists()判断路径是否存在
if (!dest.exists()) {
//mkdir()创建路径;
dest.mkdir();
}
//走到这说明此路径必然存在,所以现在只需要上传即可
//4.上传
try {
file.transferTo(dest);
//设置存储时间的格式
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
String time = sf.format(new Date());
String url = "/music/get?path=" + title;
User user = (User) session.getAttribute(Constant.USERINFO_SESSION_KEY);
int userid = user.getId();
//调用insert 插入
try {
int ret = 0;
ret = musicMapper.upLoad(title, singer, time, url, userid);
if (ret == 1) {
return new ResponseBodyMessage(0, "数据库上传成功", true);
}
} catch (BindingException e) {
//数据库上传失败之后会进行一个删除
dest.delete();
return new ResponseBodyMessage<>(-1, "数据库上传失败", false);
}
} catch (IOException e) {
e.printStackTrace();
return new ResponseBodyMessage(-1, "服务器上传失败", false);
}
}
return new ResponseBodyMessage(-1, "歌曲已存在", false);
} else {
System.out.println("请登录后上传");
return new ResponseBodyMessage(-1, "请登录后上传", false);
}
}
/**
* 前段传递过来的是一个路径
* 整体逻辑
* 1.通过前端传递path的值
* @param path
* @return
*/
@RequestMapping("/get")
public ResponseEntity func(String path) throws IOException {
File file = new File(SAVE_PATH + path);
//通过给定的路径来读取指定的文件
byte[] a = Files.readAllBytes(file.toPath());
if(a == null) {
System.out.println("播放音乐失败");
//badRequest这个方法返回的状态码是400
return ResponseEntity.badRequest().build();
}
//如果走到这说明读取成功
System.out.println("播放音乐成功");
//此OK方法的状态码是200,而a在这里读取的mp3文件,以二进制输出;
return ResponseEntity.ok(a);
}
/**
* 删除音乐的总逻辑
* 1.删除数据库
* 2.删除服务器
* @param id
* @return
*/
@RequestMapping("/delete")
public ResponseBodyMessage delete(@RequestParam String id) {
//1.删除数据库
int iid = Integer.parseInt(id);
Music music = musicMapper.selectMusicById(iid);
if(music == null) {
System.out.println("没找到你想要删除的音乐");
return new ResponseBodyMessage<>(-1,"没找到你想要删除的音乐",false);
} else {
//不为空直接把id传递过去进行删除即可
int ret = musicMapper.deleteMusicById(iid);
if(ret == 1) {
System.out.println("数据库删除成功");
//2.服务器删除
File file = new File(SAVE_PATH + music.getTitle() + ".mp3");
System.out.println("要删除音乐的文件绝对路径是 : " + file);
if(file.delete()) {
//同步删除loveMusic中的音乐
loveMusicMapper.deleteLoveMusicByMusicId(iid);
return new ResponseBodyMessage<>(0,"服务器文件删除成功",true);
} else {
return new ResponseBodyMessage<>(-1,"服务器文件删除失败",false);
}
} else {
System.out.println("数据库删除失败");
return new ResponseBodyMessage<>(-1,"删除失败",false);
}
}
}
/**
* 批量进行删除
* 和单个删除没什么区别,不过是用数组存起来了,本质上还是一个一个删,用for循环即可解决;
* @param id
* @return
*/
@RequestMapping("/deleteSel")
public ResponseBodyMessage deleteSelMusic(@RequestParam("id[]") List id) {
System.out.println("所有的要删除的ID :" + id);
int sum = 0;//用来计数,删除1个加1,直到删完
for (int i = 0; i < id.size(); i++) {
int musicId = id.get(i);
Music music = musicMapper.selectMusicById(musicId);
if (music == null) {
System.out.println("没有个id的音乐");
return new ResponseBodyMessage<>(-1, "没有你想删除的歌", false);
}
int ret = musicMapper.deleteMusicById(musicId);
if (ret == 1) {
sum += ret;
//2.2删除服务器本身文件'
//获取文件名
System.out.println("数据库删除成功");
//第二种直接通过获取title获取id的文件名;
String filename = music.getTitle();
File file = new File(SAVE_PATH + filename + ".mp3");//路径删除需要手动写一个.MP3
System.out.println("当前的路径" + file.getPath());
} else {
System.out.println("数据库删除失败");
return new ResponseBodyMessage<>(-1,"数据库删除失败",false);
}
}
if(sum == id.size()) {
System.out.println("整体删除成功");
return new ResponseBodyMessage<>(0,"音乐删除成功",true);
} else {
System.out.println("整体删除失败");
return new ResponseBodyMessage<>(-1,"音乐删除失败",false);
}
}
/**
* 查询音乐
* @param musicName
* @return
*/
@RequestMapping("/findmusic")
//这里RequestParam 默认为 true,改为false后,传递参数可以为空;也就是不传递参数;
public ResponseBodyMessage> findMusic(@RequestParam(required = false) String musicName) {
List musicList = null;
if(musicName != null) {
System.out.println("根据歌名查询");
musicList = musicMapper.findMusicByName(musicName);
} else {
System.out.println("查询所有");
musicList = musicMapper.findMusic();
}
System.out.println("查询到的歌曲 :" + musicList);
System.out.println("Xxxxxxxxxx");
return new ResponseBodyMessage<>(0,"查询到所有的音乐",musicList);
}
}
package com.example.online_music.controller;
import com.example.online_music.mapper.UserMapper;
import com.example.online_music.model.User;
import com.example.online_music.tools.BcryptTest;
import com.example.online_music.tools.Constant;
import com.example.online_music.tools.ResponseBodyMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserMapper userMapper;
//用来对密码进行加密操作;
private BcryptTest bcryptTest;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
//用来标识 http 请求和地址与 RestController 类的方法之间的映射
@RequestMapping("/login")
public ResponseBodyMessage login (@RequestParam String username, @RequestParam String password,
HttpServletRequest request) {
//直接根据前端传递过来的用户名查询对应的密码,判断即可
User user = userMapper.selectByName(username);
//如果查到用户了,返回的user中存储的是账户和密码,如果错误,则是null
if(user == null ) {
System.out.println("账户或密码错误");
return new ResponseBodyMessage(-1,"账号或密码错误",false);
} else {
boolean fig = bCryptPasswordEncoder.matches(password,user.getPassword());
if(!fig) {
System.out.println("密码错误,登录失败");
return new ResponseBodyMessage<>(-1,"用户名或密码错误",false);
}
System.out.println("登录成功");
request.getSession().setAttribute(Constant.USERINFO_SESSION_KEY,user);
return new ResponseBodyMessage(0,"登录成功",true);
}
}
/**
*
* TODO[未写完]在前端页面上面再补充一个跳转功能,注册成功之后跳转到登录页面
* 注册功能
* @param username
* @param password
* @return
*/
@RequestMapping("/enroll")
public ResponseBodyMessage enroll (@RequestParam String username,
@RequestParam String password) {
User userLogin = new User();
//拿到前端传递过来的数据之后,判断账户名和密码是否包含空格;
System.out.println("xxxxx");
if (username.contains(" ") || password.contains(" ")) {
System.out.println("注册失败,账户名或密码不能包含空格");
return new ResponseBodyMessage(-1,"注册失败,账户名或密码不能包含空格",false);
} else {
//对密码进行Bcrypt加密操作
//String newPassword = bcryptTest.encrypt(password);//调用工具类进行加密
String newPassword = bCryptPasswordEncoder.encode(password);//写入Bean对象,直接调用类,来进行加密
//这里如果不在配置类中添加Bean对象注入的话,会造成空指针异常的问题
//设置账户名和密码
userLogin.setUsername(username);
userLogin.setPassword(newPassword);
//2.调用对应的Mapper来执行对数据库的操作;因为insert 操作返回的是整形(也就是插入成功,共修改1行数据)所以判断即可;
int ret = userMapper.enroll(userLogin);
//调用完后注册成功与否的判断
if(ret == 1 ) {
System.out.println("注册成功");
return new ResponseBodyMessage(0,"注册成功",true);
}
System.out.println("注册失败!");
return new ResponseBodyMessage(-1,"注册失败",true);
}
}
}
package com.example.online_music.mapper;
import com.example.online_music.model.Music;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface LoveMusicMapper {
/**
* 查询喜欢的音乐
* @param userId
* @param musicId
* @return
*/
Music findLoveMusicByMusicIdAndUserId(int userId, int musicId);
/**
* 收藏音乐
* @param userId
* @param musicId
* @return
*/
boolean insertLoveMusic(int userId,int musicId);
/**
* 取消收藏
* @param userId 用户ID
* @param musicId 音乐Id
* @return 受影响的行数
*/
int deleteLoveMusic(int userId,int musicId);
/**
* 根据音乐ID来进行删除
* @param musicId
* @return
*/
int deleteLoveMusicByMusicId(int musicId);
}
package com.example.online_music.mapper;
import com.example.online_music.model.Music;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface MusicMapper {
int upLoad(String title, String singer, String time, String url,int userid);
Music select(String title,String singer);
//根据id来进行删除音乐
int deleteMusicById(@Param("id") Integer id);
//根据id来查询音乐
Music selectMusicById(@Param("id") Integer id);
//默认查询所有,所以用List存储
List findMusic();
//指定歌名来进行查询
List findMusicByName(String musicName);
}
package com.example.online_music.mapper;
import com.example.online_music.model.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper {
User login(User loginUser);
int enroll(User userLogin);
User selectByName(String username);
}
package com.example.online_music.model;
import lombok.Data;
@Data
public class Music {
private int id;
private String title;
private String singer;
private String time;
private String url;
private String userid;
}
package com.example.online_music.model;
import lombok.Data;
@Data
public class User {
private int id;
private String username;
private String password;
}
package com.example.online_music.tools;
import com.example.online_music.controller.UserController;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class BcryptTest {
//返回参数写成 String 类型,用来接收密码;
public static String encrypt(String password) {
//模拟从前端拿到密码
//此处的这个password 是默认的123456
//String password = "123456";
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
//通过调用 BCryptPasswordEncoder 类对象的 encode 方法来对密码进行加密;
String newpassword = bCryptPasswordEncoder.encode(password);
System.out.println("加密后的密码 : " + newpassword);
// //此处可以通过 bCryptPasswordEncoder 类下的matches 方法来对原密码和加密后的密码进行对比;
// boolean same_password_result = bCryptPasswordEncoder.matches(password,newpassword);
//
// System.out.println("加密后的密码和原密码进行对比 :" + same_password_result);
//
// boolean other_password_result = bCryptPasswordEncoder.matches("987654",newpassword);
//
// System.out.println("错误的密码和原密码进行对比" + other_password_result);
// //把加密后的密码进行返回;
return newpassword;
}
}
package com.example.online_music.tools;
public class Constant {
//用来存储登录信息用
public static final String USERINFO_SESSION_KEY="";
}
package com.example.online_music.tools;
import org.apache.commons.codec.digest.DigestUtils;
public class MD5Util {
//定义一个固定的盐值加密(可以自定义)
private static final String salt = "1b2i3t4e";
public static String md5(String src) {
//传入一个 src 来进行_MD5加密
return DigestUtils.md5Hex(src);
}
/**
* 第一次加密, 模拟前段自己加密,然后传递到后端
* 此处为模拟前端加密
* 把自己定义的盐值,和用户在前端输入的密码,任意抽取盐值的下标,和盐值拼接起来进行加密
* @param inputpass 前端输入的密码
* @return
*/
public static String inputPassToFormPass(String inputpass) {
//此处的salt是自定义的盐值,来取出自定义盐值的下标,任意抽取盐值的下标,和盐值拼接起来进行加密
String str ="" + salt.charAt(1) + salt.charAt(3) + inputpass
+salt.charAt(5) + salt.charAt(6);
return md5(str);
}
/**
* 第二次 md5加密
* 此处为模拟后端加密
* 也就是模拟在传递到后端后,把此刻传递过来的str再此随机抽取盐值,和str进拼接完成第二次加密
* @param formPass 前端加密过的密码(str),传给后端进行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("第一次进行加密: " + inputPassToFormPass("123456"));
System.out.println("第二次进行加密: " + formPassToDBPass(inputPassToFormPass("123456"),
"4e5r6v7f8n"));
System.out.println("一次性两次加密: " + inputPassToDbPass("123456","4e5r6v7f8n"));
}
}
package com.example.online_music.tools;
import lombok.Data;
@Data
//设置为泛型类,因为响应返回的可能是不同的数据类型
public class ResponseBodyMessage {
private int status; //返回的状态码
private String message;//返回给前端的信息(比如出错的信息,等等);
private T data; //返回给前端的数据;数据可能不同,所以设置泛型;
//通过右键构建出构造类
public ResponseBodyMessage(int status, String message, T data) {
this.status = status;
this.message = message;
this.data = data;
}
}
package com.example.online_music;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(exclude =
{org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})
public class online_MusicApplication {
public static void main(String[] args) {
SpringApplication.run(online_MusicApplication.class, args);
}
}
insert into lovemusic(user_id,music_id) values(#{userId},#{musicId});
delete from lovemusic where user_id=#{userId} and music_id=#{musicId};
delete from lovemusic where music_id=#{musicId};
insert into music(title,singer,time,url,userid)
values(#{title},#{singer},#{time},#{url},#{userid});
delete from music where id=#{id};
insert into user(username,password) values(#{username},#{password});
#数据库的配置,这里的数据库配置只是在本地的localhost上面配置的本地mysql数据库;
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/online_music?characterEncoding=utf8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#配置XML
mybatis.mapper-locations=classpath:mybatis/**Mapper.xml
#配置SpringBoot上传文件的大小,每个文件默认是15MB,单次最大不超过100MB
spring.servlet.multipart.max-file-size = 15MB
spring.servlet.multipart.max-request-size = 100MB
#存储音乐的路径
music.local.path= C:/work/local/music1/
#配置SpringBoot 日志调试模式是否开启
debug=true
#设置日志的级别,以及打印sql语句
#日志级别 : trace,debug,info,warn,error
#基本日志
logging.level.root=INFO
logging.level.com.example.online_music.mapper=debug
#扫描的包 : druid.sql.Statement类和frank包
logging.level.druid.sql.Statement=DEBUG
logging.level.com.example=DEBUG
SSM项目后端部分已经结束,后续会继续更新部署部分与前端部分的补齐;