后端配置和调试
mysql
下载和配置
常用命令
登入: mysql -u root -p
查看数据库: show databases;
创建数据库: create database dbname;
删除数据库:drop database dbname;
使用数据库: use dbname;
创建表 create table tbname(setname typename , setname typename);
(查询当初创建表的sql语句: show create table tbname; )
列出当前数据库的所有表: show tables;
在表中插入数据: insert into tbname values( , , ……);
查询表中所有数据 select * from tbname;
删除某行数据 delete from tbname where ?;
删除表 drop table tbname;
更多命令
详见 mysql笔记
pom.xml (配置Spring Boot)
在pom.xml文件中添加依赖:
创建初加
- Spring Boot Starter JDBC
- Project Lombok
- MySQL Connector/J
- mybatis-plus-boot-starter
- mybatis-plus-generator
在Maven仓库地址中一个一个搜上述依赖
将最新版本的代码拷贝入pom.xml 的
点击右方maven 重新加载
等待下载(挂梯子都要很久 要么就考虑换源 反正人麻了)
作用解释:
spring-boot-starter-jdbc: 提供 JDBC 支持,简化数据库访问。lombok: 用于减少样板代码,自动生成 getters、setters、toString 等方法,简化代码书写。mysql-connector-j: MySQL 数据库的 JDBC 驱动,允许 Java 应用连接 MySQL 数据库。mybatis-plus-boot-starter: 为 MyBatis 提供增强功能,简化数据库操作。mybatis-plus-generator: 代码生成器,自动生成 MyBatis 的 Mapper、Entity 等类。spring-boot-starter-thymeleaf: 支持 Thymeleaf 模板引擎,用于生成动态网页。spring-boot-starter-web: 支持创建 RESTful API 和 Web 应用的基础组件。spring-boot-starter-test: 测试支持库,用于编写和运行测试。
因为被折磨了很久 又是换电脑开发后 jdk环境不支持 又是下载依赖时网络不佳 所以来了解一下这个.xml到底是怎么个事
这是当前阶段的pom.xml代码 (后面MyBatis有兼容问题 把springboot版本改成了3.11 然后又新引入
<?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>3.1.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zwnsyw</groupId>
<artifactId>backend</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>backend</name>
<description>backend</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jdbc -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>3.3.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>9.1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-generator -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<!-- MyBatis Core Dependency -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.16</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
用到再加
还有一些依赖包 后面用到了再导入 不然会引起不必要的麻烦
- spring-boot-starter-security
- jjwt-api
- jjwt-impl
- jjwt-jackson
作用解释:
spring-boot-starter-security
- 作用: 这是 Spring Boot 的安全启动器,提供了安全功能的基本配置。它集成了 Spring Security,可以帮助你快速实现安全认证和授权。
- 功能:
- 提供基本的身份验证和授权功能。
- 支持用户的角色管理和权限控制。
- 可配置各种安全策略,例如基于表单的登录、HTTP 基本认证等。
- 使用场景: 当你需要为 Spring Boot 应用添加安全机制时,使用这个依赖可以快速上手。
jjwt-api
- 作用: 这是 JJWT(Java JWT)库的 API 部分,专门用于处理 JSON Web Tokens (JWT) 的创建、解析和验证。
- 功能:
- 提供了一套简单的 API 来生成和验证 JWT。
- 支持设置自定义声明、签名算法等。
- 使用场景: 当你需要在应用中使用 JWT 进行身份认证和信息传递时,使用这个依赖可以简化实现。
jjwt-impl
- 作用: 这是 JJWT 的实现部分,提供实际的 JWT 生成和解析功能。
- 功能:
- 实现了
jjwt-api中定义的接口和功能。 - 处理 JWT 的签名、加密和解析。
- 实现了
- 使用场景: 需要使用 JWT 的完整功能时,
jjwt-impl是必需的。
jjwt-jackson
- 作用: 这是 JJWT 的 Jackson 支持库,允许你将 JWT 的 JSON 数据与 Jackson 序列化和反序列化结合使用。
- 功能:
- 提供了将 JWT 中的声明(claims)与 Java 对象之间的转换功能。
- 允许你使用 Jackson 的特性(如 JSON 处理)与 JWT 集成。
- 使用场景: 如果你的应用使用 Jackson 作为 JSON 处理库,并且需要在 JWT 中使用自定义对象,这个依赖会非常有用。
即:
spring-boot-starter-security用于添加安全功能。jjwt-api和jjwt-impl用于处理 JWT。jjwt-jackson用于将 JWT 与 Jackson 集成。
这些依赖库通常一起使用,帮助你在 Spring Boot 应用中实现安全认证机制,特别是使用 JWT 进行用户身份验证时。
思考与拓展
貌似有些包基本什么项目都会用得到 可以将常用的依赖包整合在一起 以后创建项目时直接把他们一起写入.xml中 而不用一个一个去官网复制
整合常用依赖
可以创建一个“基础模板”项目 包含所有常用依赖 未来新项目可以从这个模板复制 pom.xml
还有些推荐的常用依赖
- JUnit:
org.junit.jupiter:junit-jupiter - Springfox: 用于生成 Swagger 文档
- Logback: 日志处理库
- Redis:
spring-boot-starter-data-redis - Kafka:
spring-kafka
pom.xml 模板
包含了常用的 Spring Boot 项目依赖 这些依赖涵盖了数据库访问、模板引擎、安全、测试等功能 后续可以根据需要进行调整和扩展
<?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>3.3.4</version> <!-- Spring Boot 父项目版本 -->
<relativePath/>
</parent>
<groupId>com.example</groupId> <!-- 项目组织 ID -->
<artifactId>your-project-name</artifactId> <!-- 项目名称 -->
<version>0.0.1-SNAPSHOT</version> <!-- 项目版本 -->
<name>Your Project Name</name> <!-- 项目描述 -->
<description>Your Project Description</description> <!-- 项目描述 -->
<properties>
<java.version>17</java.version> <!-- 指定 Java 版本 -->
</properties>
<dependencies>
<!-- Spring Boot Web 启动器,支持创建 Web 应用 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot JDBC 启动器,简化数据库访问 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- Spring Boot Thymeleaf 启动器,支持 Thymeleaf 模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- Spring Boot 安全启动器,提供基本的安全配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- MySQL 数据库的 JDBC 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>9.1.0</version>
</dependency>
<!-- Project Lombok,减少样板代码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
<scope>provided</scope> <!-- 编译时提供 -->
</dependency>
<!-- MyBatis Plus 启动器,增强 MyBatis 功能 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.9</version>
</dependency>
<!-- MyBatis Plus 代码生成器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.9</version>
</dependency>
<!-- JJWT API,处理 JSON Web Tokens (JWT) -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<!-- JJWT 实现,提供实际的 JWT 生成和解析功能 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
</dependency>
<!-- JJWT Jackson 支持,将 JWT 的 JSON 数据与 Jackson 结合 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>
<!-- Spring Boot 测试启动器,提供测试支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <!-- 仅在测试时使用 -->
</dependency>
<!-- JUnit 测试库 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version>
<scope>test</scope> <!-- 仅在测试时使用 -->
</dependency>
<!-- Springfox,用于生成 Swagger 文档 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<!-- Logback 日志库 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<!-- Redis 启动器,支持 Redis 数据库 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Kafka 启动器,支持 Kafka 消息队列 -->
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Spring Boot Maven 插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在application.properties中添加数据库配置:
springboot要访问服务器其实也是要root+密码登入的 所以要进行一些配置
spring.datasource.username=root
spring.datasource.password=zw200495
spring.datasource.url=jdbc:mysql://localhost:3306/kob?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
四层模块
tips:
包如果成了xxx.xx导致逻辑不清晰 可以如上图设置 取消勾选压缩空的中间软件包
另外了解几个概念
SpringBoot中的常用模块
- pojo层:将数据库中的表对应成Java中的Class
- mapper层(也叫Dao层):将pojo层的class中的操作,映射成sql语句
- service层:写具体的业务逻辑,组合使用mapper中的操作
- controller层:负责请求转发,接受页面过来的参数,传给Service处理,接到返回值,再传给页面
在我们的项目中 一共分为四个页面
做成四个模块 pk、record、ranklist、user
这里我们主要先实现用户的登入功能 所以操作user包
不过可以把其他包也先建好
调试(以user为例)
先实现一下pojo层 将数据表转变成类
这个转变可以是用工具一键生成(MyBatisPlus) 也可以自己定义类后用Lombok注解 半自动的生成
pojo层
这里先实现一下半自动的方法
backend包下新建pojo包
pojo下新建一个user类 用于将kob中user表转变为class
既然是翻译 (将表翻译为class) 我们就来查看一下表的结构
由此得到这样一个类的结构
package com.zwnsyw.backend.pojo;
public class User {
private Integer id;
private String username;
private String password;
}
接下来就是要实现一些基本的操作 get、set 构造函数等等
但这种机械化的东西无需自己写 利用好lombok注解就能迅速解决
package com.zwnsyw.backend.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data //自动生成 getters, setters, toString, equals, hashcode
@NoArgsConstructor // 无参构造函数
@AllArgsConstructor // 有参构造函数
public class User {
private Integer id;
private String username;
private String password;
}
在target中存放着这几行代码编译后的结果 然后又通过反编译的形式展示给我们看
可以发现 什么构造 初始化一堆乱七八糟的接口 就这样有模有样的写好了 就只加了三个注解 神奇。。。
mapper 层
将类中的操作 映射成sql语句 写入mysql
同样 backend包下创一个mapper包 里面建一个UserMapper 接口 (注意是接口)
这里我们仅需加上一个@Mapper注解 标志这是一个Mapper接口
因为使用了MyBatis-Plus 本来增删改查需要写很多sql语句 但MyBatis-Plus帮我们做了这些事情 怎么把它做好的东西拿过来呢
很简单——继承
package com.zwnsyw.backend.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zwnsyw.backend.pojo.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper //标识这个接口是 MyBatis 的 Mapper
public interface UserMapper extends BaseMapper<User> {//继承 MyBatis-Plus 的 BaseMapper 接口 自动获得 CRUD 操作的实现
}
BaseMapper<User>: 通过继承 MyBatis-Plus 的BaseMapper接口,我们可以自动获得 CRUD 操作的实现,而不需要手动编写 SQL 语句。@Mapper: 注解标识这个接口是 MyBatis 的 Mapper,使得 Spring 能够自动扫描并实现这个接口。
如果不加@Mapper的话 可以在BackendApplication中加上一个MapperScan
controller层和service层
原本是还要写层service的 但现在只是调试 就把这俩功能都写在controller里面
在backend、controller、user下创建UserController类
加上注解@RestController
package com.zwnsyw.backend.controller.user;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
}
再加上ReqestMapping注解(或具体的 GetMapping PostMapping)
这里做个测试 处理get请求 在user目录下返回所有用户
具体的实现逻辑原本是要在service中实现 这里为了方便调试 直接写在controller层中 因为要获取用户 自然要调用mapper中实现的增删改查接口 要调用mapper 就得先定义出来 同时 配套的加上Autowired注解 另外 mapper中的增删改查接口是由MyBatis-plus生成的 所以一些api我们可能不熟悉参数 可以具体去MyBatis-plus官网查
package com.zwnsyw.backend.controller.user;
import com.zwnsyw.backend.mapper.UserMapper;
import com.zwnsyw.backend.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class UserController {
@Autowired
UserMapper userMapper;
@GetMapping("/user/all/")//在user目录下返回所有用户
public List<User> getAll(){
return userMapper.selectList(null);//查询所有数据
}
}
测试查询
查询某个用户的数据
需要加上注解@PathVariable
package com.zwnsyw.backend.controller.user;
import com.zwnsyw.backend.mapper.UserMapper;
import com.zwnsyw.backend.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class UserController {
@Autowired
UserMapper userMapper;
@GetMapping("/user/all/")
public List<User> getAll(){
return userMapper.selectList(null);
}
@GetMapping("/user/{userId}/")
public User getUser(@PathVariable int userId){
return userMapper.selectById(userId);
}
}
查询某个id的另一种写法
查询某个区间的数据
这些用法全可以在官网查api
可以发现全离不开@PathVariable 就好比vue中的占位符 等待传入替换
测试插入
其实真实的插入不是这样 简单利用PathVariable的特性 来实现一个插入数据
@GetMapping("/user/add/{userId}/{username}/{password}")
public String addUser(@PathVariable int userId, @PathVariable String username, @PathVariable String password){
User user = new User(userId, username, password);
userMapper.insert(user);
return "Add User Success";
}
测试删除
同样 利用PathVariable
//测试删除
@GetMapping("/user/delete/{userId}")
public String deleteUser(@PathVariable int userId){
userMapper.deleteById(userId);
return "delete User Success";
}
对四层的理解
首先pojo层将表转换为类 使得java能够进行操作 为一切工作做准备 然后对于前端传来的请求 交给controller进行处理 它只是接口 不做具体逻辑 把接受的请求传给service service中实现具体业务逻辑 配合mapper层实现的对数据库的增删改查操作 把查询的数据返回给controller再传给前端展示 把前端传来的修改 传给mapper 再进一步持久化到数据库
流程
- POJO 层:
- 将数据库的表映射为 Java 类,使得数据库中的数据可以被 Java 代码操作。这一层为其他所有层的工作提供了基础数据结构。
- Controller 层:
- 接收前端发送的请求,并作为接口处理。Controller 层的职责是接收请求参数,判断调用哪个 Service 层的方法,然后将结果返回给前端。
- 不处理具体的业务逻辑,只是作为请求的入口和响应的出口。
- Service 层:
- 处理具体的业务逻辑。接收 Controller 层传递过来的请求参数,根据业务需求调用 Mapper 层进行数据的查询、修改、删除或保存。
- 在 Service 层中实现业务逻辑,使得代码结构清晰且具有可维护性。
- Mapper 层(DAO 层):
- 负责与数据库进行交互。通过持久化框架(如 MyBatis 或 MyBatis-Plus),执行对数据库的 CRUD 操作。
- 这个层次只关心数据的存储和访问,不涉及业务逻辑。
- 请求与响应的流程:
- 前端请求被发送到 Controller。
- Controller 根据请求调用 Service 层的对应方法。
- Service 层结合业务逻辑进行操作,可能会调用 Mapper 层访问数据库。
- Mapper 层执行数据库的操作,将数据返回给 Service 层。
- Service 层将结果返回给 Controller。
- Controller 将结果数据返回给前端。
实现
在pojo层 主要是用lombok注解 来实现表到class的转换(构造、get\set)也可以直接用MyBatis-plus配合插件直接生成实体类 在mapper 主要是使用MyBatis和MyBatis-plus 来实现 类和表的交互 增删改查 在controller层使用这一系列http请求的注解 来完成和前端的交互 service层实现具体业务逻辑 调用mapper 对增删改查进行操作
POJO 层
- 作用: 表到 Java 类(实体类)的映射。POJO 层中的类与数据库中的表相对应,用于存储数据。
- 常用工具: Lombok 和 MyBatis-Plus。
- 实现方式:
- 使用 Lombok 注解(如
@Data、@NoArgsConstructor、@AllArgsConstructor)简化实体类的构建。 - 或者使用 MyBatis-Plus 代码生成器自动生成实体类。
- 使用 Lombok 注解(如
Mapper 层(DAO 层)
- 作用: 负责将 Java 对象与数据库的表进行交互,实现数据的持久化操作(增删改查)。
- 常用工具: MyBatis 和 MyBatis-Plus。
- 实现方式:
- 通过继承 MyBatis-Plus 的
BaseMapper,自动获取基本的 CRUD 操作。 - 可以根据业务需求,定义自定义的 SQL 查询、复杂条件查询、分页查询等。
- 通过继承 MyBatis-Plus 的
Service 层
- 作用: 负责处理业务逻辑,调用 Mapper 层进行数据的增删改查,并将结果返回给 Controller 层。
- 常用工具: Spring 提供的服务注解。
- 实现方式:
- 通过在 Service 层注入 Mapper 对象,使用 Mapper 对象的方法处理数据的增删改查。
- 编写业务逻辑代码,处理业务规则和流程。
- 使用
@Service注解标识服务类。 - 对于需要事务支持的业务操作,可以使用
@Transactional注解。
Controller 层
- 作用: 负责接收和处理来自前端(如浏览器、移动端)发来的 HTTP 请求,调用 Service 层执行具体的业务逻辑,并将结果返回给前端。
- 常用工具: Spring MVC 提供的注解(如
@RestController、@GetMapping、@PostMapping等)。 - 实现方式:
- 使用
@RestController定义控制器类,处理 RESTful API 请求。 - 使用
@GetMapping、@PostMapping、@PutMapping、@DeleteMapping等注解处理不同类型的 HTTP 请求。 - 使用
@RequestBody解析请求体中的 JSON 数据,将其转换为 Java 对象。 - 使用
@RequestParam和@PathVariable处理 URL 中的参数和路径变量。
- 使用
小结
- POJO 层 主要用于定义数据结构,使用 Lombok 注解简化类的创建,或者使用 MyBatis-Plus 自动生成。
- Mapper 层 负责数据的持久化,通过 MyBatis-Plus 简化数据库操作。
- Service 层 实现业务逻辑,负责与 Mapper 层交互,并且可以处理事务。
- Controller 层 负责处理 HTTP 请求,调用 Service 层完成业务逻辑,并返回结果。
进一步的拓展
- 如果有更复杂的需求,可以在 Service 层编写更复杂的业务逻辑。
- 在 Controller 层,可以使用
@ExceptionHandler等注解来处理全局异常。 - 可以在 Mapper 层使用自定义 SQL 语句来完成复杂的数据库操作。
认证操作
很多操作需要登入成功之后才能访问 所以得加上一个授权机制
springboot中也有实现的 我们给他引入 继承下来
引入依赖和调试
引入依赖包 spring-boot-starter-security Maven仓库地址
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<!-- <version>3.3.5</version>-->
</dependency>
建议去掉所有 Spring Boot 依赖的 <version> 标签,这样所有的依赖版本都会自动继承父项目中的版本,保持一致性
如果父版本较低 而依赖版本较高 就会出现非常卡顿的情况(前面的jdbc也可以去掉版本号)
去版本号之前 进入login卡十多秒 退出login卡十多秒 且渲染都不完全
去版本号之后 飞快
言归正传 加上这个依赖后
刷新页面发现 之前的所有页面都进不去了(只有login和logout是可以访问的) 看上去是全都重定向在了/login 要求登入
默认的账号是user 密码在每次构建时都会重新生成
验证完了 才能正常访问其他页面
如果登出的话 这些页面就又不能访问了
实现它的基本原理:后端配置和调试
与数据库对接
现在登入只能用默认的用户名 和 随机生成的密码
怎么让它对接我们的数据库
需要配置一下security
实现service.impl.UserDetailsServiceImpl类
在backend包下创建service包 service包下创建impl包 在impl包里创建UserDetailsServiceImpl类
继承自UserDetailsService接口,用来接入数据库信息
(tips: Alt+insert 快速查找该接口的所有实现方法)
使用快捷方式实现loadUserByUsername方法
得到以下代码 加上一个@Service标明
package com.zwnsyw.backend.service.impl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return null;
}
}
这个api会传入一个username 我们需要通过这个username去返回一个接口UserDetails 包含对应的用户名和用户密码
首先就得实现 对数据库的查询 得用到mapper层 前面说过用mapper得先定义出来(最好加个private 防止出错) 然后加个@Autowired注解
然后调用MyBatis-plus的查询sql接口 进行查询
如果查询结果不存在 就抛出异常
package com.zwnsyw.backend.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zwnsyw.backend.mapper.UserMapper;
import com.zwnsyw.backend.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
QueryWrapper<User> queryWrapper=new QueryWrapper<>();
queryWrapper.eq("username",username);
User user=userMapper.selectOne(queryWrapper);
if(user==null){
throw new RuntimeException("用户不存在");
}
return null;
}
}
如果结果存在 就需要
实现 UserDetails 接口
新建软件包 com.zwnsyw.backend.service.impl.utils
创建类 UserDetailsImpl
它是实现UserDetails的接口 方法照旧 alt+insert
直接确定 全部插入
对得到的代码进行解读:
package com.zwnsyw.backend.service.impl.utils;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
public class UserDetailsImpl implements UserDetails {
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// 返回用户的权限集合,可以根据实际的角色和权限需求进行修改
return List.of();
}
@Override
public String getPassword() {
// 返回用户的密码,可以根据实际的用户数据进行修改
return "";
}
@Override
public String getUsername() {
// 返回用户的用户名,可以根据实际的用户数据进行修改
return "";
}
@Override
public boolean isAccountNonExpired() {
// 表示账号是否未过期,返回 true 代表账号有效
return false;
}
@Override
public boolean isAccountNonLocked() {
// 表示账号是否未被锁定,返回 true 代表账号未被锁定
return false;
}
@Override
public boolean isCredentialsNonExpired() {
// 表示用户的凭据(如密码)是否未过期,返回 true 代表凭据未过期
return false;
}
@Override
public boolean isEnabled() {
// 表示用户是否启用,返回 true 代表用户被启用
return false;
}
}
说明:
getAuthorities:根据你的权限管理逻辑,返回用户的权限列表。这里用List.of()代表一个空权限列表,你可以根据实际情况返回包含用户权限的集合。getPassword和getUsername:这两个方法应该根据你的用户数据返回正确的用户名和密码。isAccountNonExpired:表示用户账号是否未过期。现在返回true,即表示账号有效。isAccountNonLocked:表示用户账号是否未被锁定。现在返回true,即表示账号未被锁定。isCredentialsNonExpired:表示用户凭据(例如密码)是否未过期。现在返回true,即表示凭据未过期。isEnabled:表示用户是否被启用。现在返回true,即表示用户处于启用状态。
这个用户名用户密码需要由我们实现的pojo中的User中得到 调用get方法就行了
另外加上实现构造函数get、set方法的注解
得到如下:
package com.zwnsyw.backend.service.impl.utils;
import com.zwnsyw.backend.pojo.User;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserDetailsImpl implements UserDetails {
private User user;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of();
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
回到UserDetailsServiceImpl.java
如果成功查询 返回UserDetailsImpl类的用user初始化的对象即可
package com.zwnsyw.backend.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zwnsyw.backend.mapper.UserMapper;
import com.zwnsyw.backend.pojo.User;
import com.zwnsyw.backend.service.impl.utils.UserDetailsImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
QueryWrapper<User> queryWrapper=new QueryWrapper<>();
queryWrapper.eq("username",username);
User user=userMapper.selectOne(queryWrapper);
if(user==null){
throw new RuntimeException("用户不存在");
}
return new UserDetailsImpl(user);
}
}
测试
现在的逻辑是 从数据库里查用户和密码 如果匹配就算验证成功
直接登入发现报错
是因为 Spring Security 默认期望密码经过编码和加密 而不是直接用数据库里的明文进行比对 但这里只是测试用户名密码登入
没用到加密
有两个解决方法
一个是修改Spring Security配置来支持NoOpPasswordEncoder
另一个是直接在数据表中password字段加上{noop} 说明该字段是明文 没用到加密 如果数据库中的密码是 password123,那么将其更新为 {noop}password123
因为后面肯定都要加密 所以没必要配置明文登入 改一下数据库就行了
实现加密
类似于哈希 给一个字符串可以很容易变成密文 也类似于密钥对 从私到公很容易 从公到私 很难推出来
这样做 可以保证 哪怕数据库泄露了 也不会泄露用户的密码
那既然存的是密文 怎么判断输入的密码是否正确呢 回忆一下哈希就知道了 把第二串密码也加密一遍 如果一模一样就说明正确 不一样的密码哈希后成一样的结果的可能微乎其微(我可能不知道p1是什么 但我一定知道p2的加密结果是不是与p1的加密结果匹配) 这就是加密逻辑
在backend/config下加一个SecurityConfig类
package com.zwnsyw.backend.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
分析
@Configuration 注解
@Configuration表示这是一个配置类,类似于传统的 Spring XML 配置文件。这类类被 Spring 框架标识为包含一个或多个@Bean方法的类,Spring 容器将管理这个类中的@Bean方法生成的 Bean 对象。
@EnableWebSecurity 注解
@EnableWebSecurity是 Spring Security 提供的注解,用于开启 Web 安全支持。这个注解会启用 Spring Security 的 Web 安全特性,使得该配置类能够进行安全性相关的设置。
passwordEncoder 方法
- 该方法使用
@Bean注解标注,表示这个方法会返回一个由 Spring 容器管理的 Bean 实例。在这种情况下,返回的PasswordEncoder是一个BCryptPasswordEncoder实例。 @Bean的作用是告诉 Spring 容器,这个方法返回的对象需要被注册为 Spring 容器中的一个 Bean。
BCryptPasswordEncoder 类
BCryptPasswordEncoder是 Spring Security 提供的一种密码加密方式。BCrypt 是一种非常流行且安全的哈希算法,能够有效地抵抗暴力破解和字典攻击。- 在 Spring Security 中,使用
BCryptPasswordEncoder可以对用户的密码进行加密,并在用户登录时对输入的密码进行匹配验证。
代码的整体功能
该配置类的主要目的是定义一个密码编码器 PasswordEncoder。这段代码告诉 Spring,当需要 PasswordEncoder 时,会使用 BCryptPasswordEncoder 作为加密器。
为什么要这样做?
在 Spring Security 中,密码通常需要进行加密存储,以防止密码泄露。使用 BCryptPasswordEncoder 可以确保密码的安全性,而不是将明文密码直接存储在数据库中。BCrypt
使用了自带的盐值机制,使得每次加密相同的密码都会产生不同的哈希值,这大大增强了安全性。
示例解释:
- 当用户注册或更改密码时,系统会使用
passwordEncoder().encode(plainPassword)对明文密码进行加密,并存储到数据库中。 - 当用户登录时,系统会用用户输入的密码通过
passwordEncoder().matches(rawPassword, encodedPassword)方法来验证输入的密码是否与存储的加密密码匹配。
总结:
这段代码的目的是配置一个 BCryptPasswordEncoder 作为应用的密码加密器,以提高用户密码的安全性。这是一个推荐的安全实践,用于防止密码明文泄露,并增强密码存储的安全性。
其他地方也可以套用这个模板 基本不会变:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
测试(使用test)
package com.zwnsyw.backend;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@SpringBootTest
class BackendApplicationTests {
@Test
void contextLoads() {
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
System.out.println(passwordEncoder.encode("Zwww"));
}
}
得到加密结果:
如果把同样的明文加密三次 会发现其实结果并不一样
所以其底层的原理实际要比简单的哈希要复杂得多 应该是有加上一个关于时间戳的概念 反正它内部有方法会帮我们做好匹配
具体原理可以忽略 也可以做个了解 加密原理
把上次生成的密文与明文进行比对 使用passwordEncoder.matches方法 发现是true的
package com.zwnsyw.backend;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@SpringBootTest
class BackendApplicationTests {
@Test
void contextLoads() {
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
System.out.println(passwordEncoder.encode("Zwww"));
System.out.println(passwordEncoder.matches("Zwww", "$2a$10$S6o7Hdy3HhWnDMVYjBkh5.fPFxkAzaUeeD6k232/llslSAdLalGFK"));
}
}
运用加密后的密码
现在已经实现了加密 再和前面一样用明文密码就会显示不匹配
因为修改了配置文件 所以即使加了{noop}也没用
需要把数据库里的密码更新成加密后的密码
已经存在的我们就直接在test里面生成 然后更改到数据库(简单粗暴)
现在就可以用密码登入了 后面会自动帮我们把输入的密码加密 然后与数据库中的密文对比
那么注册的代码得改一下 刚才是已经存在的密码这样手动改一下 新加的就让它默认走这套逻辑 直接存加密后的密码
编辑backend/controller/user/UserController.java
修改addUser:
//测试添加
@GetMapping("/user/add/{userId}/{username}/{password}")
public String addUser(@PathVariable int userId, @PathVariable String username, @PathVariable String password){
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encodePassword = passwordEncoder.encode(password);
User user = new User(userId, username, encodePassword);
userMapper.insert(user);
return "Add User Success";
}
成功了
项目分区导航: js-import 和 export 的使用 ⬅️ | 00-后端配置和调试 | ➡️ @PathVariable
💬 评论