熊熊出没 发表于 2025-3-2 09:04:49

本地音乐服务器(一)

 1. 准备工作

1.1 项目创建

https://i-blog.csdnimg.cn/direct/d73f969e3f4042b38188c8fe24d0a124.png
修改当前的pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.7.17</version>
      <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>musicServer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>musicServer</name>
    <description>musicServer</description>
    <url/>
    <licenses>
      <license/>
    </licenses>
    <developers>
      <developer/>
    </developers>
    <scm>
      <connection/>
      <developerConnection/>
      <tag/>
      <url/>
    </scm>
    <properties>
      <java.version>8</java.version>
    </properties>
    <dependencies>
      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
      </dependency>

      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
      </dependency>
      <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
      </dependency>
      <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
      </dependency>
      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
      </dependency>
      <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.3.1</version>
      </dependency>
    </dependencies>

    <build>
      <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                  <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                  </excludes>
                </configuration>
            </plugin>
      </plugins>
    </build>

</project>
1.2  数据库设计

1.2.1 user表

(id,username,password)
1.2.2 music表

(id,title,singer歌手,time上传时间,url音乐存储位置,未来需要播放的时间需要请求的地点,userid这首歌是是谁人用户上传的)
1.2.3 lovemusic表

(id,userid,music_id)绑定关系的表,即某用户喜欢某音乐的关系;
三个表设计如下代码所示:
drop database if exists spring_musicserver;
create database if not exists spring_musicserver character set utf8;

use spring_musicserver;
drop table if exists user;
create table user (
    id int primary key auto_increment,
    username varchar(20) not null,
    password varchar(255) not null
);

drop table 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 int(11) not null
);

drop table if exists lovemusic;
create table lovemusic(
    id       int primary key auto_increment,
    user_idint(11) not null,
    music_id int(11) not null
);


1.3 举行properties配置

server.port=8089
#配置数据库
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/spring_musicserver?useUnicode=true&characterEncoding=UTF-8&userSSL=false&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=111111
spring.datasource.driver-class-name=com.mysql.cj.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
#配置spring日志调试模式是否开启
debug=true
#⽇志级别:trace,debug,info,warn,error
#基本⽇志
logging.level.root=INFO
logging.level.com.example.musicserver.mapper=debug
#扫描的包:druid.sql.Statement类和frank包
logging.level.druid.sql.Statement=DEBUG
logging.level.com.example=DEBUG 2. 登录模块设计

2.1创建User类

        创建model包,将实体类都存放到该包内里;
package com.example.spring_musicserver.model;

import lombok.Data;

/**
* @version 1.0
* @Author 上嘉路
* @Date 2024/11/13 11:21
* @注释
*/
@Data
public class User {
    private int id;
    private String username;
    private String password;
}
2.2创建接⼝UserMapper

        新建mapper包,新建UserMapper
@Mapper
public interface UserMapper {
    User login(User loginUser);
}
2.3 创建UserMapper.xml

        在resource⽬录下,新建mybatis⽂件夹,新建UserMapper.xml,下面代码是.xml文件的通用配置:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
      "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.spring_musicserver_1113.mapper.UserMapper">
    <select id="login" resultType="com.example.spring_musicserver_1113.model.User">
      select * from user where username=#{username} and password=#{password}
    </select>
</mapper> 3. 实现登录

3.1 登录的请求和响应设计

https://i-blog.csdnimg.cn/direct/89ea56e0f63f4b3aafd572653f2ddffb.png
        响应体设计字段表明:
1、状态码,为0代表乐成,负数代表失败
2、状态形貌信息,形貌此次请求乐成或者失败的缘故原由
3、返回的user范例的数据,请求乐成后,需要给前端的数据信息
对于数据库中的user表中数据添加乐成;
3.2 创建UserController类

         在controller包下,创建UserController类
package com.example.spring_musicserver_1113.controller;

/**
* @version 1.0
* @Author 作者名
* @Date 2024/11/13 14:48
* @注释
*/

import com.example.spring_musicserver_1113.mapper.UserMapper;
import com.example.spring_musicserver_1113.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
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;

    @RequestMapping("/login")
    public void login(@RequestParam String username,
                      @RequestParam String password) {
      User loginUser = new User();
      loginUser.setUsername(username);
      loginUser.setPassword(password);
      User user = userMapper.login(loginUser);
      if (user != null) {
            System.out.println("登录成功");
      } else {
            System.out.println("登录失败");
      }
    }
}
        向数据库中插入数据举行postman验证接口的精确性设计性:
https://i-blog.csdnimg.cn/direct/259cb382c516432ab89bca5ae834de32.png
 https://i-blog.csdnimg.cn/direct/7603622f194d4da690702cc29e2bb9b5.png
https://i-blog.csdnimg.cn/direct/7f7d3fd4b2184dd481693bb98ad5cace.png
        由此可知当前的代码畅通无阻; 
3.3 设计同一的的响应体类工具类

        将服务器向客户端给出的响应举行封装;
        新建tools工具包,新建ResponseBodyMessage类;
package com.example.spring_musicserver_1113.tools;

import lombok.Data;

/**
* @version 1.0
* @Author 作者名
* @Date 2024/11/13 18:40
* @注释
*/
@Data
public class ResponseBodyMessage <T> {
    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;
    }
}
对于usercontroller类举行更新:

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserMapper userMapper;

    @RequestMapping("/login")
    public ResponseBodyMessage<User> login(@RequestParam String username,
                                     @RequestParam String password) {
      User loginUser = new User();
      loginUser.setUsername(username);
      loginUser.setPassword(password);
      User user = userMapper.login(loginUser);
      if (user == null) {
            System.out.println("登录失败!");
            return new ResponseBodyMessage<>(-1,"登录失败",loginUser);
      } else {
            return new ResponseBodyMessage<>(0,"登录成功",loginUser);
      }
    }
}
使用postman举行测试:
https://i-blog.csdnimg.cn/direct/079b1e0940a7473f904de665bbc39f6b.png
        此时我们针对响应封装的使用乐成运行,根据响应可以看到,我们的数据容易泄露,且等登录乐成之后就需要把用户信息写到新创建的httpsession内里,修改代码如下所示:
        创建constant类,界说常量字符串:
public class Constant {
    public static final String USERINFO_SESSION_KEY = "USERINFO_SESSION_KEY";
}
        修改的usercontroller:
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserMapper userMapper;

    @RequestMapping("/login")
    public ResponseBodyMessage<User> login(@RequestParam String username,
                                     @RequestParam String password,
                                           HttpServletRequest request) {
      User loginUser = new User();
      loginUser.setUsername(username);
      loginUser.setPassword(password);
      User user = userMapper.login(loginUser);
      if (user == null) {
            System.out.println("登录失败!");
            return new ResponseBodyMessage<>(-1,"登录失败",loginUser);
      } else {
            //request.getSession().setAttribute("USERINFO_SESSION_KEY",user);
            //上述代码由于USERINFO_SESSION_KEY是字符串由于容易写错,所以
            //创建枚举类constant,来定义一个常量USERINFO_SESSION_KEY为一个字符串,这样就方便我们后期调用
      request.getSession().setAttribute(Constant.USERINFO_SESSION_KEY,user);
            return new ResponseBodyMessage<>(0,"登录成功",loginUser);
      }
    }
}
          HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的全部信息都封装在这个对象中,通过这个对象提供的方法,可以获得客户端请求的全部信息。该对象的getsession,即用于获取与当前请求关联的会话对象, 如果不存在会话对象(第一次登录),则创建一个新的会话。
        我们使用postman举行登录验证:
https://i-blog.csdnimg.cn/direct/df37d4f306e24a70b39a6ec4c004715f.png
3.4 md5加密使用

         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>         加盐加密的逻辑:
        第一次加盐加密:模仿前端对密码举行 使用,加盐(在原始密码中添加一些元素),加密(对加盐之后的二次密码举行加密);
        第二次加盐加密:对于第一次加盐加密的密码举行第二次使用;
        终极得到我们想要的加密字符串,在tools包下面新建MD5Util类,代码如下:
public class MD5Util {
    //定义⼀个固定的盐值

    private static final String salt = "1b2i3t4e";
    //进行加密的成员方法
    public static String md5(String src) {
      return DigestUtils.md5Hex(src);
    }

//第⼀次加密:模拟前端⾃⼰加密,然后传到后端
    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加密前端加密过的密码,传给后端进⾏第2次加密
   ⽤⼾数据库当中的盐值
      * @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"),
                        "1b2i3t4e"));
      System.out.println(" 对⽤⼾输⼊密码进⾏第2 次加 密: "+
                inputPassToDbPass("123456", "1b2i3t4e"));
    }
}
        运行结果如下:
https://i-blog.csdnimg.cn/direct/ba14b5c09fd34012bb2cc30b65117fbc.png
3.5 BCrypt加密设计

         Bcrypt就是⼀款加密⼯具,可以⽐较⽅便地实现数据的加密⼯作。你也可以简单明白为它内部⾃⼰实 现了随机加盐处置惩罚。我们使⽤MD5加密,每次加密后的密⽂其实都是⼀样的,这样就⽅便了MD5通过 ⼤数据的⽅式进⾏破解。Bcrypt⽣成的密⽂是60位的。⽽MD5的是32位的。Bcrypt破解难度更⼤。
        添加依靠:
<!-- 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> 在springboot启动类添加相关的代码:
@SpringBootApplication(exclude =
      {org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})
public class SpringMusicserver1113Application {

    public static void main(String[] args) {
      SpringApplication.run(SpringMusicserver1113Application.class, args);
    }

} bcrypt加密测试代码如下:
public class BcryptTest {
    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加密:⼀种加盐的单向Hash,不可逆的加密算法,同⼀种明⽂(plaintext),每次加密后的 密⽂都不⼀样,⽽且不可反向破解⽣成明⽂,破解难度很⼤。
        MD5加密:是不加盐的单向Hash,不可逆的加密算法,同⼀个密码经过hash的时间⽣成的是同⼀个 hash值,在⼤多数的情况下,有些经过md5加密的⽅法将会被破解。
         Bcrypt⽣成的密⽂是60位的。⽽MD5的是32位的。
        ⽬前,MD5和BCrypt⽐较流⾏。相对来说,BCrypt⽐MD5更安全,但加密更慢。 虽然BCrpyt也是输⼊的字符串+盐,但是与MD5+盐的重要区别是:每次加的盐差别,导致每次⽣成的 结果也不类似。⽆法⽐对!
3.6 加密登录实现 

3.6.1 数据库插⼊数据

        将上述BCryptTest运⾏⽣成的对123456加密后的结果,插⼊到数据库当中
https://i-blog.csdnimg.cn/direct/8394f322e2d1480fa32993037f376ba2.png
3.6.2 UserMapper类新增⽅法 

@Mapper
public interface UserMapper {
    User login(User loginUser);

    User selectByName(String username);
} 3.6.3 UserMapper.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.spring_musicserver_1113.mapper.UserMapper">
    <select id="login" resultType="com.example.spring_musicserver_1113.model.User">
      select * from user where username=#{username} and password=#{password};
    </select>

    <select id="selectByName" resultType="com.example.spring_musicserver_1113.model.User">
      select * from user where username = #{username};
    </select>
</mapper> 3.6.4 修改UserController类

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserMapper userMapper;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @RequestMapping("/login")
    public ResponseBodyMessage<User> login(@RequestParam String username,
                                           @RequestParam String password,
                                           HttpServletRequest request) {
      User user = userMapper.selectByName(username);
      if (user == null) {
            System.out.println("登录失败!");
            return new ResponseBodyMessage<>(-1,"用户名或密码错误",user);
      } else {
            boolean flag = bCryptPasswordEncoder.matches(password,user.getPassword());
            if(!flag){
                return new ResponseBodyMessage<>(-1,"用户名或密码错误!",user);
            }
            request.getSession().setAttribute(Constant.USERINFO_SESSION_KEY,user);
            return new ResponseBodyMessage<>(0,"登录成功",user);
      }
    }
}
        我们这里将创建BCryptPasswordEncoder对象变成了注入该对象,以是就需要举行创建配置相对用类,然后使用注解来注入;
3.6.5 创建包config,新建AppConfig类

@Configuration
public class AppConfig {
    @Bean
    public BCryptPasswordEncoder getBCryptPasswordEncoder(){
      return new BCryptPasswordEncoder();
    }
}   注解介绍:
        @Configuration:表明当前类是⼀个配置类,被注解的类内部包罗有⼀个或多个被@Bean注解的⽅法,⽤于构建bean界说,初始化Spring容器。
         @Bean注解:⽤于告诉⽅法,产⽣⼀个Bean对象,然后这个Bean对象交给Spring管理。产⽣这个 Bean对象的⽅法Spring只会调⽤⼀次,随后这个Spring将会将这个Bean对象放在⾃⼰的IOC容器中。
        SpringIOC 容器管理⼀个或者多个bean,这些bean都需要在@Configuration注解下进⾏创建,在⼀ 个⽅法上使⽤@Bean注解就表明这个⽅法需要交给Spring进⾏管理。
3.6.6 spring-boot启动类注解

@SpringBootApplication(exclude =
      {org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})         当启动类,没有加这个过滤的时间,我们发现不能进⾏登录 ,来由如下:
        我们在pom文件内里添加依靠的时间,添加的是spring-security的依靠,但是spring-security是一个很大的框架,我们所使用的org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;(BCryptPasswordEncoder类是spring-security框架下的一个包内里的类,在整个大框架下我们仅仅使用BCryptPasswordEncoder这一个类),以是我们要添加这一行代码来禁止启动步伐扫描spring-security的默认路径,因为在SpringBoot中,默认的SpringSecurity是⽣效了的,此时的接⼝都是被保护的,我们需要通过验证才气正常的访问。此时通过上述配置,即可禁⽤默认的登录验证。
测试登录:
https://i-blog.csdnimg.cn/direct/e2391b9f781440adbda9371afdae6479.png
ps:未完待续

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 本地音乐服务器(一)