登录模块->项目结构图
一、项目准备与数据库设计
1. 创建Spring Boot项目
先创建一个Maven项目:
项目结构图如上
2. 数据库设计
-- 创建数据库
CREATE DATABASE IF NOT EXISTS musicserver CHARACTER SET utf8;
USE musicserver;
-- 用户表
CREATE TABLE user (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(20) NOT NULL,
password VARCHAR(255) NOT NULL
);
-- 音乐表
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
);
-- 收藏表
CREATE TABLE lovemusic (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT(11) NOT NULL,
music_id INT(11) NOT NULL
);
二、项目配置
整体项目的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.6.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>musicplayer</artifactId>(自己项目名称)
<version>0.0.1-SNAPSHOT</version>
<name>musicplayer</name>(自己项目名称)
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mybatis 依赖 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</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>
<!-- 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>
<!-- 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>
</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>
application.properties配置(resources包下)
# 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/musicserver?characterEncoding=utf8&serverTimezone=UTC
spring.datasource.username=root(自己数据库)
spring.datasource.password=123456(自己数据库)
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# MyBatis配置
mybatis.mapper-locations=classpath:mybatis/**Mapper.xml
# 文件上传大小限制
spring.servlet.multipart.max-file-size=15MB
spring.servlet.multipart.max-request-size=100MB
# 日志配置
logging.level.com.example.musicserver.mapper=debug
三、登录模块实现
1. 用户实体类(model->User)
@Data
public class User {
private int id;
private String username;
private String password;
}
2. Mapper接口与XML
// UserMapper.java
@Mapper
public interface UserMapper {
User selectByName(String username);
}
<!-- UserMapper.xml -->
<select id="selectByName" resultType="com.example.musicserver.model.User">
SELECT * FROM user WHERE username=#{username}
</select>
3. 统一响应信息工具类(tools->ResponseBodyMessage)
@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;
}
}
4. 登录控制器(Controller层)
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserMapper userMapper;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@PostMapping("/login")
public ResponseBodyMessage<User> login(
@RequestParam String username,
@RequestParam String password,
HttpServletRequest request) {
User userInfo = userMapper.selectByName(username);
if (userInfo == null || !bCryptPasswordEncoder.matches(password, userInfo.getPassword())) {
return new ResponseBodyMessage<>(-1, "用户名或密码错误", null);
}
request.getSession().setAttribute(Constant.USERINFO_SESSION_KEY, userInfo);
return new ResponseBodyMessage<>(0, "登录成功", userInfo);
}
}
注解介绍:
@RestController:@ResponseBody+ @Controller合在⼀起的作⽤。@Controller注解,表明了这 个类是⼀个控制器类 ,@ResponseBody表⽰⽅法的返回值直接以指定的格式写⼊Http response body中 。
@RequestMapping:使⽤ @RequestMapping来映射请求,也就是通过它来指定控制器可以处理哪 些URL请求
@RequestParam:将请求参数绑定到你控制器的⽅法参数上 。
如果这个参数是⾮必传的可以写为: @RequestParam(required = false) ,默认是true.
5. 常量类(tools->Constant)
public class Constant {
public static final String USERINFO_SESSION_KEY = "USERINFO_SESSION_KEY";
}
6. BCrypt加密配置
BCrypt加密: 一种加盐的单向Hash,不可逆的加密算法,同一种明文(plaintext),每次加密后的密文都不一样,而且不可反向破解生成明文,破解难度很大。
MD5加密: 是不加盐的单向Hash,不可逆的加密算法,同一个密码经过hash的时候生成的是同一个hash值,在大多数的情况下,有些经过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>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
接下来就创建⼀个tools包,新建MD5Util类
package com.example.musicplayer.tools;
import org.apache.commons.codec.digest.DigestUtils;
/**
@Author hy
@Description:
*/
public class MD5Util {
private static final String salt = "1b2i3t4e";
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"),
"1b2i3t4e"));
System.out.println("对⽤⼾输⼊密码进⾏第2次加密:"+inputPassToDbPass("123456", "1b2i3t4e"));
}
}
创建⼀个tools包,新建BCryptTest类
package com.example.musicplayer.tools;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* @Author 12629
* @Date 2022/4/11 14:29
* @Description:
*/
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);
}
}
基于此我们来实现加密登录!
UserMapper新增逻辑
@Mapper
public interface UserMapper {
User login(User loginUser);
//username⽤⼾名是唯⼀的
User selectByName(String username);
}
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.musicserver.mapper.UserMapper">
<select id="login" resultType="com.example.musicserver.model.User">
select * from user where username=#{username} and password=#{password}
</select>
<select id="selectByName" resultType="com.example.musicserver.model.User">
select * from user where username=#{username}
</select>
</mapper>
修改UserController类
@RestController//@ResponseBody + @Controller合在⼀起的作⽤
@RequestMapping("/user")//使⽤ @RequestMapping 来映射请求,也就是通过它来指定控制器可
以处理哪些URL请求
public class UserController {
@Autowired
private UserMapper userMapper;
@Autowired//在⾃动装配之前,需要完成注⼊,我们再AppConfig中进⾏注⼊
private BCryptPasswordEncoder bCryptPasswordEncoder;
@RequestMapping("/login")
public ResponseBodyMessage<User> login(@RequestParam String username,
@RequestParam String password, HttpServletRequest request) {
User loginUser = new User();
loginUser.setUsername(username);
loginUser.setPassword(password);
System.out.println(loginUser);
User userInfo = userMapper.selectByName(username);
if(userInfo == null) {
return new ResponseBodyMessage<>(-1,"⽤⼾名或者密码错误",userInfo);
}else {
if(!bCryptPasswordEncoder.matches(password,userInfo.getPassword())) {
return new ResponseBodyMessage<>(-1,"⽤⼾名或者密码错
误",userInfo);
}
request.getSession().setAttribute(Constant.USERINFO_SESSION_KEY,userInfo);
return new ResponseBodyMessage<>(0,"登录成功",userInfo);
}
}
}
@Autowired:可以更准确地控制应该在何处以及如何进⾏⾃动装配。
此注解⽤于在setter⽅法,构造 函数,具有任意名称或多个参数的属性或⽅法上⾃动装配bean。默认情况下,它是类型驱动的注⼊
上面我们提到了在⾃动装配之前,需要完成注⼊,我们再AppConfig中进⾏注⼊
故创建包config,新建AppConfig类
@Configuration
public class AppConfig {
@Bean
public BCryptPasswordEncoder getBCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
7. 启动类配置
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
public class MusicserverApplication {
public static void main(String[] args) {
SpringApplication.run(MusicserverApplication.class, args);
}
}
四、密码加密原理
BCrypt vs MD5对比:
特性 | BCrypt | MD5 |
---|---|---|
安全性 | 高(每次加密结果不同) | 中(彩虹表可破解) |
密文长度 | 60位 | 32位 |
盐值处理 | 自动生成随机盐 | 需手动加盐 |
破解难度 | 极高 | 中等 |
适用场景 | 高安全需求(如金融) | 一般安全需求 |
五、功能测试
登录测试:
使用Postman发送POST请求
URL:
http://localhost:8080/user/login
参数:
username=bit&password=123456
成功响应:
{ "status": 0, "message": "登录成功", "data": { "id": , "username": "", "password": "" } }
这是我自己成功的数据
这是错误的数据,因为数据库就没有这组
所有内容在:Jared/音乐服务器 - 码云