Compare commits
2 Commits
da5a5d1314
...
58e0b5f4d1
Author | SHA1 | Date |
---|---|---|
|
58e0b5f4d1 | |
|
4efb99a655 |
|
@ -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" />
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
```
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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> {
|
||||
|
||||
|
||||
}
|
|
@ -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> {
|
||||
|
||||
}
|
|
@ -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 {
|
||||
|
||||
}
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>400</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>400</h1>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>404</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>404</h1>
|
||||
</body>
|
||||
</html>
|
|
@ -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>
|
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 326 KiB |
After Width: | Height: | Size: 55 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 89 KiB |
After Width: | Height: | Size: 79 KiB |
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 80 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 8.9 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 8.1 KiB |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 8.4 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 8.0 KiB |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 45 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.7 KiB |
|
@ -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>
|
|
@ -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>
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>订单内容</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>订单内容</h1>
|
||||
</body>
|
||||
</html>
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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("欢迎。。。")));// 是否包含字段
|
||||
}
|
||||
}
|