本地音乐服务器(一)

打印 上一主题 下一主题

主题 883|帖子 883|积分 2649

 1. 准备工作

1.1 项目创建


修改当前的pom文件:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3.          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4.     <modelVersion>4.0.0</modelVersion>
  5.     <parent>
  6.         <groupId>org.springframework.boot</groupId>
  7.         <artifactId>spring-boot-starter-parent</artifactId>
  8.         <version>2.7.17</version>
  9.         <relativePath/> <!-- lookup parent from repository -->
  10.     </parent>
  11.     <groupId>com.example</groupId>
  12.     <artifactId>musicServer</artifactId>
  13.     <version>0.0.1-SNAPSHOT</version>
  14.     <name>musicServer</name>
  15.     <description>musicServer</description>
  16.     <url/>
  17.     <licenses>
  18.         <license/>
  19.     </licenses>
  20.     <developers>
  21.         <developer/>
  22.     </developers>
  23.     <scm>
  24.         <connection/>
  25.         <developerConnection/>
  26.         <tag/>
  27.         <url/>
  28.     </scm>
  29.     <properties>
  30.         <java.version>8</java.version>
  31.     </properties>
  32.     <dependencies>
  33.         <dependency>
  34.             <groupId>org.springframework.boot</groupId>
  35.             <artifactId>spring-boot-starter-web</artifactId>
  36.         </dependency>
  37.         <dependency>
  38.             <groupId>org.springframework.boot</groupId>
  39.             <artifactId>spring-boot-devtools</artifactId>
  40.             <scope>runtime</scope>
  41.             <optional>true</optional>
  42.         </dependency>
  43.         <dependency>
  44.             <groupId>com.mysql</groupId>
  45.             <artifactId>mysql-connector-j</artifactId>
  46.             <scope>runtime</scope>
  47.         </dependency>
  48.         <dependency>
  49.             <groupId>org.projectlombok</groupId>
  50.             <artifactId>lombok</artifactId>
  51.             <optional>true</optional>
  52.         </dependency>
  53.         <dependency>
  54.             <groupId>org.springframework.boot</groupId>
  55.             <artifactId>spring-boot-starter-test</artifactId>
  56.             <scope>test</scope>
  57.         </dependency>
  58.         <dependency>
  59.             <groupId>org.mybatis.spring.boot</groupId>
  60.             <artifactId>mybatis-spring-boot-starter</artifactId>
  61.             <version>2.3.1</version>
  62.         </dependency>  
  63.     </dependencies>
  64.     <build>
  65.         <plugins>
  66.             <plugin>
  67.                 <groupId>org.springframework.boot</groupId>
  68.                 <artifactId>spring-boot-maven-plugin</artifactId>
  69.                 <configuration>
  70.                     <excludes>
  71.                         <exclude>
  72.                             <groupId>org.projectlombok</groupId>
  73.                             <artifactId>lombok</artifactId>
  74.                         </exclude>
  75.                     </excludes>
  76.                 </configuration>
  77.             </plugin>
  78.         </plugins>
  79.     </build>
  80. </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)绑定关系的表,即某用户喜欢某音乐的关系;
三个表设计如下代码所示:
  1. drop database if exists spring_musicserver;
  2. create database if not exists spring_musicserver character set utf8;
  3. use spring_musicserver;
  4. drop table if exists user;
  5. create table user (
  6.     id int primary key auto_increment,
  7.     username varchar(20) not null,
  8.     password varchar(255) not null
  9. );
  10. drop table if exists music;
  11. create table music(
  12.     id int primary key auto_increment,
  13.     title varchar(50) not null,
  14.     singer varchar(30) not null,
  15.     time varchar(13) not null,
  16.     url varchar(1000) not null,
  17.     userid int(11) not null
  18. );
  19. drop table if exists lovemusic;
  20. create table lovemusic(
  21.     id       int primary key auto_increment,
  22.     user_id  int(11) not null,
  23.     music_id int(11) not null
  24. );
复制代码
1.3 举行properties配置

  1. server.port=8089
  2. #配置数据库
  3. spring.datasource.url=jdbc:mysql://127.0.0.1:3306/spring_musicserver?useUnicode=true&characterEncoding=UTF-8&userSSL=false&serverTimezone=GMT%2B8
  4. spring.datasource.username=root
  5. spring.datasource.password=111111
  6. spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
  7. #配置xml
  8. mybatis.mapper-locations=classpath:mybatis/**Mapper.xml
  9. #配置springboot上传⽂件的⼤⼩,默认每个⽂件的配置最⼤为15Mb,单次请求的⽂件的总数不能⼤于100Mb
  10. spring.servlet.multipart.max-file-size = 15MB
  11. spring.servlet.multipart.max-request-size=100MB
  12. #配置spring日志调试模式是否开启
  13. debug=true
  14. #⽇志级别:trace,debug,info,warn,error
  15. #基本⽇志
  16. logging.level.root=INFO
  17. logging.level.com.example.musicserver.mapper=debug
  18. #扫描的包:druid.sql.Statement类和frank包
  19. logging.level.druid.sql.Statement=DEBUG
  20. logging.level.com.example=DEBUG
复制代码
2. 登录模块设计

2.1创建User类

        创建model包,将实体类都存放到该包内里;
  1. package com.example.spring_musicserver.model;
  2. import lombok.Data;
  3. /**
  4. * @version 1.0
  5. * @Author 上嘉路
  6. * @Date 2024/11/13 11:21
  7. * @注释
  8. */
  9. @Data
  10. public class User {
  11.     private int id;
  12.     private String username;
  13.     private String password;
  14. }
复制代码
2.2创建接⼝UserMapper

        新建mapper包,新建UserMapper
  1. @Mapper
  2. public interface UserMapper {
  3.     User login(User loginUser);
  4. }
复制代码
2.3 创建UserMapper.xml

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

3.1 登录的请求和响应设计


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

         在controller包下,创建UserController类
  1. package com.example.spring_musicserver_1113.controller;
  2. /**
  3. * @version 1.0
  4. * @Author 作者名
  5. * @Date 2024/11/13 14:48
  6. * @注释
  7. */
  8. import com.example.spring_musicserver_1113.mapper.UserMapper;
  9. import com.example.spring_musicserver_1113.model.User;
  10. import org.springframework.beans.factory.annotation.Autowired;
  11. import org.springframework.stereotype.Controller;
  12. import org.springframework.web.bind.annotation.RequestMapping;
  13. import org.springframework.web.bind.annotation.RequestParam;
  14. import org.springframework.web.bind.annotation.RestController;
  15. import javax.servlet.http.HttpServletRequest;
  16. @RestController
  17. @RequestMapping("/user")
  18. public class UserController {
  19.     @Autowired
  20.     private UserMapper userMapper;
  21.     @RequestMapping("/login")
  22.     public void login(@RequestParam String username,
  23.                       @RequestParam String password) {
  24.         User loginUser = new User();
  25.         loginUser.setUsername(username);
  26.         loginUser.setPassword(password);
  27.         User user = userMapper.login(loginUser);
  28.         if (user != null) {
  29.             System.out.println("登录成功");
  30.         } else {
  31.             System.out.println("登录失败");
  32.         }
  33.     }
  34. }
复制代码
        向数据库中插入数据举行postman验证接口的精确性设计性:

 


        由此可知当前的代码畅通无阻; 
3.3 设计同一的的响应体类工具类

        将服务器向客户端给出的响应举行封装;
        新建tools工具包,新建ResponseBodyMessage类;
  1. package com.example.spring_musicserver_1113.tools;
  2. import lombok.Data;
  3. /**
  4. * @version 1.0
  5. * @Author 作者名
  6. * @Date 2024/11/13 18:40
  7. * @注释
  8. */
  9. @Data
  10. public class ResponseBodyMessage <T> {
  11.     private int status;//返回的状态码
  12.     private String message;//返回的信息?出错的原因
  13.     private T data;//返回给前端的数据
  14.     public ResponseBodyMessage(int status, String message, T data) {
  15.         this.status = status;
  16.         this.message = message;
  17.         this.data = data;
  18.     }
  19. }
复制代码
对于usercontroller类举行更新:
  1. @RestController
  2. @RequestMapping("/user")
  3. public class UserController {
  4.     @Autowired
  5.     private UserMapper userMapper;
  6.     @RequestMapping("/login")
  7.     public ResponseBodyMessage<User> login(@RequestParam String username,
  8.                                      @RequestParam String password) {
  9.         User loginUser = new User();
  10.         loginUser.setUsername(username);
  11.         loginUser.setPassword(password);
  12.         User user = userMapper.login(loginUser);
  13.         if (user == null) {
  14.             System.out.println("登录失败!");
  15.             return new ResponseBodyMessage<>(-1,"登录失败",loginUser);
  16.         } else {
  17.             return new ResponseBodyMessage<>(0,"登录成功",loginUser);
  18.         }
  19.     }
  20. }
复制代码
使用postman举行测试:

        此时我们针对响应封装的使用乐成运行,根据响应可以看到,我们的数据容易泄露,且等登录乐成之后就需要把用户信息写到新创建的httpsession内里,修改代码如下所示:
        创建constant类,界说常量字符串:
  1. public class Constant {
  2.     public static final String USERINFO_SESSION_KEY = "USERINFO_SESSION_KEY";
  3. }
复制代码
        修改的usercontroller:
  1. @RestController
  2. @RequestMapping("/user")
  3. public class UserController {
  4.     @Autowired
  5.     private UserMapper userMapper;
  6.     @RequestMapping("/login")
  7.     public ResponseBodyMessage<User> login(@RequestParam String username,
  8.                                      @RequestParam String password,
  9.                                            HttpServletRequest request) {
  10.         User loginUser = new User();
  11.         loginUser.setUsername(username);
  12.         loginUser.setPassword(password);
  13.         User user = userMapper.login(loginUser);
  14.         if (user == null) {
  15.             System.out.println("登录失败!");
  16.             return new ResponseBodyMessage<>(-1,"登录失败",loginUser);
  17.         } else {
  18.             //request.getSession().setAttribute("USERINFO_SESSION_KEY",user);
  19.             //上述代码由于USERINFO_SESSION_KEY是字符串由于容易写错,所以
  20.             //创建枚举类constant,来定义一个常量USERINFO_SESSION_KEY为一个字符串,这样就方便我们后期调用
  21.         request.getSession().setAttribute(Constant.USERINFO_SESSION_KEY,user);
  22.             return new ResponseBodyMessage<>(0,"登录成功",loginUser);
  23.         }
  24.     }
  25. }
复制代码
          HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的全部信息都封装在这个对象中,通过这个对象提供的方法,可以获得客户端请求的全部信息。该对象的getsession,即用于获取与当前请求关联的会话对象, 如果不存在会话对象(第一次登录),则创建一个新的会话。
        我们使用postman举行登录验证:

3.4 md5加密使用

         MD5是⼀个安全的散列算法,输⼊两个差别的明⽂不会得到类似的输出值,根据输出值,不能得到原 始的明⽂,即其过程不可逆;但是虽然不可逆,但是不是说就是安全的。因为⾃从出现彩虹表后,这 样的密码也"不安全"。
        更安全的做法是加盐或者⻓密码等做法,让整个加密的字符串变的更⻓,破解时间变慢。密码学的应 ⽤安全,是建⽴在破解所要付出的成本远超出能得到的利益上的。 这⾥我们使用加盐的做法:盐是在每个密码中加⼊⼀些单词来变成⼀个新的密码,存⼊数据库当中。 添加依靠:
  1. <!-- md5 依赖 -->
  2. <dependency>
  3. <groupId>commons-codec</groupId>
  4. <artifactId>commons-codec</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.apache.commons</groupId>
  8. <artifactId>commons-lang3</artifactId>
  9. <version>3.9</version>
  10. </dependency>
复制代码
        加盐加密的逻辑:
        第一次加盐加密:模仿前端对密码举行 使用,加盐(在原始密码中添加一些元素),加密(对加盐之后的二次密码举行加密);
        第二次加盐加密:对于第一次加盐加密的密码举行第二次使用;
        终极得到我们想要的加密字符串,在tools包下面新建MD5Util类,代码如下:
  1. public class MD5Util {
  2.     //定义⼀个固定的盐值
  3.     private static final String salt = "1b2i3t4e";
  4.     //进行加密的成员方法
  5.     public static String md5(String src) {
  6.         return DigestUtils.md5Hex(src);
  7.     }
  8. //第⼀次加密:模拟前端⾃⼰加密,然后传到后端
  9.     public static String inputPassToFormPass(String inputPass) {
  10.         String str = ""+salt.charAt(1)+salt.charAt(3) + inputPass
  11.         +salt.charAt(5) + salt.charAt(6);
  12.         return md5(str);
  13.     }
  14.     /**
  15.     *第2次MD5加密前端加密过的密码,传给后端进⾏第2次加密
  16.      ⽤⼾数据库当中的盐值
  17.       * @return
  18.       */
  19.     public static String formPassToDBPass(String formPass, String salt) {
  20.         String str = ""+salt.charAt(0)+salt.charAt(2) + formPass
  21.                     +salt.charAt(5) + salt.charAt(4);
  22.         return md5(str);
  23.     }
  24.     /**
  25.      *
  26.      上⾯两个函数合到⼀起进⾏调⽤
  27.      * @param inputPass
  28.      * @param saltDB
  29.      * @return
  30.      */
  31.     public static String inputPassToDbPass(String inputPass, String saltDB) {
  32.         String formPass = inputPassToFormPass(inputPass);
  33.         String dbPass = formPassToDBPass(formPass, saltDB);
  34.         return dbPass;
  35.     }
  36.     public static void main(String[] args) {
  37.         System.out.println(" 对⽤⼾输⼊密码进⾏第1次加密: "+inputPassToFormPass("123456"));
  38.                 System.out.println(" 对⽤⼾输⼊密码进⾏第2次加密:"+
  39.                         formPassToDBPass(inputPassToFormPass("123456"),
  40.                         "1b2i3t4e"));
  41.         System.out.println(" 对⽤⼾输⼊密码进⾏第2 次加 密: "+
  42.                 inputPassToDbPass("123456", "1b2i3t4e"));
  43.     }
  44. }
复制代码
        运行结果如下:

3.5 BCrypt加密设计

         Bcrypt就是⼀款加密⼯具,可以⽐较⽅便地实现数据的加密⼯作。你也可以简单明白为它内部⾃⼰实 现了随机加盐处置惩罚。我们使⽤MD5加密,每次加密后的密⽂其实都是⼀样的,这样就⽅便了MD5通过 ⼤数据的⽅式进⾏破解。Bcrypt⽣成的密⽂是60位的。⽽MD5的是32位的。Bcrypt破解难度更⼤。
        添加依靠:
  1.   <!-- security依赖包(加密)-->
  2.         <dependency>
  3.             <groupId>org.springframework.security</groupId>
  4.             <artifactId>spring-security-web</artifactId>
  5.         </dependency>
  6.         <dependency>
  7.             <groupId>org.springframework.security</groupId>
  8.             <artifactId>spring-security-config</artifactId>
  9.         </dependency>
复制代码
在springboot启动类添加相关的代码:
  1. @SpringBootApplication(exclude =
  2.         {org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})
  3. public class SpringMusicserver1113Application {
  4.     public static void main(String[] args) {
  5.         SpringApplication.run(SpringMusicserver1113Application.class, args);
  6.     }
  7. }
复制代码
bcrypt加密测试代码如下:
  1. public class BcryptTest {
  2.     public static void main(String[] args) {
  3.         //模拟从前端获得的密码
  4.         String password = "123456";
  5.         BCryptPasswordEncoder bCryptPasswordEncoder = new
  6.                 BCryptPasswordEncoder();
  7.         String newPassword = bCryptPasswordEncoder.encode(password);
  8.         System.out.println(" 加密的密码为: "+newPassword);
  9.         //使⽤matches⽅法进⾏密码的校验
  10.         boolean same_password_result =
  11.                 bCryptPasswordEncoder.matches(password,newPassword);
  12.         //返回true
  13.         System.out.println(" 加密的密码和正确密码对⽐结果: "+same_password_result);
  14.         boolean other_password_result =
  15.                 bCryptPasswordEncoder.matches("987654",newPassword);
  16.         //返回 false
  17.         System.out.println(" 加密的密码和错误的密码对⽐结果: " + other_password_result);
  18.     }
  19. }
复制代码
          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加密后的结果,插⼊到数据库当中

3.6.2 UserMapper类新增⽅法 

  1. @Mapper
  2. public interface UserMapper {
  3.     User login(User loginUser);
  4.     User selectByName(String username);
  5. }
复制代码
3.6.3 UserMapper.xml配置

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mapper namespace="com.example.spring_musicserver_1113.mapper.UserMapper">
  4.     <select id="login" resultType="com.example.spring_musicserver_1113.model.User">
  5.         select * from user where username=#{username} and password=#{password};
  6.     </select>
  7.     <select id="selectByName" resultType="com.example.spring_musicserver_1113.model.User">
  8.         select * from user where username = #{username};
  9.     </select>
  10. </mapper>
复制代码
3.6.4 修改UserController类

  1. @RestController
  2. @RequestMapping("/user")
  3. public class UserController {
  4.     @Autowired
  5.     private UserMapper userMapper;
  6.     @Autowired
  7.     private BCryptPasswordEncoder bCryptPasswordEncoder;
  8.     @RequestMapping("/login")
  9.     public ResponseBodyMessage<User> login(@RequestParam String username,
  10.                                            @RequestParam String password,
  11.                                            HttpServletRequest request) {
  12.         User user = userMapper.selectByName(username);
  13.         if (user == null) {
  14.             System.out.println("登录失败!");
  15.             return new ResponseBodyMessage<>(-1,"用户名或密码错误",user);
  16.         } else {
  17.             boolean flag = bCryptPasswordEncoder.matches(password,user.getPassword());
  18.             if(!flag){
  19.                 return new ResponseBodyMessage<>(-1,"用户名或密码错误!",user);
  20.             }
  21.             request.getSession().setAttribute(Constant.USERINFO_SESSION_KEY,user);
  22.             return new ResponseBodyMessage<>(0,"登录成功",user);
  23.         }
  24.     }
  25. }
复制代码
        我们这里将创建BCryptPasswordEncoder对象变成了注入该对象,以是就需要举行创建配置相对用类,然后使用注解来注入;
3.6.5 创建包config,新建AppConfig类

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

  1. @SpringBootApplication(exclude =
  2.         {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是⽣效了的,此时的接⼝都是被保护的,我们需要通过验证才气正常的访问。此时通过上述配置,即可禁⽤默认的登录验证。
测试登录:

ps:未完待续

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

熊熊出没

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表