Compare commits

...

2 Commits

Author SHA1 Message Date
Bunny 58e0b5f4d1 feat: 自定义登录页 2025-01-27 14:18:13 +08:00
Bunny 4efb99a655 feat: Spring实战学习 2025-01-27 12:02:36 +08:00
89 changed files with 1794 additions and 49 deletions

View File

@ -9,6 +9,7 @@
<file url="file://$PROJECT_DIR$/multithreading1/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/multithreading1/src/main/resources-filtered" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/mvc/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/spring-demo/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources-filtered" charset="UTF-8" />

View File

@ -6,6 +6,7 @@
<list>
<option value="$PROJECT_DIR$/pom.xml" />
<option value="$PROJECT_DIR$/mvc/pom.xml" />
<option value="$PROJECT_DIR$/spring-demo/pom.xml" />
</list>
</option>
<option name="ignoredFiles">

View File

@ -1,4 +1,4 @@
# `SpringMVC`笔记
# `Spring`笔记
- 公共AI网站https://chatgptplus.cn/
- vue3官网https://cn.vuejs.org/
@ -147,6 +147,90 @@ public class FooController {
在这个例子中,如果访问 `/foo/bar`,它会首先匹配 `/foo/{id}`,因为它更精确。
## 编写Spring应用
### `@SpringBootApplication` 注解
1. **`@EnableAutoConfiguration`**
- 这个注解让 Spring Boot 根据项目中的依赖,自动配置 Spring 应用程序。Spring Boot 提供了大量的自动配置支持,帮助我们省去手动配置许多常见的功能。
- 例如,如果你的项目中加入了 `spring-boot-starter-web` 依赖Spring Boot 会自动配置一个嵌入式的 Tomcat 服务器和一些常见的 Web 功能。
2. **`@ComponentScan`**
- 这个注解启用 Spring 的组件扫描机制。它会扫描当前类所在的包及其子包,自动发现并注册 `@Component`、`@Service`、`@Repository`、`@Controller` 等注解标注的类。
- 通过 `@ComponentScan`你不需要手动指定要扫描的包Spring Boot 会自动扫描当前包及其子包下的所有组件。
3. **`@Configuration`**
- 这个注解表示该类是一个 Spring 配置类,类似于 XML 配置文件,用于定义 Spring 应用的 Bean 配置。
- 该类可以包含 `@Bean` 注解的方法,返回要管理的 Bean。
### `@SpringBootTest` 注解
`@SpringBootTest` 注解是用于测试 Spring Boot 应用的一个重要注解,它提供了一种方便的方式来启动 Spring Boot 应用上下文,并对整个 Spring Boot 应用进行集成测试。这个注解本身也包含了多个注解,它使得我们能够在测试类中创建一个完整的 Spring 容器来进行集成测试。
具体来说,`@SpringBootTest` 是一个组合注解,它整合了以下几个主要的注解:
1. **`@ContextConfiguration`**
- `@ContextConfiguration` 注解用于加载 Spring 配置文件或者配置类,在测试时会初始化 Spring 容器。`@SpringBootTest` 默认会加载 Spring Boot 应用的主配置类(即包含 `@SpringBootApplication` 注解的类),作为 Spring 容器的上下文。
- 它的作用是让测试类能够加载到 Spring 配置并创建一个完整的应用上下文。
2. **`@TestExecutionListeners`**
- 该注解指定了测试执行时的监听器。在 Spring 测试框架中,`@TestExecutionListeners` 会提供某些扩展功能,如事务管理、环境配置等,但它的实际作用在大多数测试中不太明显,通常由 Spring Boot 自动配置。
3. **`@DirtiesContext`**
- 这个注解会告诉 Spring 在测试执行之后清除(或重置)应用上下文,通常用于测试中的应用上下文需要被清理或重置,以避免测试间的相互影响。`@SpringBootTest` 会根据需要处理上下文的清理工作。
4. **`@BootstrapWith`**
- 这个注解是用于引导测试的,它会指定 `SpringBootTestContextBootstrapper` 来启动 Spring Boot 测试上下文。这是一个 Spring Boot 测试框架中的内部机制,用于初始化应用上下文并准备测试。
### 测试页面
编写控制界面,返回`index.html`
```java
@RequestMapping
@Controller
public class HomeController {
@Operation(summary = "主页内容")
@GetMapping("index")
public String index() {
return "index";
}
}
```
测试页面返回结果
```java
@WebMvcTest(HomeController.class)
class HomeControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void index() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/"))// 访问路径
.andExpect(MockMvcResultMatchers.status().isOk())// 判断状态是否成功
.andExpect(MockMvcResultMatchers.view().name("index"))// 判断视图名称是否是index
// 是否包含字段
.andExpect(MockMvcResultMatchers.content().string(Matchers.containsString("欢迎。。。")));
}
}
```
访问index的页面
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Index 测试页面</title>
</head>
<body>
<h1>欢迎。。。</h1>
<img alt="" th:src="@{/images/icon_3.png}">
<img alt="通常MVC项目静态资源放在 static/images 下" src="images/index/diannao.png">
</body>
</html>
```
## 访问控制
请求地址时返回对应的网页文件
@ -501,17 +585,13 @@ public class WebConfig implements WebMvcConfigurer {
`SessionInterceptor` 可以用于监控和管理 Session 数据。
### 8. **总结**
在 Spring MVC 中,向 Session 共享数据主要有以下几种方式:
- **`HttpSession`**:通过 `HttpSession` 对象存储和读取 Session 数据。
- **`@SessionAttributes`**:通过 `@SessionAttributes` 注解将模型属性添加到 Session 中。
- **`@ModelAttribute`**:结合 `@SessionAttributes` 使用,将模型数据持久化到 Session 中。
- **`@RequestParam``@PathVariable`**:将请求参数或路径变量存储到 Session 中。
- **Session 过期与清理**:可以通过配置控制会话超时,或手动清除 Session 数据。
Spring MVC 提供的 Session 管理机制非常灵活,能够满足各种需求。
> 在 Spring MVC 中,向 Session 共享数据主要有以下几种方式:
>
> - **`HttpSession`**:通过 `HttpSession` 对象存储和读取 Session 数据。
> - **`@SessionAttributes`**:通过 `@SessionAttributes` 注解将模型属性添加到 Session 中。
> - **`@ModelAttribute`**:结合 `@SessionAttributes` 使用,将模型数据持久化到 Session 中。
> - **`@RequestParam``@PathVariable`**:将请求参数或路径变量存储到 Session 中。
> - **Session 过期与清理**:可以通过配置控制会话超时,或手动清除 Session 数据。
## 向application域共享数据
@ -656,14 +736,11 @@ public class AppConfig {
在这种情况下Spring 管理的 Bean 会在应用级别共享,类似于 `ServletContext` 中存储的数据。
### 6. **总结**
- `Application` 域(即 `ServletContext`)用于在整个应用程序范围内共享数据,适合存储全局共享的信息、配置和常量。
- 通过 `HttpServletRequest.getServletContext()``@Autowired` 注解可以访问 `ServletContext` 并向 `Application` 域中共享数据。
- 数据存储在 `Application` 域中可以在整个应用程序生命周期内有效,适用于共享全局性的、无需频繁更新的数据。
- 应谨慎存储敏感数据,并注意线程安全和数据的生命周期。
通过合理地使用 `Application` 域,你可以实现全局共享的数据管理,并且不需要担心会话或请求的生命周期问题。
> - `Application` 域(即 `ServletContext`)用于在整个应用程序范围内共享数据,适合存储全局共享的信息、配置和常量。
> - 通过 `HttpServletRequest.getServletContext()``@Autowired` 注解可以访问 `ServletContext` 并向 `Application` 域中共享数据。
> - 数据存储在 `Application` 域中可以在整个应用程序生命周期内有效,适用于共享全局性的、无需频繁更新的数据。
> - 应谨慎存储敏感数据,并注意线程安全和数据的生命周期。
>
## 重定向和转发使用
@ -851,12 +928,325 @@ public class RedirectController {
}
```
### 5. **总结**
- **重定向Redirect**:客户端浏览器会发起一次新的请求,地址栏 URL 会发生变化,适用于需要跨请求跳转或外部跳转的场景。
- **转发Forward**:请求在服务器内部被转发,地址栏 URL 不变,适用于同一请求的内部跳转。
通过合理使用转发和重定向,你可以灵活地控制请求流转和用户体验。
## Spring 表单验证
在 Spring MVC 中,表单验证是通过一系列的注解来完成的。
### **`@NotNull`**
- **作用**:确保字段值不为空。
- **用法**:用于字段、方法参数或返回值上,表示该字段不能为空。如果字段为空,将验证失败并返回相应的错误信息。
- 示例
```java
@NotNull(message = "用户名不能为空")
private String username;
```
### **`@NotEmpty`**
- **作用**:确保字段不为空,并且不为一个空字符串。
- **用法**:用于字符串、集合等类型,验证字段不仅不能为空,而且不能为空字符串。
- 示例
```java
@NotEmpty(message = "密码不能为空")
private String password;
```
### **`@NotBlank`**
- **作用**:确保字段不为空,并且不为一个空白字符串(即非空白字符)。
- **用法**:类似于 `@NotEmpty`,但除了不为空,还要求去除空白字符后不能为零长度。
- 示例
```java
@NotBlank(message = "电子邮件不能为空")
private String email;
```
### **`@Size(min, max)`**
- **作用**:验证字段的大小,适用于字符串、集合、数组等类型。
- **用法**:可以设置最小值和最大值来限制字段的长度或集合的大小。
- 示例
```java
@Size(min = 6, max = 20, message = "密码长度必须在6到20之间")
private String password;
```
### **`@Email`**
- **作用**:验证字段是否符合有效的电子邮件格式。
- **用法**:用于验证字符串字段是否为有效的电子邮件地址格式。
- 示例
```java
@Email(message = "请输入有效的电子邮件地址")
private String email;
```
### **`@Pattern(regexp)`**
- **作用**:根据正则表达式验证字段值。
- **用法**:可以根据自定义的正则表达式来验证字段的内容。
- 示例
```java
@Pattern(regexp = "^\\d{10}$", message = "请输入有效的手机号码")
private String phoneNumber;
```
### **`@Min(value)``@Max(value)`**
- **作用**:确保数字类型字段的值在指定范围内。
- **用法**`@Min` 用于验证值是否大于等于指定的最小值,`@Max` 用于验证值是否小于等于指定的最大值。
- 示例
```java
@Min(value = 18, message = "年龄不能小于18岁")
@Max(value = 100, message = "年龄不能大于100岁")
private int age;
```
### **`@DecimalMin(value)``@DecimalMax(value)`**
- **作用**:用于验证浮动值是否在指定范围内,类似于 `@Min``@Max`,但适用于 `BigDecimal``Double` 类型的数值。
- **用法**`@DecimalMin` 验证值是否大于等于指定的最小值,`@DecimalMax` 验证值是否小于等于指定的最大值。
- 示例
```java
@DecimalMin(value = "0.0", inclusive = true, message = "价格不能小于0")
private BigDecimal price;
```
### **`@Future`**
- **作用**:验证日期字段的值是否为将来日期。
- **用法**:用于 `java.util.Date`、`java.time.LocalDate` 或 `java.time.LocalDateTime` 等日期类型的字段。
- 示例
```java
@Future(message = "日期必须是未来的时间")
private LocalDate eventDate;
```
### **`@Past`**
- **作用**:验证日期字段的值是否为过去的日期。
- **用法**:类似于 `@Future`,但是验证日期必须是过去的时间。
- 示例
```java
@Past(message = "出生日期必须是过去的时间")
private LocalDate birthDate;
```
### **`@AssertTrue`**
- **作用**:验证字段值是否为 `true`
- **用法**:适用于布尔类型字段,如果值不是 `true`,则验证失败。
- 示例
```java
@AssertTrue(message = "必须接受条款和条件")
private boolean acceptedTerms;
```
### **`@AssertFalse`**
- **作用**:验证字段值是否为 `false`
- **用法**:适用于布尔类型字段,如果值不是 `false`,则验证失败。
- 示例
```java
@AssertFalse(message = "不能接受条款")
private boolean declinedTerms;
```
### **`@Valid``@Validated`**
- **作用**:触发嵌套对象的验证。
- **用法**:当你有嵌套对象(如表单中的对象属性是另一个对象),使用 `@Valid``@Validated` 注解来递归验证该对象。
- 示例
```java
@Valid
private Address address;
```
### **`@Digits(integer, fraction)`**
- **作用**:验证数字字段的有效性,确保字段值是一个有效的数字,并且整数部分和小数部分的位数符合指定要求。
- **用法**`integer` 参数用于指定数字的整数部分的最大位数,`fraction` 参数用于指定小数部分的最大位数。
- 示例
```java
@Digits(integer = 5, fraction = 2, message = "金额应为最大5位整数和2位小数")
private BigDecimal amount;
```
- 这个例子验证金额字段的最大值为 `99999.99`即最多5位整数和2位小数
### **`@CreditCardNumber`**
- **作用**:验证信用卡号的有效性,确保其符合信用卡的常见格式,通常包括 Luhn 算法的验证。
- **用法**:该注解用于验证信用卡号的格式是否有效。
- 示例
```java
@CreditCardNumber(message = "请输入有效的信用卡号")
private String creditCardNumber;
```
- 该注解会根据常见的信用卡规则(如 VISA、MasterCard 等)验证输入的信用卡号是否合法。
### **`@Range(min, max)`**(不是 Spring 内置的,但通常来自 Hibernate Validator
- **作用**:验证字段值是否在指定的范围内。常用于 `Integer`、`Long`、`Double` 等数值类型的字段。
- **用法**:指定字段的有效范围,当值不在范围内时会验证失败。
- 示例
```java
@Range(min = 1, max = 100, message = "数字必须在1到100之间")
private int quantity;
```
- 该注解会验证 `quantity` 字段的值是否在 `1``100` 之间。
### **`@URL`**
- **作用**:验证字段是否为有效的 URL 格式。
- **用法**:用于字符串类型的字段,验证其是否符合有效的 URL 格式。
- 示例
```java
@URL(message = "请输入有效的网址")
private String website;
```
### **`@Valid``@Validated`**
- **作用**:用于嵌套对象的验证,确保嵌套对象的字段也进行验证。
- **用法**:这两个注解会触发嵌套对象的验证,通常用于嵌套的复杂表单数据结构。
- 示例
```java
@Valid
private Address address;
```
- 如果 `Address` 类中有字段使用了验证注解,`@Valid` 会递归地验证 `Address` 对象的所有字段。
### **`@FutureOrPresent`**
- **作用**:验证日期或时间字段的值是否是当前日期(包括今天)或未来的日期。
- **用法**:该注解用于日期和时间字段,确保其为今天或将来的日期。
- 示例
```java
@FutureOrPresent(message = "事件日期必须是今天或将来")
private LocalDate eventDate;
```
### **`@PastOrPresent`**
- **作用**:验证日期或时间字段的值是否是当前日期(包括今天)或过去的日期。
- **用法**:与 `@FutureOrPresent` 相反,确保字段是过去或今天的日期。
- 示
```java
@PastOrPresent(message = "出生日期必须是过去的时间或今天")
private LocalDate birthDate;
```
### **`@Null`**
- **作用**:验证字段是否为 `null`。如果字段不为空,则验证失败。
- **用法**:该注解可以用于字段或方法参数上,确保字段值必须为 `null`
- 示例
```java
@Null(message = "该字段必须为null")
private String nickname;
```
### **`@ScriptAssert(lang, script)`**
- **作用**:通过自定义脚本验证字段值。
- **用法**:允许使用自定义脚本(如 JavaScript来执行复杂的验证逻辑。需要指定脚本语言和脚本内容。
- 示例
```java
@ScriptAssert(lang = "javascript", script = "_this.password == _this.confirmPassword", message = "密码和确认密码必须一致")
private String password;
private String confirmPassword;
```
- 这个注解可以用于检查两个字段值是否一致。
### **`@UniqueElements`**Hibernate Validator 扩展)
- **作用**:确保集合中的元素是唯一的,常用于 List 或 Set 类型字段。
- **用法**:适用于集合类型字段,确保集合中的元素不重复。
- 示例
```java
@UniqueElements(message = "列表中的元素必须唯一")
private List<String> tags;
```
## Thymeleaf快速入门
@ -1117,15 +1507,240 @@ Thymeleaf 是一种现代化的 Java 模板引擎,广泛用于生成 HTML、XM
- 设置 `input``placeholder``${placeholderText}` 的值。
------
### 23. **`th:errors`**
### 总结
显示与 `username` 属性相关的错误信息。如果 `username` 为空或者不符合验证规则,这里就会显示出相应的错误消息。
Thymeleaf 提供了许多强大的指令来处理模板中的动态内容、条件渲染、迭代和属性绑定。常见的指令包括:
```html
<div th:errors="*{email}"></div> <!-- 错误信息展示 -->
```
- `th:text`、`th:utext`:用于设置文本内容。
- `th:each`:用于循环遍历。
- `th:if`、`th:unless`:用于条件判断。
- `th:attr`、`th:id`、`th:class`:用于设置 HTML 属性。
- `th:replace`、`th:include`:用于片段包含。
- `th:switch`、`th:case`:用于类似 `switch` 的条件语句。
你还可以通过 `th:errors` 对错误消息进行自定义格式化。例如,使用 `*{field}` 可以获取字段的错误信息。
```html
<div th:errors="*{username}">Error</div>
```
如果验证失败,错误消息将显示在 `<div>` 中。如果没有错误,它会显示默认的 "Error" 文本。
## SpringSecurity
### 密码转换器Password Encoder
Spring Security 提供了多种密码转换器Password Encoder这些转换器用于对用户密码进行加密和验证。常见的密码转换器包括
1. **BCryptPasswordEncoder**
- 使用 **BCrypt** 算法对密码进行加密。
- 是最常用的密码加密方案具有强大的加密性并且支持自动加盐salt防止暴力破解攻击。
- 示例:
```java
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
```
2. **NoOpPasswordEncoder**
- 不对密码进行加密,直接返回明文密码。
- 主要用于开发和测试环境,**不推荐在生产环境中使用**。
- 示例:
```java
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
```
3. **Pbkdf2PasswordEncoder**
- 使用 **PBKDF2** 算法进行密码加密。
- 提供较强的安全性,并且支持对密码进行哈希。
- 示例:
```java
@Bean
public PasswordEncoder passwordEncoder() {
return new Pbkdf2PasswordEncoder();
}
```
4. **Argon2PasswordEncoder**
- 使用 **Argon2** 算法对密码进行加密。
- Argon2 是目前被认为最强的密码哈希算法,支持内存密集型计算,从而防止硬件加速破解。
- 示例:
```java
@Bean
public PasswordEncoder passwordEncoder() {
return new Argon2PasswordEncoder();
}
```
5. **SCryptPasswordEncoder**
- 使用 **SCrypt** 算法进行密码加密。
- SCrypt 是另一种内存密集型的密码加密算法,与 Argon2 类似,旨在防止硬件加速破解。
- 示例:
```java
@Bean
public PasswordEncoder passwordEncoder() {
return new SCryptPasswordEncoder();
}
```
6. **MessageDigestPasswordEncoder** (已废弃)
- 基于 **MessageDigest** 算法进行加密(如 SHA-1、SHA-256 等)。
- 由于缺乏盐和密钥加密机制,已被其他更强的加密方式所替代。
> 选择密码转换器的建议:
>
> - 在现代应用中,推荐使用 **BCryptPasswordEncoder****Argon2PasswordEncoder**,这两种算法提供了强大的加密性。
> - **Pbkdf2PasswordEncoder****SCryptPasswordEncoder** 也可以作为备选方案,尤其是当你希望加密算法能够承受更多资源密集型攻击时。
> - **NoOpPasswordEncoder** 仅限于开发和测试环境。
### 访问主页
需要使用`http://localhost:8080/index`来访问主页,可以在配置中配置,访问根路径直接跳转
```java
@RequestMapping
@Controller
public class HomeController {
@Operation(summary = "主页内容")
@GetMapping("index")
public String index() {
return "index";
}
@Operation(summary = "订单内容")
@GetMapping("order")
public String order() {
return "order";
}
@Operation(summary = "login")
@GetMapping("login")
public String login() {
return "login";
}
}
```
在配置中
```java
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/")
// .setViewName("forward:/index") //两种方式写法
.setViewName("index");
registry.addViewController("/login");
}
}
```
### 自定义登录
```java
package cn.bunny.springdemo.configuration;
import cn.bunny.springdemo.dao.entity.AdminUser;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import java.util.ArrayList;
@Configuration
public class SecurityConfiguration {
@Bean
public PasswordEncoder passwordEncoder() {
return new SCryptPasswordEncoder(4, 8, 1, 8, 32);
}
/**
* 使用内存方式
*
* @param encoder 密码加密器
* @return 基于内存的用户
*/
@Bean
public UserDetailsService userDetailsService(PasswordEncoder encoder) {
ArrayList<UserDetails> userDetails = new ArrayList<>();
userDetails.add(new AdminUser("admin", encoder.encode("admin"), true));
userDetails.add(new AdminUser("bunny", encoder.encode("password"), true));
return new InMemoryUserDetailsManager(userDetails);
}
// /**
// * 使用数据库方式
// *
// * @param userService 获取用户数据(如数据库)
// * @return 基于数据库的用户
// */
// @Bean
// public UserDetailsService userDetailsService(UserService userService) {
// return username -> {
// AdminUser adminUser = userService.getOne(Wrappers.<AdminUser>lambdaQuery().eq(AdminUser::getUsername, userService));
// if (adminUser != null) {
// return adminUser;
// }
// throw new UsernameNotFoundException("未找到 AdminUser");
// };
// }
@Bean
public SecurityFilterChain filterChain(HttpSecurity security) throws Exception {
return security
.authorizeHttpRequests(authorizationManagerRequestMatcherRegistry ->
authorizationManagerRequestMatcherRegistry
.requestMatchers("/order").hasRole("USER")// 请求需要含有USER角色
.requestMatchers("/", "/index", "/login", "/images/**").permitAll()
)
/* 自定义登录页面 */
// .formLogin(AbstractAuthenticationFilterConfigurer::permitAll)// 默认的登录页会自动启用,无需额外配置
.formLogin(formLoginConfigurer -> formLoginConfigurer
.loginPage("/login")
// .loginProcessingUrl("/authenticate")
.usernameParameter("username")// 自定义用户名名称
.usernameParameter("password")// 自定义密码名称
.defaultSuccessUrl("/index")// 登录成功后默认跳转页面
// .defaultSuccessUrl("/index", true)// 登录成功后默认跳转页面,如果用户之前访问页面也需要强制跳转到 /index 可以传递第二个参数
)
.build();
}
}
```

View File

@ -17,7 +17,7 @@
<properties>
<java.version>17</java.version>
<mybatis-plus.version>3.5.6</mybatis-plus.version>
<mysql.version>8.0.30</mysql.version>
<mysql.version>8.0.33</mysql.version>
<HikariCP.version>5.1.0</HikariCP.version>
</properties>
@ -43,7 +43,6 @@
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>

View File

@ -20,7 +20,7 @@ public class Knife4jConfig {
// 使用协议
License license = new License().name("MIT").url("https://MUT.com");
// 相关信息
Info info = new Info().title("家庭理财管理系统").description("家庭理财管理系统").version("v1.0.0").contact(contact).license(license).termsOfService("MIT");
Info info = new Info().title("Spring MVC").description("Spring MVC 学习").version("v1.0.0").contact(contact).license(license).termsOfService("MIT");
return new OpenAPI().info(info).externalDocs(new ExternalDocumentation());
}
@ -28,6 +28,6 @@ public class Knife4jConfig {
// 管理员相关分类接口
@Bean
public GroupedOpenApi groupedOpenAdminApi() {
return GroupedOpenApi.builder().group("后台管理").pathsToMatch("/user/**").build();
return GroupedOpenApi.builder().group("用户管理管理").pathsToMatch("/user/**").build();
}
}

View File

@ -1,13 +0,0 @@
package cn.bunny.mvc;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class MvcApplicationTests {
@Test
void contextLoads() {
}
}

View File

@ -0,0 +1,92 @@
11:10:36:949 INFO 11120 --- [spring-demo] [main] c.b.s.controller.HomeControllerTest : Starting HomeControllerTest using Java 17.0.9 with PID 11120 (started by 13199 in F:\学习\代码\Java\MultiThread\spring-demo)
11:10:36:951 INFO 11120 --- [spring-demo] [main] c.b.s.controller.HomeControllerTest : The following 1 profile is active: "dev"
11:10:37:324 INFO 11120 --- [spring-demo] [main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index
11:10:37:341 INFO 11120 --- [spring-demo] [main] o.s.v.b.OptionalValidatorFactoryBean : Failed to set up a Bean Validation provider: jakarta.validation.NoProviderFoundException: Unable to create a Configuration, because no Jakarta Bean Validation provider could be found. Add a provider like Hibernate Validator (RI) to your classpath.
11:10:37:500 INFO 11120 --- [spring-demo] [main] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring TestDispatcherServlet ''
11:10:37:500 INFO 11120 --- [spring-demo] [main] o.s.t.web.servlet.TestDispatcherServlet : Initializing Servlet ''
11:10:37:501 INFO 11120 --- [spring-demo] [main] o.s.t.web.servlet.TestDispatcherServlet : Completed initialization in 1 ms
11:10:37:527 INFO 11120 --- [spring-demo] [main] c.b.s.controller.HomeControllerTest : Started HomeControllerTest in 0.888 seconds (process running for 1.391)
11:10:58:580 INFO 15932 --- [spring-demo] [main] c.b.s.controller.HomeControllerTest : Starting HomeControllerTest using Java 17.0.9 with PID 15932 (started by 13199 in F:\学习\代码\Java\MultiThread\spring-demo)
11:10:58:584 INFO 15932 --- [spring-demo] [main] c.b.s.controller.HomeControllerTest : The following 1 profile is active: "dev"
11:10:58:965 INFO 15932 --- [spring-demo] [main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index
11:10:58:980 INFO 15932 --- [spring-demo] [main] o.s.v.b.OptionalValidatorFactoryBean : Failed to set up a Bean Validation provider: jakarta.validation.NoProviderFoundException: Unable to create a Configuration, because no Jakarta Bean Validation provider could be found. Add a provider like Hibernate Validator (RI) to your classpath.
11:10:59:140 INFO 15932 --- [spring-demo] [main] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring TestDispatcherServlet ''
11:10:59:140 INFO 15932 --- [spring-demo] [main] o.s.t.web.servlet.TestDispatcherServlet : Initializing Servlet ''
11:10:59:141 INFO 15932 --- [spring-demo] [main] o.s.t.web.servlet.TestDispatcherServlet : Completed initialization in 1 ms
11:10:59:161 INFO 15932 --- [spring-demo] [main] c.b.s.controller.HomeControllerTest : Started HomeControllerTest in 0.902 seconds (process running for 1.458)
11:12:10:789 INFO 1180 --- [spring-demo] [main] c.b.s.controller.HomeControllerTest : Starting HomeControllerTest using Java 17.0.9 with PID 1180 (started by 13199 in F:\学习\代码\Java\MultiThread\spring-demo)
11:12:10:792 INFO 1180 --- [spring-demo] [main] c.b.s.controller.HomeControllerTest : The following 1 profile is active: "dev"
11:12:11:166 INFO 1180 --- [spring-demo] [main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index
11:12:11:185 INFO 1180 --- [spring-demo] [main] o.s.v.b.OptionalValidatorFactoryBean : Failed to set up a Bean Validation provider: jakarta.validation.NoProviderFoundException: Unable to create a Configuration, because no Jakarta Bean Validation provider could be found. Add a provider like Hibernate Validator (RI) to your classpath.
11:12:11:337 INFO 1180 --- [spring-demo] [main] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring TestDispatcherServlet ''
11:12:11:337 INFO 1180 --- [spring-demo] [main] o.s.t.web.servlet.TestDispatcherServlet : Initializing Servlet ''
11:12:11:338 INFO 1180 --- [spring-demo] [main] o.s.t.web.servlet.TestDispatcherServlet : Completed initialization in 1 ms
11:12:11:366 INFO 1180 --- [spring-demo] [main] c.b.s.controller.HomeControllerTest : Started HomeControllerTest in 0.902 seconds (process running for 1.455)
11:13:43:498 INFO 12616 --- [spring-demo] [main] c.b.s.controller.HomeControllerTest : Starting HomeControllerTest using Java 17.0.9 with PID 12616 (started by 13199 in F:\学习\代码\Java\MultiThread\spring-demo)
11:13:43:501 INFO 12616 --- [spring-demo] [main] c.b.s.controller.HomeControllerTest : The following 1 profile is active: "dev"
11:13:43:877 INFO 12616 --- [spring-demo] [main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index
11:13:43:891 INFO 12616 --- [spring-demo] [main] o.s.v.b.OptionalValidatorFactoryBean : Failed to set up a Bean Validation provider: jakarta.validation.NoProviderFoundException: Unable to create a Configuration, because no Jakarta Bean Validation provider could be found. Add a provider like Hibernate Validator (RI) to your classpath.
11:13:44:041 INFO 12616 --- [spring-demo] [main] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring TestDispatcherServlet ''
11:13:44:041 INFO 12616 --- [spring-demo] [main] o.s.t.web.servlet.TestDispatcherServlet : Initializing Servlet ''
11:13:44:042 INFO 12616 --- [spring-demo] [main] o.s.t.web.servlet.TestDispatcherServlet : Completed initialization in 1 ms
11:13:44:062 INFO 12616 --- [spring-demo] [main] c.b.s.controller.HomeControllerTest : Started HomeControllerTest in 0.888 seconds (process running for 1.466)
12:56:08:799 INFO 10508 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : Starting SpringDemoApplicationTests using Java 17.0.9 with PID 10508 (started by 13199 in F:\学习\代码\Java\MultiThread\spring-demo)
12:56:08:802 INFO 10508 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : The following 1 profile is active: "dev"
12:56:09:855 INFO 10508 --- [spring-demo] [main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index
12:56:09:868 INFO 10508 --- [spring-demo] [main] o.s.v.b.OptionalValidatorFactoryBean : Failed to set up a Bean Validation provider: jakarta.validation.NoProviderFoundException: Unable to create a Configuration, because no Jakarta Bean Validation provider could be found. Add a provider like Hibernate Validator (RI) to your classpath.
12:56:10:093 INFO 10508 --- [spring-demo] [main] r$InitializeUserDetailsManagerConfigurer : Global AuthenticationManager configured with UserDetailsService bean with name userDetailsService
12:56:10:240 INFO 10508 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : Started SpringDemoApplicationTests in 1.717 seconds (process running for 2.253)
12:56:30:906 INFO 21060 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : Starting SpringDemoApplicationTests using Java 17.0.9 with PID 21060 (started by 13199 in F:\学习\代码\Java\MultiThread\spring-demo)
12:56:30:909 INFO 21060 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : The following 1 profile is active: "dev"
12:56:31:936 INFO 21060 --- [spring-demo] [main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index
12:56:31:950 INFO 21060 --- [spring-demo] [main] o.s.v.b.OptionalValidatorFactoryBean : Failed to set up a Bean Validation provider: jakarta.validation.NoProviderFoundException: Unable to create a Configuration, because no Jakarta Bean Validation provider could be found. Add a provider like Hibernate Validator (RI) to your classpath.
12:56:32:175 INFO 21060 --- [spring-demo] [main] r$InitializeUserDetailsManagerConfigurer : Global AuthenticationManager configured with UserDetailsService bean with name userDetailsService
12:56:32:317 INFO 21060 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : Started SpringDemoApplicationTests in 1.675 seconds (process running for 2.201)
12:56:49:178 INFO 19580 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : Starting SpringDemoApplicationTests using Java 17.0.9 with PID 19580 (started by 13199 in F:\学习\代码\Java\MultiThread\spring-demo)
12:56:49:181 INFO 19580 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : The following 1 profile is active: "dev"
12:56:50:206 INFO 19580 --- [spring-demo] [main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index
12:56:50:220 INFO 19580 --- [spring-demo] [main] o.s.v.b.OptionalValidatorFactoryBean : Failed to set up a Bean Validation provider: jakarta.validation.NoProviderFoundException: Unable to create a Configuration, because no Jakarta Bean Validation provider could be found. Add a provider like Hibernate Validator (RI) to your classpath.
12:56:50:449 INFO 19580 --- [spring-demo] [main] r$InitializeUserDetailsManagerConfigurer : Global AuthenticationManager configured with UserDetailsService bean with name userDetailsService
12:56:50:598 INFO 19580 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : Started SpringDemoApplicationTests in 1.688 seconds (process running for 2.241)
12:57:19:701 INFO 12696 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : Starting SpringDemoApplicationTests using Java 17.0.9 with PID 12696 (started by 13199 in F:\学习\代码\Java\MultiThread\spring-demo)
12:57:19:704 INFO 12696 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : The following 1 profile is active: "dev"
12:57:20:735 INFO 12696 --- [spring-demo] [main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index
12:57:20:750 INFO 12696 --- [spring-demo] [main] o.s.v.b.OptionalValidatorFactoryBean : Failed to set up a Bean Validation provider: jakarta.validation.NoProviderFoundException: Unable to create a Configuration, because no Jakarta Bean Validation provider could be found. Add a provider like Hibernate Validator (RI) to your classpath.
12:57:20:979 INFO 12696 --- [spring-demo] [main] r$InitializeUserDetailsManagerConfigurer : Global AuthenticationManager configured with UserDetailsService bean with name userDetailsService
12:57:21:127 INFO 12696 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : Started SpringDemoApplicationTests in 1.708 seconds (process running for 2.258)
12:57:45:076 INFO 9352 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : Starting SpringDemoApplicationTests using Java 17.0.9 with PID 9352 (started by 13199 in F:\学习\代码\Java\MultiThread\spring-demo)
12:57:45:079 INFO 9352 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : The following 1 profile is active: "dev"
12:57:46:202 INFO 9352 --- [spring-demo] [main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index
12:57:46:219 INFO 9352 --- [spring-demo] [main] o.s.v.b.OptionalValidatorFactoryBean : Failed to set up a Bean Validation provider: jakarta.validation.NoProviderFoundException: Unable to create a Configuration, because no Jakarta Bean Validation provider could be found. Add a provider like Hibernate Validator (RI) to your classpath.
12:57:46:462 INFO 9352 --- [spring-demo] [main] r$InitializeUserDetailsManagerConfigurer : Global AuthenticationManager configured with UserDetailsService bean with name userDetailsService
12:57:46:630 INFO 9352 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : Started SpringDemoApplicationTests in 1.858 seconds (process running for 2.69)
12:58:16:559 INFO 1652 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : Starting SpringDemoApplicationTests using Java 17.0.9 with PID 1652 (started by 13199 in F:\学习\代码\Java\MultiThread\spring-demo)
12:58:16:563 INFO 1652 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : The following 1 profile is active: "dev"
12:58:17:571 INFO 1652 --- [spring-demo] [main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index
12:58:17:585 INFO 1652 --- [spring-demo] [main] o.s.v.b.OptionalValidatorFactoryBean : Failed to set up a Bean Validation provider: jakarta.validation.NoProviderFoundException: Unable to create a Configuration, because no Jakarta Bean Validation provider could be found. Add a provider like Hibernate Validator (RI) to your classpath.
12:58:17:813 INFO 1652 --- [spring-demo] [main] r$InitializeUserDetailsManagerConfigurer : Global AuthenticationManager configured with UserDetailsService bean with name userDetailsService
12:58:17:955 INFO 1652 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : Started SpringDemoApplicationTests in 1.689 seconds (process running for 2.236)
12:58:31:856 INFO 4352 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : Starting SpringDemoApplicationTests using Java 17.0.9 with PID 4352 (started by 13199 in F:\学习\代码\Java\MultiThread\spring-demo)
12:58:31:860 INFO 4352 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : The following 1 profile is active: "dev"
12:58:42:935 INFO 4352 --- [spring-demo] [main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index
12:58:42:951 INFO 4352 --- [spring-demo] [main] o.s.v.b.OptionalValidatorFactoryBean : Failed to set up a Bean Validation provider: jakarta.validation.NoProviderFoundException: Unable to create a Configuration, because no Jakarta Bean Validation provider could be found. Add a provider like Hibernate Validator (RI) to your classpath.
12:58:43:193 INFO 4352 --- [spring-demo] [main] r$InitializeUserDetailsManagerConfigurer : Global AuthenticationManager configured with UserDetailsService bean with name userDetailsService
12:58:43:364 INFO 4352 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : Started SpringDemoApplicationTests in 11.813 seconds (process running for 12.431)
12:58:47:757 INFO 22540 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : Starting SpringDemoApplicationTests using Java 17.0.9 with PID 22540 (started by 13199 in F:\学习\代码\Java\MultiThread\spring-demo)
12:58:47:760 INFO 22540 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : The following 1 profile is active: "dev"
12:58:53:578 INFO 22540 --- [spring-demo] [main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index
12:58:53:593 INFO 22540 --- [spring-demo] [main] o.s.v.b.OptionalValidatorFactoryBean : Failed to set up a Bean Validation provider: jakarta.validation.NoProviderFoundException: Unable to create a Configuration, because no Jakarta Bean Validation provider could be found. Add a provider like Hibernate Validator (RI) to your classpath.
12:58:53:835 INFO 22540 --- [spring-demo] [main] r$InitializeUserDetailsManagerConfigurer : Global AuthenticationManager configured with UserDetailsService bean with name userDetailsService
12:58:54:003 INFO 22540 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : Started SpringDemoApplicationTests in 6.548 seconds (process running for 7.169)
12:59:00:364 INFO 21332 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : Starting SpringDemoApplicationTests using Java 17.0.9 with PID 21332 (started by 13199 in F:\学习\代码\Java\MultiThread\spring-demo)
12:59:00:367 INFO 21332 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : The following 1 profile is active: "dev"
12:59:01:498 INFO 21332 --- [spring-demo] [main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index
12:59:01:514 INFO 21332 --- [spring-demo] [main] o.s.v.b.OptionalValidatorFactoryBean : Failed to set up a Bean Validation provider: jakarta.validation.NoProviderFoundException: Unable to create a Configuration, because no Jakarta Bean Validation provider could be found. Add a provider like Hibernate Validator (RI) to your classpath.
12:59:01:752 INFO 21332 --- [spring-demo] [main] r$InitializeUserDetailsManagerConfigurer : Global AuthenticationManager configured with UserDetailsService bean with name userDetailsService
12:59:01:920 INFO 21332 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : Started SpringDemoApplicationTests in 1.851 seconds (process running for 2.456)
13:02:06:411 INFO 13172 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : Starting SpringDemoApplicationTests using Java 17.0.9 with PID 13172 (started by 13199 in F:\学习\代码\Java\MultiThread\spring-demo)
13:02:06:416 INFO 13172 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : The following 1 profile is active: "dev"
13:02:07:458 INFO 13172 --- [spring-demo] [main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index
13:02:07:471 INFO 13172 --- [spring-demo] [main] o.s.v.b.OptionalValidatorFactoryBean : Failed to set up a Bean Validation provider: jakarta.validation.NoProviderFoundException: Unable to create a Configuration, because no Jakarta Bean Validation provider could be found. Add a provider like Hibernate Validator (RI) to your classpath.
13:02:07:695 INFO 13172 --- [spring-demo] [main] r$InitializeUserDetailsManagerConfigurer : Global AuthenticationManager configured with UserDetailsService bean with name userDetailsService
13:02:07:837 INFO 13172 --- [spring-demo] [main] c.b.s.SpringDemoApplicationTests : Started SpringDemoApplicationTests in 1.696 seconds (process running for 2.265)

158
spring-demo/pom.xml Normal file
View File

@ -0,0 +1,158 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
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.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.bunny</groupId>
<artifactId>spring-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-demo</name>
<description>spring-demo</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
<fastjson2.version>2.0.47</fastjson2.version>
<version>${junit.version}</version>
<mybatis-plus.version>3.5.6</mybatis-plus.version>
<mysql.version>8.0.33</mysql.version>
<HikariCP.version>5.1.0</HikariCP.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- spring-security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version> <!-- 请使用最新版本 -->
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpg-jdk15on</artifactId>
<version>1.70</version> <!-- 请使用最新版本 -->
</dependency>
<!-- devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- knife4j -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.5.0</version>
</dependency>
<!-- thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- fastjson2 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>${fastjson2.version}</version>
</dependency>
<!-- hutool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.27</version>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- mysql连接池 -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>${HikariCP.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<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>

View File

@ -0,0 +1,11 @@
package cn.bunny.springdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringDemoApplication.class, args);
}
}

View File

@ -0,0 +1,33 @@
package cn.bunny.springdemo.configuration;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import lombok.extern.slf4j.Slf4j;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@Slf4j
public class Knife4jConfig {
@Bean
public OpenAPI openAPI() {
// 作者等信息
Contact contact = new Contact().name("Bunny").email("1319900154@qq.com").url("http://bunny-web.site");
// 使用协议
License license = new License().name("MIT").url("https://MUT.com");
// 相关信息
Info info = new Info().title("Spring ").description("Spring 学习").version("v1.0.0").contact(contact).license(license).termsOfService("MIT");
return new OpenAPI().info(info).externalDocs(new ExternalDocumentation());
}
// 管理员相关分类接口
@Bean
public GroupedOpenApi groupedOpenAdminApi() {
return GroupedOpenApi.builder().group("基础请求").pathsToMatch("/api/**").build();
}
}

View File

@ -0,0 +1,76 @@
package cn.bunny.springdemo.configuration;
import cn.bunny.springdemo.dao.entity.AdminUser;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import java.util.ArrayList;
@Configuration
public class SecurityConfiguration {
@Bean
public PasswordEncoder passwordEncoder() {
return new SCryptPasswordEncoder(4, 8, 1, 8, 32);
}
/**
* 使用内存方式
*
* @param encoder 密码加密器
* @return 基于内存的用户
*/
@Bean
public UserDetailsService userDetailsService(PasswordEncoder encoder) {
ArrayList<UserDetails> userDetails = new ArrayList<>();
userDetails.add(new AdminUser("admin", encoder.encode("admin"), true));
userDetails.add(new AdminUser("bunny", encoder.encode("password"), true));
return new InMemoryUserDetailsManager(userDetails);
}
// /**
// * 使用数据库方式
// *
// * @param userService 获取用户数据如数据库
// * @return 基于数据库的用户
// */
// @Bean
// public UserDetailsService userDetailsService(UserService userService) {
// return username -> {
// AdminUser adminUser = userService.getOne(Wrappers.<AdminUser>lambdaQuery().eq(AdminUser::getUsername, userService));
// if (adminUser != null) {
// return adminUser;
// }
// throw new UsernameNotFoundException("未找到 AdminUser");
// };
// }
@Bean
public SecurityFilterChain filterChain(HttpSecurity security) throws Exception {
return security
.authorizeHttpRequests(authorizationManagerRequestMatcherRegistry ->
authorizationManagerRequestMatcherRegistry
.requestMatchers("/order").hasRole("USER")// 请求需要含有USER角色
.requestMatchers("/", "/index", "/login", "/images/**").permitAll()
)
/* 自定义登录页面 */
// .formLogin(AbstractAuthenticationFilterConfigurer::permitAll)// 默认的登录页会自动启用无需额外配置
.formLogin(formLoginConfigurer -> formLoginConfigurer
.loginPage("/login")
// .loginProcessingUrl("/authenticate")
.usernameParameter("username")// 自定义用户名名称
.usernameParameter("password")// 自定义密码名称
.defaultSuccessUrl("/index")// 登录成功后默认跳转页面
// .defaultSuccessUrl("/index", true)// 登录成功后默认跳转页面如果用户之前访问页面也需要强制跳转到 /index 可以传递第二个参数
)
.build();
}
}

View File

@ -0,0 +1,16 @@
package cn.bunny.springdemo.configuration;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/")
// .setViewName("forward:/index") //两种方式写法
.setViewName("index");
}
}

View File

@ -0,0 +1,29 @@
package cn.bunny.springdemo.controller;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@RequestMapping
@Controller
public class HomeController {
@Operation(summary = "主页内容")
@GetMapping("index")
public String index() {
return "index";
}
@Operation(summary = "订单内容")
@GetMapping("order")
public String order() {
return "order";
}
@Operation(summary = "login")
@GetMapping("login")
public String login() {
return "login";
}
}

View File

@ -0,0 +1,48 @@
package cn.bunny.springdemo.dao;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@Schema(name = "BaseEntity", title = "基础信息字段", description = "基础信息字段")
public class BaseEntity implements Serializable {
@Schema(name = "id", title = "唯一标识")
@TableId(value = "id", type = IdType.ASSIGN_ID)
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
@Schema(name = "createTime", title = "创建时间")
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@Schema(name = "updateTime", title = "更新时间")
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@Schema(name = "createUser", title = "创建用户")
@TableField(fill = FieldFill.INSERT)
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JsonSerialize(using = ToStringSerializer.class)
private Long createUser;
@Schema(name = "updateUser", title = "操作用户")
@TableField(fill = FieldFill.INSERT_UPDATE)
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JsonSerialize(using = ToStringSerializer.class)
private Long updateUser;
@Schema(name = "isDeleted", title = "是否被删除")
@TableLogic
private Boolean isDeleted;
}

View File

@ -0,0 +1,18 @@
package cn.bunny.springdemo.dao;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
@Schema(name = "BaseUserEntity", title = "基础信息字段包含用户信息", description = "基础信息字段包含用户信息")
public class BaseUserEntity extends BaseEntity {
@Schema(name = "username", title = "用户名")
private String createUsername;
@Schema(name = "nickname", title = "昵称")
private String updateUsername;
}

View File

@ -0,0 +1,44 @@
package cn.bunny.springdemo.dao.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Schema(name = "BillDto对象", title = "账单信息查询内容", description = "账单信息查询内容")
public class BillDto {
@Schema(name = "userId", title = "绑定的用户id")
private Long userId;
@Schema(name = "amount", title = "金额")
private BigDecimal amount;
@Schema(name = "categoryIds", title = "类别分类")
private List<Long> categoryIds;
@Schema(name = "username", title = "类型1 - 收入,-1 - 支出")
private Byte type;
@Schema(name = "description", title = "描述")
private String description;
@Schema(name = "startDate", title = "开始交易日期")
@JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd")
private LocalDate startDate;
@Schema(name = "endDate", title = "结束交易日期")
@JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd")
private LocalDate endDate;
}

View File

@ -0,0 +1,49 @@
package cn.bunny.springdemo.dao.entity;
import cn.bunny.springdemo.dao.BaseEntity;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
/**
* <p>
* 管理员用户信息
* </p>
*
* @author Bunny
* @since 2024-06-26
*/
@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@TableName("sys_user")
@Schema(name = "AdminUser对象", title = "用户信息", description = "用户信息")
public class AdminUser extends BaseEntity implements UserDetails {
@Schema(name = "username", title = "用户名")
private String username;
@Schema(name = "password", title = "密码")
private String password;
@Schema(name = "status", title = "状态", description = "1:禁用 0:正常")
private Boolean status;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of(new SimpleGrantedAuthority("ROLE_USER"));
}
}

View File

@ -0,0 +1,49 @@
package cn.bunny.springdemo.dao.entity;
import cn.bunny.springdemo.dao.BaseEntity;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* <p>
* 账单信息
* </p>
*
* @author Bunny
* @since 2024-11-07
*/
@Getter
@Setter
@Accessors(chain = true)
@TableName("t_bill")
@Schema(name = "Bill对象", title = "账单信息", description = "账单信息")
public class Bill extends BaseEntity {
@Schema(name = "username", title = "类型1 - 收入,-1 - 支出")
private Byte type;
@Schema(name = "userId", title = "绑定的用户id")
private Long userId;
@Schema(name = "amount", title = "金额")
private BigDecimal amount;
@Schema(name = "description", title = "描述")
private String description;
@Schema(name = "transactionDate", title = "交易日期")
private LocalDateTime transactionDate;
@Schema(name = "categoryId", title = "类别id")
private Long categoryId;
}

View File

@ -0,0 +1,19 @@
package cn.bunny.springdemo.mapper;
import cn.bunny.springdemo.dao.entity.AdminUser;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* 用户信息 Mapper 接口
* </p>
*
* @author Bunny
* @since 2024-09-26
*/
@Mapper
public interface UserMapper extends BaseMapper<AdminUser> {
}

View File

@ -0,0 +1,16 @@
package cn.bunny.springdemo.services;
import cn.bunny.springdemo.dao.entity.AdminUser;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 用户信息 服务类
* </p>
*
* @author Bunny
* @since 2024-09-26
*/
public interface UserService extends IService<AdminUser> {
}

View File

@ -0,0 +1,21 @@
package cn.bunny.springdemo.services.impl;
import cn.bunny.springdemo.dao.entity.AdminUser;
import cn.bunny.springdemo.mapper.UserMapper;
import cn.bunny.springdemo.services.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 用户信息 服务实现类
*
* @author Bunny
* @since 2024-09-26
*/
@Service
@Transactional
public class UserServiceImpl extends ServiceImpl<UserMapper, AdminUser> implements UserService {
}

View File

@ -0,0 +1,38 @@
server:
port: 8080
logging:
level:
cn.bunny.service.mapper: info
cn.bunny.service.controller: info
cn.bunny.service.service: info
root: info
pattern:
dateformat: HH:mm:ss:SSS
file:
path: "logs/${spring.application.name}"
#mybatis-plus:
# configuration:
# map-underscore-to-camel-case: true
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 查看日志
bunny:
master:
host: 192.168.3.130
port: 3306
database: family_financial
username: root
password: "123456"
mongodb:
database: financial
host: 192.168.3.130
port: 27017
username: admin
password: "123456"
authentication-database: admin
additional-hosts: 192.168.3.130

View File

@ -0,0 +1,20 @@
spring:
application:
name: spring-demo
profiles:
active: dev
servlet:
multipart:
max-file-size: 6MB
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://${bunny.master.host}:${bunny.master.port}/${bunny.master.database}?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true
username: ${bunny.master.username}
password: ${bunny.master.password}
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>400</title>
</head>
<body>
<h1>400</h1>
</body>
</html>

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>404</title>
</head>
<body>
<h1>404</h1>
</body>
</html>

View File

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="UTF-8">
<title>500 - 服务器错误</title>
<meta content="width=device-width, maximum-scale=1, initial-scale=1" name="viewport"/>
<style>
html, body {
height: 100%;
}
body {
color: #333;
margin: auto;
padding: 1em;
display: table;
user-select: none;
box-sizing: border-box;
font: lighter 20px "微软雅黑";
}
a {
color: #3498db;
text-decoration: none;
}
h1 {
margin-top: 0;
font-size: 3.5em;
}
main {
margin: 0 auto;
text-align: center;
display: table-cell;
vertical-align: middle;
}
.btn {
color: #fff;
padding: .75em 1em;
background: #3498db;
border-radius: 1.5em;
display: inline-block;
transition: opacity .3s, transform .3s;
}
.btn:hover {
transform: scale(1.1);
}
.btn:active {
opacity: .7;
}
</style>
</head>
<body>
<main>
<h1>:'(</h1>
<p>服务器开小差啦!管理员正在修理中...</p>
<p>还请阁下静候站点恢复~</p>
</main>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Index 测试页面</title>
</head>
<body>
<h1>欢迎。。。</h1>
<img alt="" th:src="@{/images/icon_3.png}">
<img alt="通常MVC项目静态资源放在 static/images 下" src="images/index/diannao.png">
</body>
</html>

View File

@ -0,0 +1,255 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>边框流动效果的login页面 www.bootstrapmb.com</title>
<meta charset="UTF-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<meta content="ie=edge" http-equiv="X-UA-Compatible"/>
<style media="screen">
* {
padding: 0;
margin: 0;
box-sizing: border-box
}
body {
font-family: sans-serif;
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background: #0c0116
}
.form {
position: relative;
padding: 60px 15px;
width: 270px;
height: 350px;
background: #0c0116;
overflow: hidden;
box-shadow: 0 0 10px 0 #747772;
border-radius: 5px
}
.form-inner {
position: absolute;
height: 98%;
width: 98%;
top: 50%;
left: 50%;
background: #0c0116;
transform: translate(-50%, -50%)
}
.content {
height: 100%;
width: 100%;
padding: 25px
}
.form-inner h2 {
font-size: 25px;
color: #d7a3d7;
text-align: center;
padding-top: 35px
}
.input {
display: block;
padding: 12px 15px;
width: 100%;
left: 50%;
border-radius: 10px;
margin-top: 20px;
border: 1.5px solid #6d5779;
outline: 0;
background: #19052c;
color: white
}
.btn {
cursor: pointer;
color: white;
margin-top: 40px;
width: 100%;
padding: 12px;
outline: 0;
background: #800080;
border: 0;
font-size: 18px;
border-radius: 10px;
transition: .4s
}
.btn:hover {
background: #c907c9
}
.form span {
position: absolute;
height: 50%;
width: 50%
}
.form span:nth-child(1) {
background: #ffda05;
top: 0;
left: -48%;
animation: 5s span1 infinite linear;
animation-delay: 1s
}
.form span:nth-child(2) {
background: #00a800;
bottom: 0;
right: -48%;
animation: 5s span2 infinite linear
}
.form span:nth-child(3) {
background: #800080;
right: -48%;
top: 0;
animation: 5s span3 infinite linear
}
.form span:nth-child(4) {
background: #f00;
bottom: 0;
right: -48%;
animation: 5s span4 infinite linear;
animation-delay: 1s
}
@keyframes span1 {
0% {
top: -48%;
left: -48%
}
25% {
top: -48%;
left: 98%
}
50% {
top: 98%;
left: 98%
}
75% {
top: 98%;
left: -48%
}
100% {
top: -48%;
left: -48%
}
}
@keyframes span2 {
0% {
bottom: -48%;
right: -48%
}
25% {
bottom: -48%;
right: 98%
}
50% {
bottom: 98%;
right: 98%
}
75% {
bottom: 98%;
right: -48%
}
100% {
bottom: -48%;
right: -48%
}
}
@keyframes span3 {
0% {
top: -48%;
left: -48%
}
25% {
top: -48%;
left: 98%
}
50% {
top: 98%;
left: 98%
}
75% {
top: 98%;
left: -48%
}
100% {
top: -48%;
left: -48%
}
}
@keyframes span4 {
0% {
bottom: -48%;
right: -48%
}
25% {
bottom: -48%;
right: 98%
}
50% {
bottom: 98%;
right: 98%
}
75% {
bottom: 98%;
right: -48%
}
100% {
bottom: -48%;
right: -48%
}
}
</style>
</head>
<body>
<form class="form" method="post" th:action="@{/login}">
<span></span>
<span></span>
<span></span>
<span></span>
<div class="form-inner">
<h2>登录界面</h2>
<div class="content">
<label>
<input class="input" name="username" placeholder="用户名" type="text" value="admin"/>
</label>
<label>
<input class="input" name="password" placeholder="密码" type="password" value="admin"/>
</label>
<button class="btn">登录</button>
</div>
</div>
</form>
</body>
</html>

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>订单内容</title>
</head>
<body>
<h1>订单内容</h1>
</body>
</html>

View File

@ -0,0 +1,20 @@
package cn.bunny.springdemo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.password.PasswordEncoder;
@SpringBootTest
class SpringDemoApplicationTests {
@Autowired
PasswordEncoder passwordEncoder;
@Test
void contextLoads() {
String s = passwordEncoder.encode("admin123");
System.out.println(s);
}
}

View File

@ -0,0 +1,24 @@
package cn.bunny.springdemo.controller;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
@WebMvcTest(HomeController.class)
class HomeControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void index() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/"))// 访问路径
.andExpect(MockMvcResultMatchers.status().isOk())// 判断状态是否成功
.andExpect(MockMvcResultMatchers.view().name("index"))// 判断视图名称是否是index
.andExpect(MockMvcResultMatchers.content().string(Matchers.containsString("欢迎。。。")));// 是否包含字段
}
}