mirror of
https://github.com/continew-org/continew-starter.git
synced 2025-11-15 08:57:09 +08:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1c86c632dd | |||
| 58dc51f66c | |||
| f67f278784 | |||
| c9311df093 | |||
| 9ebcd14878 | |||
| 74a1166b5f | |||
| b604f2fc7e | |||
| 12c3d64066 | |||
| 111e732967 | |||
| 88f82d1c0a | |||
| 5ccdd9e5da | |||
| ea71cf573b | |||
| 00bba33517 | |||
| 7b795194d3 | |||
| 00798bdb4c | |||
| c33a6709f5 | |||
| 2afb0b625f | |||
| 669ea85658 | |||
| 18b9d1ba79 | |||
| c963978980 | |||
| 2aea8ba831 | |||
| 1ba1596f4e | |||
| 3184faaa27 | |||
|
|
2b0fb9aff9 | ||
| 24f99754d0 | |||
| ab76665aab | |||
| 6db8990560 |
43
CHANGELOG.md
43
CHANGELOG.md
@@ -1,3 +1,46 @@
|
||||
## [v1.4.0](https://github.com/Charles7c/continew-starter/compare/v1.3.0...v1.4.0) (2024-02-14)
|
||||
|
||||
### ✨ 新特性
|
||||
|
||||
- 【captcha/graphic】新增图形验证码服务接口,并调整验证码默认启用 ([3184faa](https://github.com/Charles7c/continew-starter/commit/3184faaa27111845867d2210f0db16381d53d800))
|
||||
- 【log/httptrace-pro】Log 注解新增 include、exclude 属性,用于扩展或减少日志包含信息 ([669ea85](https://github.com/Charles7c/continew-starter/commit/669ea85658c89c631518def8f84d4f5d60059ad7)) ([2afb0b6](https://github.com/Charles7c/continew-starter/commit/2afb0b625fc936364c6dacacc735ce421c5cb37c))
|
||||
- 【security/mask】新增安全模块-脱敏,支持 JSON 数据脱敏 ([7b79519](https://github.com/Charles7c/continew-starter/commit/7b795194d3db979c239ab30d78fdb61d95f06896)) ([111e732](https://github.com/Charles7c/continew-starter/commit/111e7329673778c475c4ff4aa5ba6eef9f43f506))
|
||||
- 【security/crypto】新增安全模块-加密,支持 MyBatis ORM 框架字段加密 ([5ccdd9e](https://github.com/Charles7c/continew-starter/commit/5ccdd9e5da2a81d6a1f69bdf3f0e4eb1475b68a0)) ([88f82d1](https://github.com/Charles7c/continew-starter/commit/88f82d1c0aa5abf8f094564f4b84ae84efd80946)) ([b604f2f](https://github.com/Charles7c/continew-starter/commit/b604f2fc7eb938a52338ee41cf1823af374a14da)) ([74a1166](https://github.com/Charles7c/continew-starter/commit/74a1166b5f250c2ba8aab027d98bc11e59860c01)) ([9ebcd14](https://github.com/Charles7c/continew-starter/commit/9ebcd14878b499039a70380b0773b00b9f8dc111))
|
||||
- 【security/all】新增 continew-starter-security-all 模块,统一引入加密、脱敏、密码编码器能力 ([12c3d64](https://github.com/Charles7c/continew-starter/commit/12c3d640668298439ef0b610f5b36848e1f91b1a))
|
||||
|
||||
### 💎 功能优化
|
||||
|
||||
- 【log/httptrace-pro】默认启用日志 ([2aea8ba](https://github.com/Charles7c/continew-starter/commit/2aea8ba8318dded142a274221af7de2b62d4ced9))
|
||||
- 【log/httptrace-pro】兼容小写 user-agent 情况 ([18b9d1b](https://github.com/Charles7c/continew-starter/commit/18b9d1ba799ce96d8831b7243508b2517ff5c5c7))
|
||||
- 【auth/satoken】JWT 配置支持启用/关闭 ([c33a670](https://github.com/Charles7c/continew-starter/commit/c33a6709f50c2240cc9826c4ee2e83d88db5fb07))
|
||||
- 【cache/redisson】优化协议前缀变量命名 ([00798bd](https://github.com/Charles7c/continew-starter/commit/00798bdb4c82c8ec8b3cf1110a0afaaa94ad2b27))
|
||||
- 【auth】调整 Redisson 模块为可选依赖 ([00bba33](https://github.com/Charles7c/continew-starter/commit/00bba33517c15936ec2f40a8a7f3213d25a223aa))
|
||||
- 【data/mybatis-plus】重构 ID 生成器配置,支持默认、CosId、自定义 ([c9311df](https://github.com/Charles7c/continew-starter/commit/c9311df093d4524b272535640333c413a2eda86f)) ([58dc51f](https://github.com/Charles7c/continew-starter/commit/58dc51f66c3a77f7f1621557cdd065243b6ae5a9))
|
||||
- 【message/sms】精简部分依赖 ([f67f278](https://github.com/Charles7c/continew-starter/commit/f67f278784002de553c923f399d585e35f0a6356))
|
||||
- 根据 Sonar 建议调整,StrUtil => CharSequenceUtil ([ea71cf5](https://github.com/Charles7c/continew-starter/commit/ea71cf573b7b6452b9315c67967f29e25468a04a))
|
||||
|
||||
### 🐛 问题修复
|
||||
|
||||
- 【extension/crud】修复删除后置处理方法访问修饰符使用错误 ([24f9975](https://github.com/Charles7c/continew-starter/commit/24f99754d041e113f07eb43570d6a49c4ff24008))
|
||||
- 【message/mail】修复发送邮件收件人不为空判断错误 ([Gitee PR#12](https://gitee.com/Charles7c/continew-starter/pulls/12))
|
||||
- 【auth/satoken】修复 SaInterceptor Bean 获取方式错误 ([1ba1596](https://github.com/Charles7c/continew-starter/commit/1ba1596f4e4b31d82e174e981711e45a1df67ee7))
|
||||
|
||||
### 📦 依赖升级
|
||||
|
||||
- 【dependencies】Spring Boot 3.1.7 => 3.1.8 ([ab76665](https://github.com/Charles7c/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
|
||||
- 【dependencies】Dynamic Datasource 4.2.0 => 4.3.0 ([ab76665](https://github.com/Charles7c/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
|
||||
- 【dependencies】JetCache 2.7.4 => 2.7.5 ([ab76665](https://github.com/Charles7c/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
|
||||
- 【dependencies】Redisson 3.25.2 => 3.26.0 ([ab76665](https://github.com/Charles7c/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
|
||||
- 【dependencies】SMS4J 3.0.4 => 3.1.1 ([ab76665](https://github.com/Charles7c/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
|
||||
- 【dependencies】X File Storage 2.0.0 => 2.1.0 ([ab76665](https://github.com/Charles7c/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
|
||||
- 【dependencies】Amazon S3 1.12.626 => 1.12.651 ([ab76665](https://github.com/Charles7c/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
|
||||
- 【dependencies】Crane4j 2.4.0 => 2.5.0 ([ab76665](https://github.com/Charles7c/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8)) ([c963978](https://github.com/Charles7c/continew-starter/commit/c96397898027140c243b034dc3d23bd3d60695e7))
|
||||
- 【dependencies】Knife4j 4.4.0 => 4.5.0 ([ab76665](https://github.com/Charles7c/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
|
||||
- 【dependencies】Hutool 5.8.24 => 5.8.25 ([ab76665](https://github.com/Charles7c/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
|
||||
- 【dependencies】ip2region 3.1.6 => 3.1.7 ([ab76665](https://github.com/Charles7c/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
|
||||
- 【dependencies】flatten-maven-plugin 1.5.0 => 1.6.0 ([ab76665](https://github.com/Charles7c/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
|
||||
- 【dependencies】spotless-maven-plugin 2.40.0 => 2.43.0 ([ab76665](https://github.com/Charles7c/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
|
||||
|
||||
## [v1.3.0](https://github.com/Charles7c/continew-starter/compare/v1.2.0...v1.3.0) (2024-02-03)
|
||||
|
||||
### ✨ 新特性
|
||||
|
||||
114
README.md
114
README.md
@@ -7,7 +7,7 @@
|
||||
<img src="https://img.shields.io/maven-central/v/top.charles7c.continew/continew-starter.svg?label=Maven%20Central&logo=sonatype&logoColor=FFF" alt="Release" />
|
||||
</a>
|
||||
<a href="https://github.com/Charles7c/continew-starter" target="_blank">
|
||||
<img src="https://img.shields.io/badge/RELEASE-v1.3.0-%23ff3f59.svg" alt="Release" />
|
||||
<img src="https://img.shields.io/badge/RELEASE-v1.4.0-%23ff3f59.svg" alt="Release" />
|
||||
</a>
|
||||
<a href="https://app.codacy.com/gh/Charles7c/continew-starter/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade" target="_blank">
|
||||
<img src="https://app.codacy.com/project/badge/Grade/90ed633957a9410aa8745f0654827c01" alt="Codacy Badge" />
|
||||
@@ -16,7 +16,7 @@
|
||||
<img src="https://sonarcloud.io/api/project_badges/measure?project=Charles7c_continew-starter&metric=alert_status" alt="Sonar Status" />
|
||||
</a>
|
||||
<a href="https://spring.io/projects/spring-boot" target="_blank">
|
||||
<img src="https://img.shields.io/badge/Spring Boot-3.1.7-%236CB52D.svg?logo=Spring-Boot" alt="Spring Boot" />
|
||||
<img src="https://img.shields.io/badge/Spring Boot-3.1.8-%236CB52D.svg?logo=Spring-Boot" alt="Spring Boot" />
|
||||
</a>
|
||||
<a href="https://github.com/Charles7c/continew-starter" target="_blank">
|
||||
<img src="https://img.shields.io/badge/Open JDK-17-%236CB52D.svg?logo=OpenJDK&logoColor=FFF" alt="Open JDK" />
|
||||
@@ -164,25 +164,97 @@ continew-starter.web:
|
||||
|
||||
## 模块结构
|
||||
|
||||
| 模块名称 | 模块说明 | 依赖版本 |
|
||||
| ---------------------------------- | --------------------------------------------------- | ------------------------------------------------------------ |
|
||||
| continew-starter-core | 核心模块:包含线程池等自动配置 | <a href="https://spring.io/projects/spring-boot" target="_blank">Spring Boot</a>:3.1.7<br /><a href="https://www.hutool.cn/" target="_blank">Hutool</a>:5.8.24<br />mica-ip2region:3.1.6 |
|
||||
| continew-starter-json-jackson | JSON 模块:Jackson 自动配置 | Jackson:2.15.3 |
|
||||
| continew-starter-api-doc | API 文档模块:Knife4j 自动配置 | <a href="https://doc.xiaominfo.com/" target="_blank">Knife4j</a>:4.4.0 |
|
||||
| continew-starter-security | 安全模块:密码编码器、数据库字段加密、JSON 脱敏等 | |
|
||||
| continew-starter-web | Web 模块:跨域、全局异常、错误处理等自动配置 | <a href="https://undertow.io/" target="_blank">Undertow</a>:2.3.10.Final<br />TLog:1.5.1 |
|
||||
| continew-starter-log-httptrace-pro | 日志模块:Spring Boot Actuator HttpTrace 重置增强版 | |
|
||||
| continew-starter-storage-local | 存储模块:本地存储 | |
|
||||
| continew-starter-file-excel | 文件处理模块:Excel 相关配置 | <a href="https://easyexcel.opensource.alibaba.com/" target="_blank">Easy Excel</a>:3.3.4 |
|
||||
| continew-starter-captcha-graphic | 验证码模块:图形验证码 | Easy Captcha:1.6.2 |
|
||||
| continew-starter-captcha-behavior | 验证码模块:行为验证码 | AJ-Captcha:1.3.0 |
|
||||
| continew-starter-cache-redisson | 缓存模块:Redisson 自动配置 | <a href="https://github.com/redisson/redisson/wiki/Redisson%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D" target="_blank">Redisson</a>:3.25.2 |
|
||||
| continew-starter-data-mybatis-plus | 数据访问模块:MyBatis Plus 自动配置 | <a href="https://baomidou.com/" target="_blank">MyBatis Plus</a>:3.5.5<br /><a href="https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611" target="_blank">dynamic-datasource-spring-boot-starter</a>:4.2.0<br /><a href="https://github.com/p6spy/p6spy" target="_blank">P6Spy</a>:3.9.1 |
|
||||
| continew-starter-auth-satoken | 认证模块:SaToken 自动配置 | <a href="https://sa-token.dev33.cn/" target="_blank">Sa-Token</a>:1.37.0 |
|
||||
| continew-starter-auth-justauth | 认证模块:JustAuth 自动配置 | <a href="https://justauth.cn/" target="_blank">Just Auth</a>:1.16.6 |
|
||||
| continew-starter-messaging-mail | 消息模块:邮件 | Jakarta Mail:1.1.0 |
|
||||
| continew-starter-messaging-sms | 消息模块:短信 | <a href="https://sms4j.com/" target="_blank">SMS4J</a>:3.0.4 |
|
||||
| continew-starter-extension-crud | 扩展模块:BaseController 自定义 CRUD API 封装 | |
|
||||
### 核心模块
|
||||
|
||||
| 模块名称 | 模块说明 | 依赖版本 |
|
||||
| --------------------- | ------------------------------------ | ------------------------------------------------------------ |
|
||||
| continew-starter-core | 核心模块:包含线程池、项目等自动配置 | <a href="https://spring.io/projects/spring-boot" target="_blank">Spring Boot</a>:3.1.8<br /><a href="https://www.hutool.cn/" target="_blank">Hutool</a>:5.8.25<br />mica-ip2region:3.1.7 |
|
||||
|
||||
### JSON模块
|
||||
|
||||
| 模块名称 | 模块说明 | 依赖版本 |
|
||||
| ----------------------------- | -------------------- | --------------- |
|
||||
| continew-starter-json-jackson | Jackson 序列化等配置 | Jackson:2.15.3 |
|
||||
|
||||
### 接口文档
|
||||
|
||||
| 模块名称 | 模块说明 | 依赖版本 |
|
||||
| ------------------------ | ---------------- | ------------------------------------------------------------ |
|
||||
| continew-starter-api-doc | Knife4j 自动配置 | <a href="https://doc.xiaominfo.com/" target="_blank">Knife4j</a>:4.5.0 |
|
||||
|
||||
### 安全模块
|
||||
|
||||
| 模块名称 | 模块说明 | 依赖版本 |
|
||||
| ---------------------------------- | ----------------- | -------- |
|
||||
| continew-starter-security-password | 密码编码器 | |
|
||||
| continew-starter-security-mask | JSON 脱敏 | |
|
||||
| continew-starter-security-crypto | 数据库字段加/解密 | |
|
||||
| continew-starter-security-all | | |
|
||||
|
||||
### Web模块
|
||||
|
||||
| 模块名称 | 模块说明 | 依赖版本 |
|
||||
| -------------------- | ---------------------------------- | ------------------------------------------------------------ |
|
||||
| continew-starter-web | 跨域、全局异常、错误处理等自动配置 | <a href="https://undertow.io/" target="_blank">Undertow</a>:2.3.10.Final<br />TLog:1.5.1 |
|
||||
|
||||
### 日志模块
|
||||
|
||||
| 模块名称 | 模块说明 | 依赖版本 |
|
||||
| ---------------------------------- | ----------------------------------------- | -------- |
|
||||
| continew-starter-log-httptrace-pro | Spring Boot Actuator HttpTrace 重置增强版 | |
|
||||
|
||||
### 存储模块
|
||||
|
||||
| 模块名称 | 模块说明 | 依赖版本 |
|
||||
| ------------------------------ | -------- | -------- |
|
||||
| continew-starter-storage-local | 本地存储 | |
|
||||
|
||||
### 文件处理模块
|
||||
|
||||
| 模块名称 | 模块说明 | 依赖版本 |
|
||||
| --------------------------- | -------------- | ------------------------------------------------------------ |
|
||||
| continew-starter-file-excel | Excel 相关配置 | <a href="https://easyexcel.opensource.alibaba.com/" target="_blank">Easy Excel</a>:3.3.3 |
|
||||
|
||||
### 验证码模块
|
||||
|
||||
| 模块名称 | 模块说明 | 依赖版本 |
|
||||
| --------------------------------- | ---------- | ------------------- |
|
||||
| continew-starter-captcha-graphic | 图形验证码 | Easy Captcha:1.6.2 |
|
||||
| continew-starter-captcha-behavior | 行为验证码 | AJ-Captcha:1.3.0 |
|
||||
|
||||
### 缓存模块
|
||||
|
||||
| 模块名称 | 模块说明 | 依赖版本 |
|
||||
| ---------------------------------- | --------------------- | ------------------------------------------------------------ |
|
||||
| continew-starter-cache-redisson | Redisson 自动配置 | <a href="https://github.com/redisson/redisson/wiki/Redisson%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D" target="_blank">Redisson</a>:3.26.0 |
|
||||
| continew-starter-cache-springcache | Spring Cache 自动配置 | |
|
||||
| continew-starter-cache-jetcache | JetCache 自动配置 | |
|
||||
|
||||
### 数据访问模块
|
||||
|
||||
| 模块名称 | 模块说明 | 依赖版本 |
|
||||
| ---------------------------------- | --------------------- | ------------------------------------------------------------ |
|
||||
| continew-starter-data-mybatis-plus | MyBatis Plus 自动配置 | <a href="https://baomidou.com/" target="_blank">MyBatis Plus</a>:3.5.5<br /><a href="https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611" target="_blank">dynamic-datasource-spring-boot-starter</a>:4.3.0<br /><a href="https://github.com/p6spy/p6spy" target="_blank">P6Spy</a>:3.9.1 |
|
||||
|
||||
### 认证模块
|
||||
|
||||
| 模块名称 | 模块说明 | 依赖版本 |
|
||||
| ------------------------------ | ----------------- | ------------------------------------------------------------ |
|
||||
| continew-starter-auth-satoken | SaToken 自动配置 | <a href="https://sa-token.dev33.cn/" target="_blank">Sa-Token</a>:1.37.0 |
|
||||
| continew-starter-auth-justauth | JustAuth 自动配置 | <a href="https://justauth.cn/" target="_blank">Just Auth</a>:1.16.6 |
|
||||
|
||||
### 消息模块
|
||||
|
||||
| 模块名称 | 模块说明 | 依赖版本 |
|
||||
| ------------------------------- | -------- | ------------------------------------------------------------ |
|
||||
| continew-starter-messaging-mail | 邮件 | Jakarta Mail:1.1.0 |
|
||||
| continew-starter-messaging-sms | 短信 | <a href="https://sms4j.com/" target="_blank">SMS4J</a>:3.1.1 |
|
||||
|
||||
### 扩展模块
|
||||
|
||||
| 模块名称 | 模块说明 | 依赖版本 |
|
||||
| ------------------------------- | --------------------------------------------- | -------- |
|
||||
| continew-starter-extension-crud | 扩展模块:BaseController 自定义 CRUD API 封装 | |
|
||||
|
||||
## 贡献代码
|
||||
|
||||
|
||||
@@ -23,5 +23,12 @@
|
||||
<groupId>com.xkcoding.justauth</groupId>
|
||||
<artifactId>justauth-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 缓存模块 - Redisson -->
|
||||
<dependency>
|
||||
<groupId>top.charles7c.continew</groupId>
|
||||
<artifactId>continew-starter-cache-redisson</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -25,6 +25,13 @@
|
||||
<artifactId>sa-token-jwt</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 缓存模块 - Redisson -->
|
||||
<dependency>
|
||||
<groupId>top.charles7c.continew</groupId>
|
||||
<artifactId>continew-starter-cache-redisson</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 模块 -->
|
||||
<dependency>
|
||||
<groupId>top.charles7c.continew</groupId>
|
||||
|
||||
@@ -21,6 +21,7 @@ import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
|
||||
import cn.dev33.satoken.router.SaRouter;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -58,7 +59,7 @@ public class SaTokenAutoConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(saInterceptor()).addPathPatterns(StringConstants.PATH_PATTERN);
|
||||
registry.addInterceptor(SpringUtil.getBean(SaInterceptor.class)).addPathPatterns(StringConstants.PATH_PATTERN);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,6 +87,7 @@ public class SaTokenAutoConfiguration implements WebMvcConfigurer {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(prefix = "sa-token.extension", name = "enableJwt", havingValue = "true")
|
||||
public StpLogic stpLogic() {
|
||||
return new StpLogicJwtForSimple();
|
||||
}
|
||||
|
||||
@@ -34,6 +34,11 @@ public class SaTokenExtensionProperties {
|
||||
*/
|
||||
private boolean enabled = false;
|
||||
|
||||
/**
|
||||
* 启用 JWT
|
||||
*/
|
||||
private boolean enableJwt = false;
|
||||
|
||||
/**
|
||||
* 持久层配置
|
||||
*/
|
||||
@@ -54,6 +59,14 @@ public class SaTokenExtensionProperties {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public boolean isEnableJwt() {
|
||||
return enableJwt;
|
||||
}
|
||||
|
||||
public void setEnableJwt(boolean enableJwt) {
|
||||
this.enableJwt = enableJwt;
|
||||
}
|
||||
|
||||
public SaTokenDaoProperties getDao() {
|
||||
return dao;
|
||||
}
|
||||
|
||||
@@ -47,8 +47,6 @@ public class SaTokenDaoConfiguration {
|
||||
* 自定义持久层实现-默认(内存)
|
||||
*/
|
||||
@ConditionalOnMissingBean(SaTokenDao.class)
|
||||
@ConditionalOnClass(RedisClient.class)
|
||||
@AutoConfigureBefore(RedissonAutoConfiguration.class)
|
||||
@ConditionalOnProperty(name = "sa-token.extension.dao.type", havingValue = "default", matchIfMissing = true)
|
||||
public static class Default {
|
||||
static {
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
</modules>
|
||||
|
||||
<dependencies>
|
||||
<!-- 缓存模块 - Redisson -->
|
||||
<!-- 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>top.charles7c.continew</groupId>
|
||||
<artifactId>continew-starter-cache-redisson</artifactId>
|
||||
<artifactId>continew-starter-core</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -18,7 +18,7 @@ package top.charles7c.continew.starter.cache.redisson.autoconfigure;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.redisson.codec.JsonJacksonCodec;
|
||||
import org.redisson.config.ClusterServersConfig;
|
||||
@@ -51,10 +51,11 @@ import java.util.List;
|
||||
public class RedissonAutoConfiguration {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(RedissonAutoConfiguration.class);
|
||||
|
||||
private final RedissonProperties properties;
|
||||
private final RedisProperties redisProperties;
|
||||
private final ObjectMapper objectMapper;
|
||||
private static final String REDIS_PROTOCOL_PREFIX = "redis://";
|
||||
private static final String REDISS_PROTOCOL_PREFIX = "rediss://";
|
||||
|
||||
public RedissonAutoConfiguration(RedissonProperties properties,
|
||||
RedisProperties redisProperties,
|
||||
@@ -68,11 +69,13 @@ public class RedissonAutoConfiguration {
|
||||
public RedissonAutoConfigurationCustomizer redissonAutoConfigurationCustomizer() {
|
||||
return config -> {
|
||||
RedissonProperties.Mode mode = properties.getMode();
|
||||
String protocol = redisProperties.getSsl().isEnabled() ? "rediss://" : "redis://";
|
||||
String protocolPrefix = redisProperties.getSsl().isEnabled()
|
||||
? REDISS_PROTOCOL_PREFIX
|
||||
: REDIS_PROTOCOL_PREFIX;
|
||||
switch (mode) {
|
||||
case CLUSTER -> this.buildClusterModeConfig(config, protocol);
|
||||
case SENTINEL -> this.buildSentinelModeConfig(config, protocol);
|
||||
default -> this.buildSingleModeConfig(config, protocol);
|
||||
case CLUSTER -> this.buildClusterModeConfig(config, protocolPrefix);
|
||||
case SENTINEL -> this.buildSentinelModeConfig(config, protocolPrefix);
|
||||
default -> this.buildSingleModeConfig(config, protocolPrefix);
|
||||
}
|
||||
// Jackson 处理
|
||||
config.setCodec(new JsonJacksonCodec(objectMapper));
|
||||
@@ -83,10 +86,10 @@ public class RedissonAutoConfiguration {
|
||||
/**
|
||||
* 构建集群模式配置
|
||||
*
|
||||
* @param config 配置
|
||||
* @param protocol 协议
|
||||
* @param config 配置
|
||||
* @param protocolPrefix 协议前缀
|
||||
*/
|
||||
private void buildClusterModeConfig(Config config, String protocol) {
|
||||
private void buildClusterModeConfig(Config config, String protocolPrefix) {
|
||||
ClusterServersConfig clusterServersConfig = config.useClusterServers();
|
||||
ClusterServersConfig customClusterServersConfig = properties.getClusterServersConfig();
|
||||
if (null != customClusterServersConfig) {
|
||||
@@ -96,9 +99,9 @@ public class RedissonAutoConfiguration {
|
||||
// 下方配置如果为空,则使用 Redis 的配置
|
||||
if (CollUtil.isEmpty(clusterServersConfig.getNodeAddresses())) {
|
||||
List<String> nodeList = redisProperties.getCluster().getNodes();
|
||||
nodeList.stream().map(node -> protocol + node).forEach(clusterServersConfig::addNodeAddress);
|
||||
nodeList.stream().map(node -> protocolPrefix + node).forEach(clusterServersConfig::addNodeAddress);
|
||||
}
|
||||
if (StrUtil.isBlank(clusterServersConfig.getPassword())) {
|
||||
if (CharSequenceUtil.isBlank(clusterServersConfig.getPassword())) {
|
||||
clusterServersConfig.setPassword(redisProperties.getPassword());
|
||||
}
|
||||
}
|
||||
@@ -106,10 +109,10 @@ public class RedissonAutoConfiguration {
|
||||
/**
|
||||
* 构建哨兵模式配置
|
||||
*
|
||||
* @param config 配置
|
||||
* @param protocol 协议
|
||||
* @param config 配置
|
||||
* @param protocolPrefix 协议前缀
|
||||
*/
|
||||
private void buildSentinelModeConfig(Config config, String protocol) {
|
||||
private void buildSentinelModeConfig(Config config, String protocolPrefix) {
|
||||
SentinelServersConfig sentinelServersConfig = config.useSentinelServers();
|
||||
SentinelServersConfig customSentinelServersConfig = properties.getSentinelServersConfig();
|
||||
if (null != customSentinelServersConfig) {
|
||||
@@ -119,12 +122,12 @@ public class RedissonAutoConfiguration {
|
||||
// 下方配置如果为空,则使用 Redis 的配置
|
||||
if (CollUtil.isEmpty(sentinelServersConfig.getSentinelAddresses())) {
|
||||
List<String> nodeList = redisProperties.getSentinel().getNodes();
|
||||
nodeList.stream().map(node -> protocol + node).forEach(sentinelServersConfig::addSentinelAddress);
|
||||
nodeList.stream().map(node -> protocolPrefix + node).forEach(sentinelServersConfig::addSentinelAddress);
|
||||
}
|
||||
if (StrUtil.isBlank(sentinelServersConfig.getPassword())) {
|
||||
if (CharSequenceUtil.isBlank(sentinelServersConfig.getPassword())) {
|
||||
sentinelServersConfig.setPassword(redisProperties.getPassword());
|
||||
}
|
||||
if (StrUtil.isBlank(sentinelServersConfig.getMasterName())) {
|
||||
if (CharSequenceUtil.isBlank(sentinelServersConfig.getMasterName())) {
|
||||
sentinelServersConfig.setMasterName(redisProperties.getSentinel().getMaster());
|
||||
}
|
||||
}
|
||||
@@ -132,10 +135,10 @@ public class RedissonAutoConfiguration {
|
||||
/**
|
||||
* 构建单机模式配置
|
||||
*
|
||||
* @param config 配置
|
||||
* @param protocol 协议
|
||||
* @param config 配置
|
||||
* @param protocolPrefix 协议前缀
|
||||
*/
|
||||
private void buildSingleModeConfig(Config config, String protocol) {
|
||||
private void buildSingleModeConfig(Config config, String protocolPrefix) {
|
||||
SingleServerConfig singleServerConfig = config.useSingleServer();
|
||||
SingleServerConfig customSingleServerConfig = properties.getSingleServerConfig();
|
||||
if (null != customSingleServerConfig) {
|
||||
@@ -143,12 +146,12 @@ public class RedissonAutoConfiguration {
|
||||
}
|
||||
// 下方配置如果为空,则使用 Redis 的配置
|
||||
singleServerConfig.setDatabase(redisProperties.getDatabase());
|
||||
if (StrUtil.isBlank(singleServerConfig.getPassword())) {
|
||||
if (CharSequenceUtil.isBlank(singleServerConfig.getPassword())) {
|
||||
singleServerConfig.setPassword(redisProperties.getPassword());
|
||||
}
|
||||
if (StrUtil.isBlank(singleServerConfig.getAddress())) {
|
||||
singleServerConfig.setAddress(protocol + redisProperties.getHost() + StringConstants.COLON + redisProperties
|
||||
.getPort());
|
||||
if (CharSequenceUtil.isBlank(singleServerConfig.getAddress())) {
|
||||
singleServerConfig.setAddress(protocolPrefix + redisProperties
|
||||
.getHost() + StringConstants.COLON + redisProperties.getPort());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package top.charles7c.continew.starter.cache.springcache.autoconfigure;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
@@ -92,7 +92,7 @@ public class SpringCacheAutoConfiguration implements CachingConfigurer {
|
||||
@Override
|
||||
public KeyGenerator keyGenerator() {
|
||||
return (target, method, params) -> {
|
||||
String key = StrUtil.toUnderlineCase(method.getName()).toUpperCase();
|
||||
String key = CharSequenceUtil.toUnderlineCase(method.getName()).toUpperCase();
|
||||
Map<String, Object> paramMap = MapUtil.newHashMap(params.length);
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
paramMap.put(String.valueOf(i), params[i]);
|
||||
|
||||
@@ -18,7 +18,7 @@ package top.charles7c.continew.starter.captcha.behavior.autoconfigure;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import com.anji.captcha.model.common.Const;
|
||||
import com.anji.captcha.service.CaptchaService;
|
||||
import com.anji.captcha.service.impl.CaptchaServiceFactory;
|
||||
@@ -81,8 +81,8 @@ public class BehaviorCaptchaAutoConfiguration {
|
||||
config.put(Const.CAPTCHA_FONT_TYPE, properties.getFontType());
|
||||
config.put(Const.CAPTCHA_TYPE, properties.getType().getCodeValue());
|
||||
config.put(Const.CAPTCHA_INTERFERENCE_OPTIONS, properties.getInterferenceOptions());
|
||||
config.put(Const.ORIGINAL_PATH_JIGSAW, StrUtil.emptyIfNull(properties.getJigsawBaseMapPath()));
|
||||
config.put(Const.ORIGINAL_PATH_PIC_CLICK, StrUtil.emptyIfNull(properties.getPicClickBaseMapPath()));
|
||||
config.put(Const.ORIGINAL_PATH_JIGSAW, CharSequenceUtil.emptyIfNull(properties.getJigsawBaseMapPath()));
|
||||
config.put(Const.ORIGINAL_PATH_PIC_CLICK, CharSequenceUtil.emptyIfNull(properties.getPicClickBaseMapPath()));
|
||||
config.put(Const.CAPTCHA_SLIP_OFFSET, properties.getSlipOffset());
|
||||
config.put(Const.CAPTCHA_AES_STATUS, String.valueOf(properties.getEnableAes()));
|
||||
config.put(Const.CAPTCHA_WATER_FONT, properties.getWaterFont());
|
||||
@@ -98,8 +98,8 @@ public class BehaviorCaptchaAutoConfiguration {
|
||||
config.put(Const.CAPTCHA_FONT_SIZE, properties.getFontSize());
|
||||
config.put(Const.CAPTCHA_FONT_STYLE, properties.getFontStyle());
|
||||
config.put(Const.CAPTCHA_WORD_COUNT, 4);
|
||||
if (StrUtil.startWith(properties.getJigsawBaseMapPath(), "classpath:") || StrUtil.startWith(properties
|
||||
.getPicClickBaseMapPath(), "classpath:")) {
|
||||
if (CharSequenceUtil.startWith(properties.getJigsawBaseMapPath(), "classpath:") || CharSequenceUtil
|
||||
.startWith(properties.getPicClickBaseMapPath(), "classpath:")) {
|
||||
// 自定义 resources 目录下初始化底图
|
||||
config.put(Const.CAPTCHA_INIT_ORIGINAL, true);
|
||||
initializeBaseMap(properties.getJigsawBaseMapPath(), properties.getPicClickBaseMapPath());
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
package top.charles7c.continew.starter.captcha.graphic.autoconfigure;
|
||||
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.wf.captcha.base.Captcha;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -27,10 +24,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import top.charles7c.continew.starter.captcha.graphic.core.GraphicCaptchaService;
|
||||
import top.charles7c.continew.starter.core.constant.PropertiesConstants;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* 图形验证码自动配置
|
||||
*
|
||||
@@ -39,24 +35,18 @@ import java.awt.*;
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@EnableConfigurationProperties(GraphicCaptchaProperties.class)
|
||||
@ConditionalOnProperty(prefix = PropertiesConstants.CAPTCHA_GRAPHIC, name = PropertiesConstants.ENABLED, havingValue = "true")
|
||||
@ConditionalOnProperty(prefix = PropertiesConstants.CAPTCHA_GRAPHIC, name = PropertiesConstants.ENABLED, havingValue = "true", matchIfMissing = true)
|
||||
public class GraphicCaptchaAutoConfiguration {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(GraphicCaptchaAutoConfiguration.class);
|
||||
|
||||
/**
|
||||
* 验证码配置
|
||||
* 验证码服务接口配置
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public Captcha captcha(GraphicCaptchaProperties properties) {
|
||||
Captcha captcha = ReflectUtil.newInstance(properties.getType().getCaptchaImpl(), properties
|
||||
.getWidth(), properties.getHeight());
|
||||
captcha.setLen(properties.getLength());
|
||||
if (StrUtil.isNotBlank(properties.getFontName())) {
|
||||
captcha.setFont(new Font(properties.getFontName(), Font.PLAIN, properties.getFontSize()));
|
||||
}
|
||||
return captcha;
|
||||
public GraphicCaptchaService graphicCaptchaService(GraphicCaptchaProperties properties) {
|
||||
return new GraphicCaptchaService(properties);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
|
||||
@@ -32,7 +32,7 @@ public class GraphicCaptchaProperties {
|
||||
/**
|
||||
* 是否启用图形验证码
|
||||
*/
|
||||
private boolean enabled = false;
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.captcha.graphic.core;
|
||||
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import com.wf.captcha.base.Captcha;
|
||||
import top.charles7c.continew.starter.captcha.graphic.autoconfigure.GraphicCaptchaProperties;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* 图形验证码服务接口
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class GraphicCaptchaService {
|
||||
|
||||
private final GraphicCaptchaProperties properties;
|
||||
|
||||
public GraphicCaptchaService(GraphicCaptchaProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证码实例
|
||||
*
|
||||
* @return 验证码实例
|
||||
*/
|
||||
public Captcha getCaptcha() {
|
||||
Captcha captcha = ReflectUtil.newInstance(properties.getType().getCaptchaImpl(), properties
|
||||
.getWidth(), properties.getHeight());
|
||||
captcha.setLen(properties.getLength());
|
||||
if (CharSequenceUtil.isNotBlank(properties.getFontName())) {
|
||||
captcha.setFont(new Font(properties.getFontName(), Font.PLAIN, properties.getFontSize()));
|
||||
}
|
||||
return captcha;
|
||||
}
|
||||
}
|
||||
@@ -52,12 +52,17 @@ public class PropertiesConstants {
|
||||
/**
|
||||
* 安全配置
|
||||
*/
|
||||
public static final String SECURITY = CONTINEW_STARTER + ".security";
|
||||
public static final String SECURITY = CONTINEW_STARTER + StringConstants.DOT + "security";
|
||||
|
||||
/**
|
||||
* 密码编解码配置
|
||||
*/
|
||||
public static final String PASSWORD = SECURITY + ".password";
|
||||
public static final String PASSWORD = SECURITY + StringConstants.DOT + "password";
|
||||
|
||||
/**
|
||||
* 加/解密配置
|
||||
*/
|
||||
public static final String CRYPTO = SECURITY + StringConstants.DOT + "crypto";
|
||||
|
||||
/**
|
||||
* Web 配置
|
||||
|
||||
@@ -104,6 +104,11 @@ public class StringConstants {
|
||||
*/
|
||||
public static final char C_AT = CharPool.AT;
|
||||
|
||||
/**
|
||||
* 字符常量:星号 {@code '*'}
|
||||
*/
|
||||
public static final char C_ASTERISK = '*';
|
||||
|
||||
/**
|
||||
* 字符串常量:制表符 {@code "\t"}
|
||||
*/
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package top.charles7c.continew.starter.core.util;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import org.springframework.boot.env.YamlPropertySourceLoader;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.io.Resource;
|
||||
@@ -43,7 +43,7 @@ public class GeneralPropertySourceFactory extends DefaultPropertySourceFactory {
|
||||
EncodedResource encodedResource) throws IOException {
|
||||
Resource resource = encodedResource.getResource();
|
||||
String resourceName = resource.getFilename();
|
||||
if (StrUtil.isNotBlank(resourceName) && StrUtil.endWithAny(resourceName, ".yml", ".yaml")) {
|
||||
if (CharSequenceUtil.isNotBlank(resourceName) && CharSequenceUtil.endWithAny(resourceName, ".yml", ".yaml")) {
|
||||
return new YamlPropertySourceLoader().load(resourceName, resource).get(0);
|
||||
}
|
||||
return super.createPropertySource(name, encodedResource);
|
||||
|
||||
@@ -18,7 +18,7 @@ package top.charles7c.continew.starter.core.util;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.net.NetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import net.dreamlu.mica.ip2region.core.Ip2regionSearcher;
|
||||
@@ -52,7 +52,7 @@ public class IpUtils {
|
||||
IpInfo ipInfo = ip2regionSearcher.memorySearch(ip);
|
||||
if (null != ipInfo) {
|
||||
Set<String> regionSet = CollUtil.newLinkedHashSet(ipInfo.getAddress(), ipInfo.getIsp());
|
||||
regionSet.removeIf(StrUtil::isBlank);
|
||||
regionSet.removeIf(CharSequenceUtil::isBlank);
|
||||
return String.join(StringConstants.SPACE, regionSet);
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package top.charles7c.continew.starter.core.util.db;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.db.Db;
|
||||
import cn.hutool.db.Entity;
|
||||
import cn.hutool.db.meta.Column;
|
||||
@@ -61,7 +61,7 @@ public class MetaUtils {
|
||||
String querySql = "SHOW TABLE STATUS";
|
||||
List<Entity> tableEntityList;
|
||||
Db db = Db.use(dataSource);
|
||||
if (StrUtil.isNotBlank(tableName)) {
|
||||
if (CharSequenceUtil.isNotBlank(tableName)) {
|
||||
tableEntityList = db.query(String.format("%s WHERE NAME = ?", querySql), tableName);
|
||||
} else {
|
||||
tableEntityList = db.query(querySql);
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package top.charles7c.continew.starter.core.util.validate;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import top.charles7c.continew.starter.core.constant.StringConstants;
|
||||
import top.charles7c.continew.starter.core.exception.BusinessException;
|
||||
|
||||
@@ -45,7 +45,7 @@ public class CheckUtils extends Validator {
|
||||
* @param fieldValue 字段值
|
||||
*/
|
||||
public static void throwIfNotExists(Object obj, String entityName, String fieldName, Object fieldValue) {
|
||||
String message = String.format("%s 为 [%s] 的 %s 记录已不存在", fieldName, fieldValue, StrUtil
|
||||
String message = String.format("%s 为 [%s] 的 %s 记录已不存在", fieldName, fieldValue, CharSequenceUtil
|
||||
.replace(entityName, "DO", StringConstants.EMPTY));
|
||||
throwIfNull(obj, message, EXCEPTION_TYPE);
|
||||
}
|
||||
@@ -58,7 +58,7 @@ public class CheckUtils extends Validator {
|
||||
* @param params 参数值
|
||||
*/
|
||||
public static void throwIfNull(Object obj, String template, Object... params) {
|
||||
throwIfNull(obj, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIfNull(obj, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +69,7 @@ public class CheckUtils extends Validator {
|
||||
* @param params 参数值
|
||||
*/
|
||||
public static void throwIfNotNull(Object obj, String template, Object... params) {
|
||||
throwIfNotNull(obj, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIfNotNull(obj, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,7 +93,7 @@ public class CheckUtils extends Validator {
|
||||
* @param params 参数值
|
||||
*/
|
||||
public static void throwIfEmpty(Object obj, String template, Object... params) {
|
||||
throwIfEmpty(obj, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIfEmpty(obj, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,7 +104,7 @@ public class CheckUtils extends Validator {
|
||||
* @param params 参数值
|
||||
*/
|
||||
public static void throwIfNotEmpty(Object obj, String template, Object... params) {
|
||||
throwIfNotEmpty(obj, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIfNotEmpty(obj, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,7 +115,7 @@ public class CheckUtils extends Validator {
|
||||
* @param params 参数值
|
||||
*/
|
||||
public static void throwIfBlank(CharSequence str, String template, Object... params) {
|
||||
throwIfBlank(str, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIfBlank(str, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,7 +126,7 @@ public class CheckUtils extends Validator {
|
||||
* @param params 参数值
|
||||
*/
|
||||
public static void throwIfNotBlank(CharSequence str, String template, Object... params) {
|
||||
throwIfNotBlank(str, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIfNotBlank(str, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,7 +138,7 @@ public class CheckUtils extends Validator {
|
||||
* @param params 参数值
|
||||
*/
|
||||
public static void throwIfEqual(Object obj1, Object obj2, String template, Object... params) {
|
||||
throwIfEqual(obj1, obj2, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIfEqual(obj1, obj2, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,7 +150,7 @@ public class CheckUtils extends Validator {
|
||||
* @param params 参数值
|
||||
*/
|
||||
public static void throwIfNotEqual(Object obj1, Object obj2, String template, Object... params) {
|
||||
throwIfNotEqual(obj1, obj2, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIfNotEqual(obj1, obj2, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,7 +162,7 @@ public class CheckUtils extends Validator {
|
||||
* @param params 参数值
|
||||
*/
|
||||
public static void throwIfEqualIgnoreCase(CharSequence str1, CharSequence str2, String template, Object... params) {
|
||||
throwIfEqualIgnoreCase(str1, str2, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIfEqualIgnoreCase(str1, str2, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,7 +177,7 @@ public class CheckUtils extends Validator {
|
||||
CharSequence str2,
|
||||
String template,
|
||||
Object... params) {
|
||||
throwIfNotEqualIgnoreCase(str1, str2, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIfNotEqualIgnoreCase(str1, str2, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,7 +188,7 @@ public class CheckUtils extends Validator {
|
||||
* @param params 参数值
|
||||
*/
|
||||
public static void throwIf(boolean condition, String template, Object... params) {
|
||||
throwIf(condition, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIf(condition, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,6 +199,6 @@ public class CheckUtils extends Validator {
|
||||
* @param params 参数值
|
||||
*/
|
||||
public static void throwIf(BooleanSupplier conditionSupplier, String template, Object... params) {
|
||||
throwIf(conditionSupplier, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIf(conditionSupplier, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package top.charles7c.continew.starter.core.util.validate;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import top.charles7c.continew.starter.core.exception.BadRequestException;
|
||||
|
||||
import java.util.function.BooleanSupplier;
|
||||
@@ -43,7 +43,7 @@ public class ValidationUtils extends Validator {
|
||||
* @param params 参数值
|
||||
*/
|
||||
public static void throwIfNull(Object obj, String template, Object... params) {
|
||||
throwIfNull(obj, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIfNull(obj, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,7 +54,7 @@ public class ValidationUtils extends Validator {
|
||||
* @param params 参数值
|
||||
*/
|
||||
public static void throwIfNotNull(Object obj, String template, Object... params) {
|
||||
throwIfNotNull(obj, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIfNotNull(obj, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,7 +65,7 @@ public class ValidationUtils extends Validator {
|
||||
* @param params 参数值
|
||||
*/
|
||||
public static void throwIfEmpty(Object obj, String template, Object... params) {
|
||||
throwIfEmpty(obj, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIfEmpty(obj, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,7 +76,7 @@ public class ValidationUtils extends Validator {
|
||||
* @param params 参数值
|
||||
*/
|
||||
public static void throwIfNotEmpty(Object obj, String template, Object... params) {
|
||||
throwIfNotEmpty(obj, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIfNotEmpty(obj, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,7 +87,7 @@ public class ValidationUtils extends Validator {
|
||||
* @param params 参数值
|
||||
*/
|
||||
public static void throwIfBlank(CharSequence str, String template, Object... params) {
|
||||
throwIfBlank(str, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIfBlank(str, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,7 +98,7 @@ public class ValidationUtils extends Validator {
|
||||
* @param params 参数值
|
||||
*/
|
||||
public static void throwIfNotBlank(CharSequence str, String template, Object... params) {
|
||||
throwIfNotBlank(str, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIfNotBlank(str, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,7 +110,7 @@ public class ValidationUtils extends Validator {
|
||||
* @param params 参数值
|
||||
*/
|
||||
public static void throwIfEqual(Object obj1, Object obj2, String template, Object... params) {
|
||||
throwIfEqual(obj1, obj2, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIfEqual(obj1, obj2, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,7 +122,7 @@ public class ValidationUtils extends Validator {
|
||||
* @param params 参数值
|
||||
*/
|
||||
public static void throwIfNotEqual(Object obj1, Object obj2, String template, Object... params) {
|
||||
throwIfNotEqual(obj1, obj2, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIfNotEqual(obj1, obj2, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,7 +134,7 @@ public class ValidationUtils extends Validator {
|
||||
* @param params 参数值
|
||||
*/
|
||||
public static void throwIfEqualIgnoreCase(CharSequence str1, CharSequence str2, String template, Object... params) {
|
||||
throwIfEqualIgnoreCase(str1, str2, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIfEqualIgnoreCase(str1, str2, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,7 +149,7 @@ public class ValidationUtils extends Validator {
|
||||
CharSequence str2,
|
||||
String template,
|
||||
Object... params) {
|
||||
throwIfNotEqualIgnoreCase(str1, str2, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIfNotEqualIgnoreCase(str1, str2, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,7 +160,7 @@ public class ValidationUtils extends Validator {
|
||||
* @param params 参数值
|
||||
*/
|
||||
public static void throwIf(boolean condition, String template, Object... params) {
|
||||
throwIf(condition, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIf(condition, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,6 +171,6 @@ public class ValidationUtils extends Validator {
|
||||
* @param params 参数值
|
||||
*/
|
||||
public static void throwIf(BooleanSupplier conditionSupplier, String template, Object... params) {
|
||||
throwIf(conditionSupplier, StrUtil.format(template, params), EXCEPTION_TYPE);
|
||||
throwIf(conditionSupplier, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
package top.charles7c.continew.starter.core.util.validate;
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -90,7 +90,7 @@ public class Validator {
|
||||
protected static void throwIfBlank(CharSequence str,
|
||||
String message,
|
||||
Class<? extends RuntimeException> exceptionType) {
|
||||
throwIf(StrUtil.isBlank(str), message, exceptionType);
|
||||
throwIf(CharSequenceUtil.isBlank(str), message, exceptionType);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,7 +103,7 @@ public class Validator {
|
||||
protected static void throwIfNotBlank(CharSequence str,
|
||||
String message,
|
||||
Class<? extends RuntimeException> exceptionType) {
|
||||
throwIf(StrUtil.isNotBlank(str), message, exceptionType);
|
||||
throwIf(CharSequenceUtil.isNotBlank(str), message, exceptionType);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,7 +148,7 @@ public class Validator {
|
||||
CharSequence str2,
|
||||
String message,
|
||||
Class<? extends RuntimeException> exceptionType) {
|
||||
throwIf(StrUtil.equalsIgnoreCase(str1, str2), message, exceptionType);
|
||||
throwIf(CharSequenceUtil.equalsIgnoreCase(str1, str2), message, exceptionType);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,7 +163,7 @@ public class Validator {
|
||||
CharSequence str2,
|
||||
String message,
|
||||
Class<? extends RuntimeException> exceptionType) {
|
||||
throwIf(!StrUtil.equalsIgnoreCase(str1, str2), message, exceptionType);
|
||||
throwIf(!CharSequenceUtil.equalsIgnoreCase(str1, str2), message, exceptionType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,10 +31,11 @@
|
||||
<artifactId>p6spy</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- CosId(通用、灵活、高性能的分布式 ID 生成器) -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<groupId>me.ahoo.cosid</groupId>
|
||||
<artifactId>cosid-spring-boot-starter</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -18,6 +18,8 @@ package top.charles7c.continew.starter.data.mybatis.plus.autoconfigure;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||
import top.charles7c.continew.starter.data.mybatis.plus.autoconfigure.idgenerator.MyBatisPlusIdGeneratorProperties;
|
||||
|
||||
/**
|
||||
* MyBatis Plus 扩展配置属性
|
||||
@@ -41,6 +43,12 @@ public class MyBatisPlusExtensionProperties {
|
||||
*/
|
||||
private String mapperPackage;
|
||||
|
||||
/**
|
||||
* ID 生成器
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
private MyBatisPlusIdGeneratorProperties idGenerator;
|
||||
|
||||
/**
|
||||
* 数据权限插件配置
|
||||
*/
|
||||
@@ -144,6 +152,14 @@ public class MyBatisPlusExtensionProperties {
|
||||
this.mapperPackage = mapperPackage;
|
||||
}
|
||||
|
||||
public MyBatisPlusIdGeneratorProperties getIdGenerator() {
|
||||
return idGenerator;
|
||||
}
|
||||
|
||||
public void setIdGenerator(MyBatisPlusIdGeneratorProperties idGenerator) {
|
||||
this.idGenerator = idGenerator;
|
||||
}
|
||||
|
||||
public DataPermissionProperties getDataPermission() {
|
||||
return dataPermission;
|
||||
}
|
||||
|
||||
@@ -16,10 +16,7 @@
|
||||
|
||||
package top.charles7c.continew.starter.data.mybatis.plus.autoconfigure;
|
||||
|
||||
import cn.hutool.core.net.NetUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
|
||||
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
|
||||
@@ -34,10 +31,13 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
import top.charles7c.continew.starter.core.constant.PropertiesConstants;
|
||||
import top.charles7c.continew.starter.core.util.GeneralPropertySourceFactory;
|
||||
import top.charles7c.continew.starter.data.mybatis.plus.autoconfigure.idgenerator.MyBatisPlusIdGeneratorConfiguration;
|
||||
import top.charles7c.continew.starter.data.mybatis.plus.datapermission.DataPermissionFilter;
|
||||
import top.charles7c.continew.starter.data.mybatis.plus.datapermission.DataPermissionHandlerImpl;
|
||||
|
||||
@@ -81,6 +81,15 @@ public class MybatisPlusAutoConfiguration {
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* ID 生成器配置
|
||||
*/
|
||||
@Configuration
|
||||
@Import({MyBatisPlusIdGeneratorConfiguration.Default.class, MyBatisPlusIdGeneratorConfiguration.CosId.class,
|
||||
MyBatisPlusIdGeneratorConfiguration.Custom.class})
|
||||
protected static class MyBatisPlusIdGeneratorAutoConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据权限处理器
|
||||
*/
|
||||
@@ -91,18 +100,6 @@ public class MybatisPlusAutoConfiguration {
|
||||
return new DataPermissionHandlerImpl(dataPermissionFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* ID 生成器配置(仅在主键类型(idType)配置为 ASSIGN_ID 或 ASSIGN_UUID 时有效)
|
||||
* <p>
|
||||
* 使用网卡信息绑定雪花生成器,防止集群雪花 ID 重复
|
||||
* </p>
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public IdentifierGenerator identifierGenerator() {
|
||||
return new DefaultIdentifierGenerator(NetUtil.getLocalhost());
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页插件配置(<a href="https://baomidou.com/pages/97710a/#paginationinnerinterceptor">PaginationInnerInterceptor</a>)
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.data.mybatis.plus.autoconfigure.idgenerator;
|
||||
|
||||
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
|
||||
import me.ahoo.cosid.snowflake.SnowflakeId;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
|
||||
/**
|
||||
* MyBatis Plus ID 生成器 - CosId
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class MyBatisPlusCosIdIdentifierGenerator implements IdentifierGenerator {
|
||||
|
||||
@Qualifier("__share__SnowflakeId")
|
||||
@Lazy
|
||||
@Autowired
|
||||
private SnowflakeId snowflakeId;
|
||||
|
||||
@Override
|
||||
public Number nextId(Object entity) {
|
||||
return snowflakeId.generate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.data.mybatis.plus.autoconfigure.idgenerator;
|
||||
|
||||
import cn.hutool.core.net.NetUtil;
|
||||
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
|
||||
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
|
||||
import me.ahoo.cosid.IdGenerator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.ResolvableType;
|
||||
|
||||
/**
|
||||
* MyBatis ID 生成器配置
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class MyBatisPlusIdGeneratorConfiguration {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MyBatisPlusIdGeneratorConfiguration.class);
|
||||
|
||||
private MyBatisPlusIdGeneratorConfiguration() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义 ID 生成器-默认(雪花算法,使用网卡信息绑定雪花生成器,防止集群雪花 ID 重复)
|
||||
*/
|
||||
@ConditionalOnMissingBean(IdentifierGenerator.class)
|
||||
@ConditionalOnProperty(name = "mybatis-plus.extension.id-generator.type", havingValue = "default", matchIfMissing = true)
|
||||
public static class Default {
|
||||
static {
|
||||
log.debug("[ContiNew Starter] - Auto Configuration 'MyBatis Plus-IdGenerator-Default' completed initialization.");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IdentifierGenerator identifierGenerator() {
|
||||
return new DefaultIdentifierGenerator(NetUtil.getLocalhost());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义 ID 生成器-CosId
|
||||
*/
|
||||
@ConditionalOnMissingBean(IdentifierGenerator.class)
|
||||
@ConditionalOnClass(IdGenerator.class)
|
||||
@ConditionalOnProperty(name = "mybatis-plus.extension.id-generator.type", havingValue = "cosid")
|
||||
public static class CosId {
|
||||
static {
|
||||
log.debug("[ContiNew Starter] - Auto Configuration 'MyBatis Plus-IdGenerator-CosId' completed initialization.");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IdentifierGenerator identifierGenerator() {
|
||||
return new MyBatisPlusCosIdIdentifierGenerator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义 ID 生成器
|
||||
*/
|
||||
@ConditionalOnProperty(name = "mybatis-plus.extension.id-generator.type", havingValue = "custom")
|
||||
public static class Custom {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public IdentifierGenerator identifierGenerator() {
|
||||
if (log.isErrorEnabled()) {
|
||||
log.error("Consider defining a bean of type '{}' in your configuration.", ResolvableType
|
||||
.forClass(IdentifierGenerator.class));
|
||||
}
|
||||
throw new NoSuchBeanDefinitionException(IdentifierGenerator.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.data.mybatis.plus.autoconfigure.idgenerator;
|
||||
|
||||
import top.charles7c.continew.starter.data.mybatis.plus.enums.MyBatisPlusIdGeneratorType;
|
||||
|
||||
/**
|
||||
* MyBatis ID 生成器配置属性
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class MyBatisPlusIdGeneratorProperties {
|
||||
|
||||
/**
|
||||
* ID 生成器类型
|
||||
*/
|
||||
private MyBatisPlusIdGeneratorType type = MyBatisPlusIdGeneratorType.DEFAULT;
|
||||
|
||||
public MyBatisPlusIdGeneratorType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(MyBatisPlusIdGeneratorType type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
@@ -16,18 +16,9 @@
|
||||
|
||||
package top.charles7c.continew.starter.data.mybatis.plus.datapermission;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||
import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import top.charles7c.continew.starter.core.constant.StringConstants;
|
||||
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
import net.sf.jsqlparser.expression.Function;
|
||||
import net.sf.jsqlparser.expression.LongValue;
|
||||
@@ -42,6 +33,13 @@ import net.sf.jsqlparser.schema.Table;
|
||||
import net.sf.jsqlparser.statement.select.PlainSelect;
|
||||
import net.sf.jsqlparser.statement.select.SelectExpressionItem;
|
||||
import net.sf.jsqlparser.statement.select.SubSelect;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import top.charles7c.continew.starter.core.constant.StringConstants;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 数据权限处理器实现类
|
||||
@@ -69,7 +67,7 @@ public class DataPermissionHandlerImpl implements DataPermissionHandler {
|
||||
for (Method method : methodArr) {
|
||||
DataPermission dataPermission = method.getAnnotation(DataPermission.class);
|
||||
String name = method.getName();
|
||||
if (null == dataPermission || !StrUtil.equalsAny(methodName, name, name + "_COUNT")) {
|
||||
if (null == dataPermission || !CharSequenceUtil.equalsAny(methodName, name, name + "_COUNT")) {
|
||||
continue;
|
||||
}
|
||||
if (dataPermissionFilter.isFilter()) {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.data.mybatis.plus.enums;
|
||||
|
||||
/**
|
||||
* MyBatis ID 生成器类型枚举
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public enum MyBatisPlusIdGeneratorType {
|
||||
|
||||
/**
|
||||
* 默认
|
||||
*/
|
||||
DEFAULT,
|
||||
|
||||
/**
|
||||
* CosId
|
||||
*/
|
||||
COSID,
|
||||
|
||||
/**
|
||||
* 自定义
|
||||
*/
|
||||
CUSTOM
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import org.slf4j.LoggerFactory;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import top.charles7c.continew.starter.core.exception.BadRequestException;
|
||||
import top.charles7c.continew.starter.core.util.ReflectUtils;
|
||||
@@ -117,7 +117,7 @@ public class QueryWrapperHelper {
|
||||
// 没有 @Query 注解,默认等值查询
|
||||
Query queryAnnotation = field.getAnnotation(Query.class);
|
||||
if (null == queryAnnotation) {
|
||||
return Collections.singletonList(q -> q.eq(StrUtil.toUnderlineCase(fieldName), fieldValue));
|
||||
return Collections.singletonList(q -> q.eq(CharSequenceUtil.toUnderlineCase(fieldName), fieldValue));
|
||||
}
|
||||
// 解析单列查询
|
||||
QueryType queryType = queryAnnotation.type();
|
||||
@@ -125,7 +125,7 @@ public class QueryWrapperHelper {
|
||||
final int columnLength = ArrayUtil.length(columns);
|
||||
List<Consumer<QueryWrapper<R>>> consumers = new ArrayList<>(columnLength);
|
||||
if (columnLength <= 1) {
|
||||
String columnName = columnLength == 1 ? columns[0] : StrUtil.toUnderlineCase(fieldName);
|
||||
String columnName = columnLength == 1 ? columns[0] : CharSequenceUtil.toUnderlineCase(fieldName);
|
||||
parse(queryType, columnName, fieldValue, consumers);
|
||||
return consumers;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>3.1.7</version>
|
||||
<version>3.1.8</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
@@ -53,30 +53,31 @@
|
||||
|
||||
<properties>
|
||||
<!-- 项目版本号 -->
|
||||
<revision>1.3.0</revision>
|
||||
<revision>1.4.0</revision>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
<just-auth.version>1.16.6</just-auth.version>
|
||||
<mybatis-plus.version>3.5.5</mybatis-plus.version>
|
||||
<dynamic-datasource.version>4.2.0</dynamic-datasource.version>
|
||||
<dynamic-datasource.version>4.3.0</dynamic-datasource.version>
|
||||
<p6spy.version>3.9.1</p6spy.version>
|
||||
<jetcache.version>2.7.4</jetcache.version>
|
||||
<redisson.version>3.25.2</redisson.version>
|
||||
<sms4j.version>3.0.4</sms4j.version>
|
||||
<jetcache.version>2.7.5</jetcache.version>
|
||||
<redisson.version>3.26.0</redisson.version>
|
||||
<cosid.version>2.6.5</cosid.version>
|
||||
<sms4j.version>3.1.1</sms4j.version>
|
||||
<aj-captcha.version>1.3.0</aj-captcha.version>
|
||||
<easy-captcha.version>1.6.2</easy-captcha.version>
|
||||
<easy-excel.version>3.3.3</easy-excel.version>
|
||||
<nashorn.version>15.4</nashorn.version>
|
||||
<x-file-storage.version>2.0.0</x-file-storage.version>
|
||||
<aws-s3.version>1.12.626</aws-s3.version>
|
||||
<crane4j.version>2.4.0</crane4j.version>
|
||||
<knife4j.version>4.4.0</knife4j.version>
|
||||
<x-file-storage.version>2.1.0</x-file-storage.version>
|
||||
<aws-s3.version>1.12.651</aws-s3.version>
|
||||
<crane4j.version>2.5.0</crane4j.version>
|
||||
<knife4j.version>4.5.0</knife4j.version>
|
||||
<tlog.version>1.5.1</tlog.version>
|
||||
<ttl.version>2.14.4</ttl.version>
|
||||
<ip2region.version>3.1.6</ip2region.version>
|
||||
<hutool.version>5.8.24</hutool.version>
|
||||
<ip2region.version>3.1.7</ip2region.version>
|
||||
<hutool.version>5.8.25</hutool.version>
|
||||
<!-- Maven Plugin Versions -->
|
||||
<flatten.version>1.5.0</flatten.version>
|
||||
<spotless.version>2.40.0</spotless.version>
|
||||
<flatten.version>1.6.0</flatten.version>
|
||||
<spotless.version>2.43.0</spotless.version>
|
||||
<sonar.version>3.9.1.2184</sonar.version>
|
||||
</properties>
|
||||
|
||||
@@ -120,6 +121,11 @@
|
||||
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-core</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Dynamic Datasource(基于 Spring Boot 的快速集成多数据源的启动器) -->
|
||||
<dependency>
|
||||
@@ -169,6 +175,23 @@
|
||||
<version>${redisson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- CosId(通用、灵活、高性能的分布式 ID 生成器) -->
|
||||
<dependency>
|
||||
<groupId>me.ahoo.cosid</groupId>
|
||||
<artifactId>cosid-spring-boot-starter</artifactId>
|
||||
<version>${cosid.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>me.ahoo.cosid</groupId>
|
||||
<artifactId>cosid-spring-redis</artifactId>
|
||||
<version>${cosid.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>me.ahoo.cosid</groupId>
|
||||
<artifactId>cosid-jdbc</artifactId>
|
||||
<version>${cosid.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- SMS4J(短信聚合框架,轻松集成多家短信服务,解决接入多个短信 SDK 的繁琐流程) -->
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
@@ -382,6 +405,27 @@
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 安全模块 - 统一模块 -->
|
||||
<dependency>
|
||||
<groupId>top.charles7c.continew</groupId>
|
||||
<artifactId>continew-starter-security-all</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 安全模块 - 加密 -->
|
||||
<dependency>
|
||||
<groupId>top.charles7c.continew</groupId>
|
||||
<artifactId>continew-starter-security-crypto</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 安全模块 - 脱敏 -->
|
||||
<dependency>
|
||||
<groupId>top.charles7c.continew</groupId>
|
||||
<artifactId>continew-starter-security-mask</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 安全模块 - 密码编码器 -->
|
||||
<dependency>
|
||||
<groupId>top.charles7c.continew</groupId>
|
||||
|
||||
@@ -18,7 +18,7 @@ package top.charles7c.continew.starter.extension.crud.controller;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.lang.tree.Tree;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||
@@ -184,7 +184,8 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q,
|
||||
private void checkPermission(Api api) {
|
||||
CrudRequestMapping crudRequestMapping = this.getClass().getDeclaredAnnotation(CrudRequestMapping.class);
|
||||
String path = crudRequestMapping.value();
|
||||
String permissionPrefix = String.join(StringConstants.COLON, StrUtil.splitTrim(path, StringConstants.SLASH));
|
||||
String permissionPrefix = String.join(StringConstants.COLON, CharSequenceUtil
|
||||
.splitTrim(path, StringConstants.SLASH));
|
||||
StpUtil.checkPermission(String.format("%s:%s", permissionPrefix, api.name().toLowerCase()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package top.charles7c.continew.starter.extension.crud.model.query;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.metadata.OrderItem;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
@@ -77,7 +77,7 @@ public class PageQuery extends SortQuery {
|
||||
for (Sort.Order order : pageSort) {
|
||||
OrderItem orderItem = new OrderItem();
|
||||
orderItem.setAsc(order.isAscending());
|
||||
orderItem.setColumn(StrUtil.toUnderlineCase(order.getProperty()));
|
||||
orderItem.setColumn(CharSequenceUtil.toUnderlineCase(order.getProperty()));
|
||||
mybatisPage.addOrder(orderItem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package top.charles7c.continew.starter.extension.crud.model.query;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import top.charles7c.continew.starter.core.constant.StringConstants;
|
||||
@@ -56,10 +56,10 @@ public class SortQuery implements Serializable {
|
||||
}
|
||||
|
||||
List<Sort.Order> orders = new ArrayList<>(sort.length);
|
||||
if (StrUtil.contains(sort[0], StringConstants.COMMA)) {
|
||||
if (CharSequenceUtil.contains(sort[0], StringConstants.COMMA)) {
|
||||
// e.g "sort=createTime,desc&sort=name,asc"
|
||||
for (String s : sort) {
|
||||
List<String> sortList = StrUtil.splitTrim(s, StringConstants.COMMA);
|
||||
List<String> sortList = CharSequenceUtil.splitTrim(s, StringConstants.COMMA);
|
||||
Sort.Order order = new Sort.Order(Sort.Direction.valueOf(sortList.get(1).toUpperCase()), sortList
|
||||
.get(0));
|
||||
orders.add(order);
|
||||
|
||||
@@ -34,6 +34,6 @@ public interface CommonUserService {
|
||||
* @param id ID
|
||||
* @return 昵称
|
||||
*/
|
||||
@ContainerMethod(namespace = ContainerPool.USER_NICKNAME, type = MappingType.NONE)
|
||||
@ContainerMethod(namespace = ContainerPool.USER_NICKNAME, type = MappingType.ORDER_OF_KEYS)
|
||||
String getNicknameById(Long id);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import cn.hutool.core.lang.tree.Tree;
|
||||
import cn.hutool.core.lang.tree.TreeNodeConfig;
|
||||
import cn.hutool.core.util.ClassUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
@@ -107,15 +107,15 @@ public abstract class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseDO,
|
||||
// 构建树
|
||||
return TreeUtils.build(list, treeNodeConfig, (node, tree) -> {
|
||||
// 转换器
|
||||
tree.setId(ReflectUtil.invoke(node, StrUtil.genGetter(treeField.value())));
|
||||
tree.setParentId(ReflectUtil.invoke(node, StrUtil.genGetter(treeField.parentIdKey())));
|
||||
tree.setName(ReflectUtil.invoke(node, StrUtil.genGetter(treeField.nameKey())));
|
||||
tree.setWeight(ReflectUtil.invoke(node, StrUtil.genGetter(treeField.weightKey())));
|
||||
tree.setId(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.value())));
|
||||
tree.setParentId(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.parentIdKey())));
|
||||
tree.setName(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.nameKey())));
|
||||
tree.setWeight(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.weightKey())));
|
||||
if (!isSimple) {
|
||||
List<Field> fieldList = ReflectUtils.getNonStaticFields(listClass);
|
||||
fieldList.removeIf(f -> StrUtil.containsAnyIgnoreCase(f.getName(), treeField.value(), treeField
|
||||
fieldList.removeIf(f -> CharSequenceUtil.containsAnyIgnoreCase(f.getName(), treeField.value(), treeField
|
||||
.parentIdKey(), treeField.nameKey(), treeField.weightKey(), treeField.childrenKey()));
|
||||
fieldList.forEach(f -> tree.putExtra(f.getName(), ReflectUtil.invoke(node, StrUtil.genGetter(f
|
||||
fieldList.forEach(f -> tree.putExtra(f.getName(), ReflectUtil.invoke(node, CharSequenceUtil.genGetter(f
|
||||
.getName()))));
|
||||
}
|
||||
});
|
||||
@@ -224,7 +224,7 @@ public abstract class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseDO,
|
||||
String checkProperty;
|
||||
// 携带表别名则获取 . 后面的字段名
|
||||
if (property.contains(StringConstants.DOT)) {
|
||||
checkProperty = CollUtil.getLast(StrUtil.split(property, StringConstants.DOT));
|
||||
checkProperty = CollUtil.getLast(CharSequenceUtil.split(property, StringConstants.DOT));
|
||||
} else {
|
||||
checkProperty = property;
|
||||
}
|
||||
@@ -232,7 +232,7 @@ public abstract class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseDO,
|
||||
.filter(field -> checkProperty.equals(field.getName()))
|
||||
.findFirst();
|
||||
ValidationUtils.throwIf(optional.isEmpty(), "无效的排序字段 [{}]", property);
|
||||
queryWrapper.orderBy(true, order.isAscending(), StrUtil.toUnderlineCase(property));
|
||||
queryWrapper.orderBy(true, order.isAscending(), CharSequenceUtil.toUnderlineCase(property));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,6 +267,7 @@ public abstract class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseDO,
|
||||
* @param req 创建信息
|
||||
*/
|
||||
protected void beforeAdd(C req) {
|
||||
/* 新增前置处理 */
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,6 +277,7 @@ public abstract class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseDO,
|
||||
* @param id ID
|
||||
*/
|
||||
protected void beforeUpdate(C req, Long id) {
|
||||
/* 修改前置处理 */
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -284,6 +286,7 @@ public abstract class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseDO,
|
||||
* @param ids ID 列表
|
||||
*/
|
||||
protected void beforeDelete(List<Long> ids) {
|
||||
/* 删除前置处理 */
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -293,6 +296,7 @@ public abstract class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseDO,
|
||||
* @param entity 实体信息
|
||||
*/
|
||||
protected void afterAdd(C req, T entity) {
|
||||
/* 新增后置处理 */
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -302,6 +306,7 @@ public abstract class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseDO,
|
||||
* @param entity 实体信息
|
||||
*/
|
||||
protected void afterUpdate(C req, T entity) {
|
||||
/* 修改后置处理 */
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -309,7 +314,8 @@ public abstract class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseDO,
|
||||
*
|
||||
* @param ids ID 列表
|
||||
*/
|
||||
private void afterDelete(List<Long> ids) {
|
||||
protected void afterDelete(List<Long> ids) {
|
||||
/* 删除后置处理 */
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package top.charles7c.continew.starter.log.common.annotation;
|
||||
|
||||
import top.charles7c.continew.starter.log.common.enums.Include;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
@@ -46,6 +48,16 @@ public @interface Log {
|
||||
*/
|
||||
String module() default "";
|
||||
|
||||
/**
|
||||
* 包含信息(在全局配置基础上扩展包含信息)
|
||||
*/
|
||||
Include[] includes() default {};
|
||||
|
||||
/**
|
||||
* 排除信息(在全局配置基础上减少包含信息)
|
||||
*/
|
||||
Include[] excludes() default {};
|
||||
|
||||
/**
|
||||
* 是否忽略日志记录(用于接口方法或类上)
|
||||
*/
|
||||
|
||||
@@ -16,9 +16,8 @@
|
||||
|
||||
package top.charles7c.continew.starter.log.common.model;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import top.charles7c.continew.starter.core.util.ExceptionUtils;
|
||||
import top.charles7c.continew.starter.core.util.IpUtils;
|
||||
import top.charles7c.continew.starter.log.common.enums.Include;
|
||||
import top.charles7c.continew.starter.web.util.ServletUtils;
|
||||
@@ -91,8 +90,16 @@ public class LogRequest {
|
||||
this.param = request.getParam();
|
||||
}
|
||||
this.address = (includes.contains(Include.IP_ADDRESS)) ? IpUtils.getAddress(this.ip) : null;
|
||||
String userAgentString = ExceptionUtils.exToNull(() -> this.headers.get(HttpHeaders.USER_AGENT));
|
||||
if (StrUtil.isNotBlank(userAgentString)) {
|
||||
if (null == this.headers) {
|
||||
return;
|
||||
}
|
||||
String userAgentString = this.headers.entrySet()
|
||||
.stream()
|
||||
.filter(h -> HttpHeaders.USER_AGENT.equalsIgnoreCase(h.getKey()))
|
||||
.map(Map.Entry::getValue)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (CharSequenceUtil.isNotBlank(userAgentString)) {
|
||||
this.browser = (includes.contains(Include.BROWSER)) ? ServletUtils.getBrowser(userAgentString) : null;
|
||||
this.os = (includes.contains(Include.OS)) ? ServletUtils.getOs(userAgentString) : null;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,6 @@ import java.lang.annotation.*;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Documented
|
||||
@ConditionalOnProperty(prefix = PropertiesConstants.LOG, name = PropertiesConstants.ENABLED, havingValue = "true")
|
||||
@ConditionalOnProperty(prefix = PropertiesConstants.LOG, name = PropertiesConstants.ENABLED, havingValue = "true", matchIfMissing = true)
|
||||
public @interface ConditionalOnEnabledLog {
|
||||
}
|
||||
@@ -35,7 +35,7 @@ public class LogProperties {
|
||||
/**
|
||||
* 是否启用日志
|
||||
*/
|
||||
private boolean enabled = false;
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* 是否打印日志,开启后可打印访问日志(类似于 Nginx access log)
|
||||
@@ -45,7 +45,7 @@ public class LogProperties {
|
||||
/**
|
||||
* 包含信息
|
||||
*/
|
||||
private Set<Include> include = new HashSet<>(Include.defaultIncludes());
|
||||
private Set<Include> includes = new HashSet<>(Include.defaultIncludes());
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
@@ -63,11 +63,11 @@ public class LogProperties {
|
||||
isPrint = print;
|
||||
}
|
||||
|
||||
public Set<Include> getInclude() {
|
||||
return include;
|
||||
public Set<Include> getIncludes() {
|
||||
return includes;
|
||||
}
|
||||
|
||||
public void setInclude(Set<Include> include) {
|
||||
this.include = include;
|
||||
public void setIncludes(Set<Include> includes) {
|
||||
this.includes = includes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ public class LogFilter extends OncePerRequestFilter implements Ordered {
|
||||
* @return true:是;false:否
|
||||
*/
|
||||
private boolean isRequestWrapper(HttpServletRequest request) {
|
||||
Set<Include> includeSet = logProperties.getInclude();
|
||||
Set<Include> includeSet = logProperties.getIncludes();
|
||||
return !(request instanceof ContentCachingRequestWrapper) && (includeSet
|
||||
.contains(Include.REQUEST_BODY) || includeSet.contains(Include.REQUEST_PARAM));
|
||||
}
|
||||
@@ -117,7 +117,7 @@ public class LogFilter extends OncePerRequestFilter implements Ordered {
|
||||
* @return true:是;false:否
|
||||
*/
|
||||
private boolean isResponseWrapper(HttpServletResponse response) {
|
||||
Set<Include> includeSet = logProperties.getInclude();
|
||||
Set<Include> includeSet = logProperties.getIncludes();
|
||||
return !(response instanceof ContentCachingResponseWrapper) && (includeSet
|
||||
.contains(Include.RESPONSE_BODY) || includeSet.contains(Include.RESPONSE_PARAM));
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package top.charles7c.continew.starter.log.httptracepro.handler;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
@@ -83,18 +83,20 @@ public class LogInterceptor implements HandlerInterceptor {
|
||||
return;
|
||||
}
|
||||
timestampTtl.remove();
|
||||
Set<Include> includeSet = logProperties.getInclude();
|
||||
try {
|
||||
HandlerMethod handlerMethod = (HandlerMethod)handler;
|
||||
Log methodLog = handlerMethod.getMethodAnnotation(Log.class);
|
||||
Log classLog = handlerMethod.getBeanType().getDeclaredAnnotation(Log.class);
|
||||
Set<Include> includeSet = this.getIncludes(methodLog, classLog);
|
||||
LogRecord finishedLogRecord = startedLogRecord.finish(new RecordableServletHttpResponse(response, response
|
||||
.getStatus()), includeSet);
|
||||
HandlerMethod handlerMethod = (HandlerMethod)handler;
|
||||
// 记录日志描述
|
||||
if (includeSet.contains(Include.DESCRIPTION)) {
|
||||
this.logDescription(finishedLogRecord, handlerMethod);
|
||||
this.logDescription(finishedLogRecord, methodLog, handlerMethod);
|
||||
}
|
||||
// 记录所属模块
|
||||
if (includeSet.contains(Include.MODULE)) {
|
||||
this.logModule(finishedLogRecord, handlerMethod);
|
||||
this.logModule(finishedLogRecord, methodLog, classLog, handlerMethod);
|
||||
}
|
||||
if (Boolean.TRUE.equals(logProperties.getIsPrint())) {
|
||||
LogResponse logResponse = finishedLogRecord.getResponse();
|
||||
@@ -107,23 +109,58 @@ public class LogInterceptor implements HandlerInterceptor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取日志包含信息
|
||||
*
|
||||
* @param methodLog 方法级 Log 注解
|
||||
* @param classLog 类级 Log 注解
|
||||
* @return 日志包含信息
|
||||
*/
|
||||
private Set<Include> getIncludes(Log methodLog, Log classLog) {
|
||||
Set<Include> includeSet = logProperties.getIncludes();
|
||||
if (null != classLog) {
|
||||
this.processInclude(includeSet, classLog);
|
||||
}
|
||||
if (null != methodLog) {
|
||||
this.processInclude(includeSet, methodLog);
|
||||
}
|
||||
return includeSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理日志包含信息
|
||||
*
|
||||
* @param includes 日志包含信息
|
||||
* @param logAnnotation Log 注解
|
||||
*/
|
||||
private void processInclude(Set<Include> includes, Log logAnnotation) {
|
||||
Include[] includeArr = logAnnotation.includes();
|
||||
if (includeArr.length > 0) {
|
||||
includes.addAll(Set.of(includeArr));
|
||||
}
|
||||
Include[] excludeArr = logAnnotation.excludes();
|
||||
if (excludeArr.length > 0) {
|
||||
includes.removeAll(Set.of(excludeArr));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录描述
|
||||
*
|
||||
* @param logRecord 日志信息
|
||||
* @param methodLog 方法级 Log 注解
|
||||
* @param handlerMethod 处理器方法
|
||||
*/
|
||||
private void logDescription(LogRecord logRecord, HandlerMethod handlerMethod) {
|
||||
private void logDescription(LogRecord logRecord, Log methodLog, HandlerMethod handlerMethod) {
|
||||
// 例如:@Log("新增部门") -> 新增部门
|
||||
Log methodLog = handlerMethod.getMethodAnnotation(Log.class);
|
||||
if (null != methodLog && StrUtil.isNotBlank(methodLog.value())) {
|
||||
if (null != methodLog && CharSequenceUtil.isNotBlank(methodLog.value())) {
|
||||
logRecord.setDescription(methodLog.value());
|
||||
return;
|
||||
}
|
||||
// 例如:@Operation(summary="新增部门") -> 新增部门
|
||||
Operation methodOperation = handlerMethod.getMethodAnnotation(Operation.class);
|
||||
if (null != methodOperation) {
|
||||
logRecord.setDescription(StrUtil.blankToDefault(methodOperation.summary(), "请在该接口方法上指定日志描述"));
|
||||
logRecord.setDescription(CharSequenceUtil.blankToDefault(methodOperation.summary(), "请在该接口方法上指定日志描述"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,17 +168,17 @@ public class LogInterceptor implements HandlerInterceptor {
|
||||
* 记录模块
|
||||
*
|
||||
* @param logRecord 日志信息
|
||||
* @param methodLog 方法级 Log 注解
|
||||
* @param classLog 类级 Log 注解
|
||||
* @param handlerMethod 处理器方法
|
||||
*/
|
||||
private void logModule(LogRecord logRecord, HandlerMethod handlerMethod) {
|
||||
private void logModule(LogRecord logRecord, Log methodLog, Log classLog, HandlerMethod handlerMethod) {
|
||||
// 例如:@Log(module = "部门管理") -> 部门管理
|
||||
Log methodLog = handlerMethod.getMethodAnnotation(Log.class);
|
||||
if (null != methodLog && StrUtil.isNotBlank(methodLog.module())) {
|
||||
if (null != methodLog && CharSequenceUtil.isNotBlank(methodLog.module())) {
|
||||
logRecord.setModule(methodLog.module());
|
||||
return;
|
||||
}
|
||||
Log classLog = handlerMethod.getBeanType().getDeclaredAnnotation(Log.class);
|
||||
if (null != classLog && StrUtil.isNotBlank(classLog.module())) {
|
||||
if (null != classLog && CharSequenceUtil.isNotBlank(classLog.module())) {
|
||||
logRecord.setModule(classLog.module());
|
||||
return;
|
||||
}
|
||||
@@ -149,7 +186,7 @@ public class LogInterceptor implements HandlerInterceptor {
|
||||
Tag classTag = handlerMethod.getBeanType().getDeclaredAnnotation(Tag.class);
|
||||
if (null != classTag) {
|
||||
String name = classTag.name();
|
||||
logRecord.setModule(StrUtil.blankToDefault(name, "请在该接口类上指定所属模块"));
|
||||
logRecord.setModule(CharSequenceUtil.blankToDefault(name, "请在该接口类上指定所属模块"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package top.charles7c.continew.starter.log.httptracepro.handler;
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.servlet.JakartaServletUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
@@ -53,7 +54,7 @@ public final class RecordableServletHttpRequest implements RecordableHttpRequest
|
||||
@Override
|
||||
public URI getUrl() {
|
||||
String queryString = request.getQueryString();
|
||||
if (StrUtil.isBlank(queryString)) {
|
||||
if (CharSequenceUtil.isBlank(queryString)) {
|
||||
return URI.create(request.getRequestURL().toString());
|
||||
}
|
||||
try {
|
||||
@@ -89,7 +90,7 @@ public final class RecordableServletHttpRequest implements RecordableHttpRequest
|
||||
@Override
|
||||
public Map<String, Object> getParam() {
|
||||
String body = this.getBody();
|
||||
return StrUtil.isNotBlank(body) && JSONUtil.isTypeJSON(body)
|
||||
return CharSequenceUtil.isNotBlank(body) && JSONUtil.isTypeJSON(body)
|
||||
? JSONUtil.toBean(body, Map.class)
|
||||
: Collections.unmodifiableMap(request.getParameterMap());
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package top.charles7c.continew.starter.log.httptracepro.handler;
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
@@ -67,6 +68,6 @@ public final class RecordableServletHttpResponse implements RecordableHttpRespon
|
||||
@Override
|
||||
public Map<String, Object> getParam() {
|
||||
String body = this.getBody();
|
||||
return StrUtil.isNotBlank(body) && JSONUtil.isTypeJSON(body) ? JSONUtil.toBean(body, Map.class) : null;
|
||||
return CharSequenceUtil.isNotBlank(body) && JSONUtil.isTypeJSON(body) ? JSONUtil.toBean(body, Map.class) : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ package top.charles7c.continew.starter.messaging.mail.util;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import jakarta.mail.MessagingException;
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
@@ -156,7 +156,7 @@ public class MailUtils {
|
||||
String content,
|
||||
boolean isHtml,
|
||||
File... files) throws MessagingException {
|
||||
Assert.isTrue(CollUtil.isEmpty(tos), "请至少指定一名收件人");
|
||||
Assert.isFalse(CollUtil.isEmpty(tos), "请至少指定一名收件人");
|
||||
MimeMessage mimeMessage = MAIL_SENDER.createMimeMessage();
|
||||
MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true, StandardCharsets.UTF_8
|
||||
.displayName());
|
||||
@@ -192,14 +192,14 @@ public class MailUtils {
|
||||
* @return 联系人列表
|
||||
*/
|
||||
private static List<String> splitAddress(String addresses) {
|
||||
if (StrUtil.isBlank(addresses)) {
|
||||
if (CharSequenceUtil.isBlank(addresses)) {
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
List<String> result;
|
||||
if (StrUtil.contains(addresses, StringConstants.COMMA)) {
|
||||
result = StrUtil.splitTrim(addresses, StringConstants.COMMA);
|
||||
} else if (StrUtil.contains(addresses, StringConstants.SEMICOLON)) {
|
||||
result = StrUtil.splitTrim(addresses, StringConstants.SEMICOLON);
|
||||
if (CharSequenceUtil.contains(addresses, StringConstants.COMMA)) {
|
||||
result = CharSequenceUtil.splitTrim(addresses, StringConstants.COMMA);
|
||||
} else if (CharSequenceUtil.contains(addresses, StringConstants.SEMICOLON)) {
|
||||
result = CharSequenceUtil.splitTrim(addresses, StringConstants.SEMICOLON);
|
||||
} else {
|
||||
result = CollUtil.newArrayList(addresses);
|
||||
}
|
||||
|
||||
@@ -18,10 +18,5 @@
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-crypto</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>top.charles7c.continew</groupId>
|
||||
<artifactId>continew-starter-security</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>continew-starter-security-all</artifactId>
|
||||
<description>ContiNew Starter 安全模块 - 统一模块</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- 安全模块 - 加密 -->
|
||||
<dependency>
|
||||
<groupId>top.charles7c.continew</groupId>
|
||||
<artifactId>continew-starter-security-crypto</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 安全模块 - 脱敏 -->
|
||||
<dependency>
|
||||
<groupId>top.charles7c.continew</groupId>
|
||||
<artifactId>continew-starter-security-mask</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 安全模块 - 密码编码器 -->
|
||||
<dependency>
|
||||
<groupId>top.charles7c.continew</groupId>
|
||||
<artifactId>continew-starter-security-password</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>top.charles7c.continew</groupId>
|
||||
<artifactId>continew-starter-security</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>continew-starter-security-crypto</artifactId>
|
||||
<description>ContiNew Starter 安全模块 - 加密</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- Hutool 加密解密模块(封装 JDK 中加密解密算法) -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-crypto</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- MyBatis Plus(MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,简化开发、提高效率) -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-core</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.security.crypto.annotation;
|
||||
|
||||
import top.charles7c.continew.starter.security.crypto.encryptor.IEncryptor;
|
||||
import top.charles7c.continew.starter.security.crypto.enums.Algorithm;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 字段加/解密注解
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Target({ElementType.FIELD, ElementType.PARAMETER})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface FieldEncrypt {
|
||||
|
||||
/**
|
||||
* 加密/解密算法
|
||||
*/
|
||||
Algorithm value() default Algorithm.AES;
|
||||
|
||||
/**
|
||||
* 加密/解密处理器
|
||||
* <p>
|
||||
* 优先级高于加密/解密算法
|
||||
* </p>
|
||||
*/
|
||||
Class<? extends IEncryptor> encryptor() default IEncryptor.class;
|
||||
|
||||
/**
|
||||
* 对称加密算法密钥
|
||||
*/
|
||||
String password() default "";
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.security.crypto.autoconfigure;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import top.charles7c.continew.starter.core.constant.PropertiesConstants;
|
||||
import top.charles7c.continew.starter.security.crypto.core.MyBatisDecryptInterceptor;
|
||||
import top.charles7c.continew.starter.security.crypto.core.MyBatisEncryptInterceptor;
|
||||
|
||||
/**
|
||||
* 加/解密自动配置
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@EnableConfigurationProperties(CryptoProperties.class)
|
||||
@ConditionalOnProperty(prefix = PropertiesConstants.CRYPTO, name = PropertiesConstants.ENABLED, havingValue = "true", matchIfMissing = true)
|
||||
public class CryptoAutoConfiguration {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(CryptoAutoConfiguration.class);
|
||||
private final CryptoProperties properties;
|
||||
|
||||
public CryptoAutoConfiguration(CryptoProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* MyBatis 加密拦截器配置
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(MyBatisEncryptInterceptor.class)
|
||||
public MyBatisEncryptInterceptor myBatisEncryptInterceptor() {
|
||||
return new MyBatisEncryptInterceptor(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* MyBatis 解密拦截器配置
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(MyBatisDecryptInterceptor.class)
|
||||
public MyBatisDecryptInterceptor myBatisDecryptInterceptor() {
|
||||
return new MyBatisDecryptInterceptor(properties);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void postConstruct() {
|
||||
log.debug("[ContiNew Starter] - Auto Configuration 'Security-Crypto' completed initialization.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.security.crypto.autoconfigure;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import top.charles7c.continew.starter.core.constant.PropertiesConstants;
|
||||
|
||||
/**
|
||||
* 加/解密配置属性
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@ConfigurationProperties(PropertiesConstants.CRYPTO)
|
||||
public class CryptoProperties {
|
||||
|
||||
/**
|
||||
* 是否启用加/解密配置
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* 对称加密算法密钥
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 非对称加密算法公钥
|
||||
*/
|
||||
private String publicKey;
|
||||
|
||||
/**
|
||||
* 非对称加密算法私钥
|
||||
*/
|
||||
private String privateKey;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getPublicKey() {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
public void setPublicKey(String publicKey) {
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
public String getPrivateKey() {
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
public void setPrivateKey(String privateKey) {
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.security.crypto.core;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.plugin.*;
|
||||
import top.charles7c.continew.starter.core.constant.StringConstants;
|
||||
import top.charles7c.continew.starter.core.exception.BusinessException;
|
||||
import top.charles7c.continew.starter.security.crypto.annotation.FieldEncrypt;
|
||||
import top.charles7c.continew.starter.security.crypto.encryptor.IEncryptor;
|
||||
import top.charles7c.continew.starter.security.crypto.enums.Algorithm;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 字段解密拦截器
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public abstract class AbstractMyBatisInterceptor implements Interceptor {
|
||||
|
||||
private static final Map<String, Map<String, FieldEncrypt>> ENCRYPT_PARAM_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 获取加密参数
|
||||
*
|
||||
* @param mappedStatementId 映射语句 ID
|
||||
* @return 加密参数
|
||||
*/
|
||||
public Map<String, FieldEncrypt> getEncryptParams(String mappedStatementId) {
|
||||
return ENCRYPT_PARAM_CACHE.computeIfAbsent(mappedStatementId, this::getEncryptParamsNoCached);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取参数名称
|
||||
*
|
||||
* @param parameter 参数
|
||||
* @return 参数名称
|
||||
*/
|
||||
public String getParameterName(Parameter parameter) {
|
||||
Param param = parameter.getAnnotation(Param.class);
|
||||
return null != param ? param.value() : parameter.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有字符串类型、需要加/解密的、有值字段
|
||||
*
|
||||
* @param obj 对象
|
||||
* @return 字段列表
|
||||
*/
|
||||
public List<Field> getEncryptFields(Object obj) {
|
||||
if (null == obj) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Arrays.stream(ReflectUtil.getFields(obj.getClass()))
|
||||
.filter(field -> String.class.equals(field.getType()))
|
||||
.filter(field -> null != field.getAnnotation(FieldEncrypt.class))
|
||||
.filter(field -> null != ReflectUtil.getFieldValue(obj, field))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段加/解密处理器
|
||||
*
|
||||
* @param fieldEncrypt 字段加密注解
|
||||
* @return 加/解密处理器
|
||||
*/
|
||||
public IEncryptor getEncryptor(FieldEncrypt fieldEncrypt) {
|
||||
Class<? extends IEncryptor> encryptorClass = fieldEncrypt.encryptor();
|
||||
// 使用预定义加/解密处理器
|
||||
if (encryptorClass == IEncryptor.class) {
|
||||
Algorithm algorithm = fieldEncrypt.value();
|
||||
return ReflectUtil.newInstance(algorithm.getEncryptor());
|
||||
}
|
||||
// 使用自定义加/解密处理器
|
||||
return SpringUtil.getBean(encryptorClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取参数列表(无缓存)
|
||||
*
|
||||
* @param mappedStatementId 映射语句 ID
|
||||
* @return 参数列表
|
||||
*/
|
||||
private Map<String, FieldEncrypt> getEncryptParamsNoCached(String mappedStatementId) {
|
||||
try {
|
||||
String className = CharSequenceUtil.subBefore(mappedStatementId, StringConstants.DOT, true);
|
||||
String wrapperMethodName = CharSequenceUtil.subAfter(mappedStatementId, StringConstants.DOT, true);
|
||||
String methodName = Stream.of("_mpCount", "_COUNT")
|
||||
.filter(wrapperMethodName::endsWith)
|
||||
.findFirst()
|
||||
.map(suffix -> wrapperMethodName.substring(0, wrapperMethodName.length() - suffix.length()))
|
||||
.orElse(wrapperMethodName);
|
||||
// 获取真实方法
|
||||
Optional<Method> methodOptional = Arrays.stream(ReflectUtil.getMethods(Class
|
||||
.forName(className), m -> Objects.equals(m.getName(), methodName))).findFirst();
|
||||
if (methodOptional.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
// 获取方法中的加密参数
|
||||
Map<String, FieldEncrypt> map = MapUtil.newHashMap();
|
||||
Parameter[] parameterArr = methodOptional.get().getParameters();
|
||||
for (int i = 0; i < parameterArr.length; i++) {
|
||||
Parameter parameter = parameterArr[i];
|
||||
String parameterName = this.getParameterName(parameter);
|
||||
FieldEncrypt fieldEncrypt = parameter.getAnnotation(FieldEncrypt.class);
|
||||
if (null != fieldEncrypt) {
|
||||
map.put(parameterName, fieldEncrypt);
|
||||
if (String.class.equals(parameter.getType())) {
|
||||
map.put("param" + (i + 1), fieldEncrypt);
|
||||
}
|
||||
} else if (parameterName.startsWith(Constants.ENTITY)) {
|
||||
map.put(parameterName, null);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new BusinessException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.security.crypto.core;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import org.apache.ibatis.executor.resultset.ResultSetHandler;
|
||||
import org.apache.ibatis.plugin.*;
|
||||
import org.apache.ibatis.type.SimpleTypeRegistry;
|
||||
import top.charles7c.continew.starter.security.crypto.annotation.FieldEncrypt;
|
||||
import top.charles7c.continew.starter.security.crypto.autoconfigure.CryptoProperties;
|
||||
import top.charles7c.continew.starter.security.crypto.encryptor.IEncryptor;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 字段解密拦截器
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Intercepts({@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})
|
||||
public class MyBatisDecryptInterceptor extends AbstractMyBatisInterceptor {
|
||||
|
||||
private CryptoProperties properties;
|
||||
|
||||
public MyBatisDecryptInterceptor(CryptoProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public MyBatisDecryptInterceptor() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object intercept(Invocation invocation) throws Throwable {
|
||||
Object obj = invocation.proceed();
|
||||
if (null == obj || !(invocation.getTarget() instanceof ResultSetHandler)) {
|
||||
return obj;
|
||||
}
|
||||
List<?> resultList = (List<?>)obj;
|
||||
for (Object result : resultList) {
|
||||
// String、Integer、Long 等简单类型对象无需处理
|
||||
if (SimpleTypeRegistry.isSimpleType(result.getClass())) {
|
||||
continue;
|
||||
}
|
||||
// 获取所有字符串类型、需要解密的、有值字段
|
||||
List<Field> fieldList = super.getEncryptFields(result);
|
||||
// 解密处理
|
||||
for (Field field : fieldList) {
|
||||
IEncryptor encryptor = super.getEncryptor(field.getAnnotation(FieldEncrypt.class));
|
||||
Object fieldValue = ReflectUtil.getFieldValue(result, field);
|
||||
// 优先获取自定义对称加密算法密钥,获取不到时再获取全局配置
|
||||
String password = ObjectUtil.defaultIfBlank(field.getAnnotation(FieldEncrypt.class)
|
||||
.password(), properties.getPassword());
|
||||
String ciphertext = encryptor.decrypt(fieldValue.toString(), password, properties.getPrivateKey());
|
||||
ReflectUtil.setFieldValue(result, field, ciphertext);
|
||||
}
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.security.crypto.core;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||
import org.apache.ibatis.cache.CacheKey;
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.mapping.BoundSql;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.mapping.SqlCommandType;
|
||||
import org.apache.ibatis.plugin.*;
|
||||
import org.apache.ibatis.session.ResultHandler;
|
||||
import org.apache.ibatis.session.RowBounds;
|
||||
import org.apache.ibatis.type.SimpleTypeRegistry;
|
||||
import top.charles7c.continew.starter.security.crypto.annotation.FieldEncrypt;
|
||||
import top.charles7c.continew.starter.security.crypto.autoconfigure.CryptoProperties;
|
||||
import top.charles7c.continew.starter.security.crypto.encryptor.IEncryptor;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 字段加密拦截器
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Intercepts({@Signature(method = "update", type = Executor.class, args = {MappedStatement.class, Object.class}),
|
||||
@Signature(method = "query", type = Executor.class, args = {MappedStatement.class, Object.class, RowBounds.class,
|
||||
ResultHandler.class, CacheKey.class, BoundSql.class}),
|
||||
@Signature(method = "query", type = Executor.class, args = {MappedStatement.class, Object.class, RowBounds.class,
|
||||
ResultHandler.class})})
|
||||
public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor {
|
||||
|
||||
private CryptoProperties properties;
|
||||
|
||||
public MyBatisEncryptInterceptor(CryptoProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public MyBatisEncryptInterceptor() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object intercept(Invocation invocation) throws Throwable {
|
||||
Object[] args = invocation.getArgs();
|
||||
MappedStatement mappedStatement = (MappedStatement)args[0];
|
||||
Object parameter = args[1];
|
||||
if (!this.isEncryptRequired(parameter, mappedStatement.getSqlCommandType())) {
|
||||
return invocation.proceed();
|
||||
}
|
||||
// 使用 @Param 注解的场景
|
||||
if (parameter instanceof HashMap parameterMap) {
|
||||
this.encryptMap(parameterMap, mappedStatement);
|
||||
} else {
|
||||
this.doEncrypt(this.getEncryptFields(parameter), parameter);
|
||||
}
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否需要加密处理
|
||||
*
|
||||
* @param parameter 参数
|
||||
* @param sqlCommandType SQL 类型
|
||||
* @return true:是;false:否
|
||||
*/
|
||||
private boolean isEncryptRequired(Object parameter, SqlCommandType sqlCommandType) {
|
||||
if (ObjectUtil.isEmpty(parameter)) {
|
||||
return false;
|
||||
}
|
||||
if (!(SqlCommandType.UPDATE == sqlCommandType || SqlCommandType.INSERT == sqlCommandType || SqlCommandType.SELECT == sqlCommandType)) {
|
||||
return false;
|
||||
}
|
||||
return !SimpleTypeRegistry.isSimpleType(parameter.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密 Map 类型数据(使用 @Param 注解的场景)
|
||||
*
|
||||
* @param parameterMap 参数
|
||||
* @param mappedStatement 映射语句
|
||||
* @throws Exception /
|
||||
*/
|
||||
private void encryptMap(HashMap<String, Object> parameterMap, MappedStatement mappedStatement) throws Exception {
|
||||
Map<String, FieldEncrypt> encryptParamMap = super.getEncryptParams(mappedStatement.getId());
|
||||
for (Map.Entry<String, FieldEncrypt> encryptParamEntry : encryptParamMap.entrySet()) {
|
||||
String parameterName = encryptParamEntry.getKey();
|
||||
if (parameterName.startsWith(Constants.ENTITY)) {
|
||||
// 兼容 MyBatis Plus 封装的 update 相关方法,updateById、update
|
||||
Object entity = parameterMap.getOrDefault(parameterName, null);
|
||||
this.doEncrypt(this.getEncryptFields(entity), entity);
|
||||
} else {
|
||||
FieldEncrypt fieldEncrypt = encryptParamEntry.getValue();
|
||||
parameterMap.put(parameterName, this.doEncrypt(parameterMap.get(parameterName), fieldEncrypt));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理加密
|
||||
*
|
||||
* @param parameterValue 参数值
|
||||
* @param fieldEncrypt 字段加密注解
|
||||
* @throws Exception /
|
||||
*/
|
||||
private Object doEncrypt(Object parameterValue, FieldEncrypt fieldEncrypt) throws Exception {
|
||||
if (null == parameterValue) {
|
||||
return null;
|
||||
}
|
||||
IEncryptor encryptor = super.getEncryptor(fieldEncrypt);
|
||||
// 优先获取自定义对称加密算法密钥,获取不到时再获取全局配置
|
||||
String password = ObjectUtil.defaultIfBlank(fieldEncrypt.password(), properties.getPassword());
|
||||
return encryptor.encrypt(parameterValue.toString(), password, properties.getPublicKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理加密
|
||||
*
|
||||
* @param fieldList 加密字段列表
|
||||
* @param entity 实体
|
||||
* @throws Exception /
|
||||
*/
|
||||
private void doEncrypt(List<Field> fieldList, Object entity) throws Exception {
|
||||
for (Field field : fieldList) {
|
||||
IEncryptor encryptor = super.getEncryptor(field.getAnnotation(FieldEncrypt.class));
|
||||
Object fieldValue = ReflectUtil.getFieldValue(entity, field);
|
||||
// 优先获取自定义对称加密算法密钥,获取不到时再获取全局配置
|
||||
String password = ObjectUtil.defaultIfBlank(field.getAnnotation(FieldEncrypt.class).password(), properties
|
||||
.getPassword());
|
||||
String ciphertext = encryptor.encrypt(fieldValue.toString(), password, properties.getPublicKey());
|
||||
ReflectUtil.setFieldValue(entity, field, ciphertext);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.security.crypto.encryptor;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
|
||||
import cn.hutool.crypto.symmetric.SymmetricCrypto;
|
||||
import top.charles7c.continew.starter.core.constant.StringConstants;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 对称加/解密处理器
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public abstract class AbstractSymmetricCryptoEncryptor implements IEncryptor {
|
||||
|
||||
private static final Map<String, SymmetricCrypto> CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public String encrypt(String plaintext, String password, String publicKey) throws Exception {
|
||||
if (StrUtil.isBlank(plaintext)) {
|
||||
return plaintext;
|
||||
}
|
||||
return this.getCrypto(password).encryptHex(plaintext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decrypt(String ciphertext, String password, String privateKey) throws Exception {
|
||||
if (StrUtil.isBlank(ciphertext)) {
|
||||
return ciphertext;
|
||||
}
|
||||
return this.getCrypto(password).decryptStr(ciphertext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对称加密算法
|
||||
*
|
||||
* @param password 密钥
|
||||
* @return 对称加密算法
|
||||
*/
|
||||
protected SymmetricCrypto getCrypto(String password) {
|
||||
SymmetricAlgorithm algorithm = this.getAlgorithm();
|
||||
String key = algorithm + StringConstants.UNDERLINE + password;
|
||||
if (CACHE.containsKey(key)) {
|
||||
return CACHE.get(key);
|
||||
}
|
||||
SymmetricCrypto symmetricCrypto = new SymmetricCrypto(algorithm, password.getBytes(StandardCharsets.UTF_8));
|
||||
CACHE.put(key, symmetricCrypto);
|
||||
return symmetricCrypto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对称加密算法类型
|
||||
*
|
||||
* @return 对称加密算法类型
|
||||
*/
|
||||
protected abstract SymmetricAlgorithm getAlgorithm();
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.security.crypto.encryptor;
|
||||
|
||||
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
|
||||
|
||||
/**
|
||||
* AES(Advanced Encryption Standard) 加/解密处理器
|
||||
* <p>
|
||||
* 美国国家标准与技术研究院(NIST)采纳的对称加密算法标准,提供128位、192位和256位三种密钥长度,以高效和安全性著称。
|
||||
* </p>
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class AesEncryptor extends AbstractSymmetricCryptoEncryptor {
|
||||
|
||||
@Override
|
||||
protected SymmetricAlgorithm getAlgorithm() {
|
||||
return SymmetricAlgorithm.AES;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.security.crypto.encryptor;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
|
||||
/**
|
||||
* Base64 加/解密处理器
|
||||
* <p>
|
||||
* 一种用于编码二进制数据到文本格式的算法,常用于邮件附件、网页传输等场合,但它不是一种加密算法,只提供数据的编码和解码,不保证数据的安全性。
|
||||
* </p>
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class Base64Encryptor implements IEncryptor {
|
||||
|
||||
@Override
|
||||
public String encrypt(String plaintext, String password, String publicKey) throws Exception {
|
||||
return Base64.encode(plaintext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decrypt(String ciphertext, String password, String privateKey) throws Exception {
|
||||
return Base64.decodeStr(ciphertext);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.security.crypto.encryptor;
|
||||
|
||||
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
|
||||
|
||||
/**
|
||||
* DES(Data Encryption Standard) 加/解密处理器
|
||||
* <p>
|
||||
* 一种对称加密算法,使用相同的密钥进行加密和解密。DES 使用 56 位密钥(实际上有 64 位,但有 8 位用于奇偶校验)和一系列置换和替换操作来加密数据。
|
||||
* </p>
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class DesEncryptor extends AbstractSymmetricCryptoEncryptor {
|
||||
|
||||
@Override
|
||||
protected SymmetricAlgorithm getAlgorithm() {
|
||||
return SymmetricAlgorithm.DES;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.security.crypto.encryptor;
|
||||
|
||||
/**
|
||||
* 加/解密接口
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public interface IEncryptor {
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param plaintext 明文
|
||||
* @param password 对称加密算法密钥
|
||||
* @param publicKey 非对称加密算法公钥
|
||||
* @return 加密后的文本
|
||||
* @throws Exception /
|
||||
*/
|
||||
String encrypt(String plaintext, String password, String publicKey) throws Exception;
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param ciphertext 密文
|
||||
* @param password 对称加密算法密钥
|
||||
* @param privateKey 非对称加密算法私钥
|
||||
* @return 解密后的文本
|
||||
* @throws Exception /
|
||||
*/
|
||||
String decrypt(String ciphertext, String password, String privateKey) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.security.crypto.encryptor;
|
||||
|
||||
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
|
||||
|
||||
/**
|
||||
* PBEWithMD5AndDES(Password Based Encryption With MD5 And DES) 加/解密处理器
|
||||
* <p>
|
||||
* 混合加密算法,结合了 MD5 散列算法和 DES(Data Encryption Standard)加密算法
|
||||
* </p>
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class PbeWithMd5AndDesEncryptor extends AbstractSymmetricCryptoEncryptor {
|
||||
|
||||
@Override
|
||||
protected SymmetricAlgorithm getAlgorithm() {
|
||||
return SymmetricAlgorithm.PBEWithMD5AndDES;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.security.crypto.encryptor;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.asymmetric.KeyType;
|
||||
|
||||
/**
|
||||
* RSA 加/解密处理器
|
||||
* <p>
|
||||
* 非对称加密算法,由罗纳德·李维斯特(Ron Rivest)、阿迪·沙米尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)于1977年提出,安全性基于大数因子分解问题的困难性。
|
||||
* </p>
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class RsaEncryptor implements IEncryptor {
|
||||
|
||||
@Override
|
||||
public String encrypt(String plaintext, String password, String publicKey) throws Exception {
|
||||
return Base64.encode(SecureUtil.rsa(null, publicKey).encrypt(plaintext, KeyType.PublicKey));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decrypt(String ciphertext, String password, String privateKey) throws Exception {
|
||||
return new String(SecureUtil.rsa(privateKey, null).decrypt(Base64.decode(ciphertext), KeyType.PrivateKey));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.security.crypto.enums;
|
||||
|
||||
import top.charles7c.continew.starter.security.crypto.encryptor.*;
|
||||
|
||||
/**
|
||||
* 加密/解密算法枚举
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public enum Algorithm {
|
||||
|
||||
/**
|
||||
* AES
|
||||
*/
|
||||
AES(AesEncryptor.class),
|
||||
|
||||
/**
|
||||
* DES
|
||||
*/
|
||||
DES(DesEncryptor.class),
|
||||
|
||||
/**
|
||||
* PBEWithMD5AndDES
|
||||
*/
|
||||
PBEWithMD5AndDES(PbeWithMd5AndDesEncryptor.class),
|
||||
|
||||
/**
|
||||
* RSA
|
||||
*/
|
||||
RSA(RsaEncryptor.class),
|
||||
|
||||
/**
|
||||
* Base64
|
||||
*/
|
||||
BASE64(Base64Encryptor.class),;
|
||||
|
||||
/**
|
||||
* 加密/解密处理器
|
||||
*/
|
||||
private final Class<? extends IEncryptor> encryptor;
|
||||
|
||||
Algorithm(Class<? extends IEncryptor> encryptor) {
|
||||
this.encryptor = encryptor;
|
||||
}
|
||||
|
||||
public Class<? extends IEncryptor> getEncryptor() {
|
||||
return encryptor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
top.charles7c.continew.starter.security.crypto.autoconfigure.CryptoAutoConfiguration
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>top.charles7c.continew</groupId>
|
||||
<artifactId>continew-starter-security</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>continew-starter-security-mask</artifactId>
|
||||
<description>ContiNew Starter 安全模块 - 脱敏</description>
|
||||
</project>
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.security.mask.annotation;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import top.charles7c.continew.starter.core.constant.StringConstants;
|
||||
import top.charles7c.continew.starter.security.mask.core.JsonMaskSerializer;
|
||||
import top.charles7c.continew.starter.security.mask.enums.MaskType;
|
||||
import top.charles7c.continew.starter.security.mask.strategy.IMaskStrategy;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* JSON 脱敏注解
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@JacksonAnnotationsInside
|
||||
@JsonSerialize(using = JsonMaskSerializer.class)
|
||||
public @interface JsonMask {
|
||||
|
||||
/**
|
||||
* 脱敏类型
|
||||
*/
|
||||
MaskType value() default MaskType.CUSTOM;
|
||||
|
||||
/**
|
||||
* 脱敏策略
|
||||
* <p>
|
||||
* 优先级高于脱敏类型
|
||||
* </p>
|
||||
*/
|
||||
Class<? extends IMaskStrategy> strategy() default IMaskStrategy.class;
|
||||
|
||||
/**
|
||||
* 左侧保留位数
|
||||
* <p>
|
||||
* 仅在脱敏类型为 {@code DesensitizedType.CUSTOM } 时使用
|
||||
* </p>
|
||||
*/
|
||||
int left() default 0;
|
||||
|
||||
/**
|
||||
* 右侧保留位数
|
||||
* <p>
|
||||
* 仅在脱敏类型为 {@code DesensitizedType.CUSTOM } 时使用
|
||||
* </p>
|
||||
*/
|
||||
int right() default 0;
|
||||
|
||||
/**
|
||||
* 脱敏符号(默认:*)
|
||||
*/
|
||||
char character() default StringConstants.C_ASTERISK;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.security.mask.core;
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.BeanProperty;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
|
||||
import top.charles7c.continew.starter.core.constant.StringConstants;
|
||||
import top.charles7c.continew.starter.security.mask.annotation.JsonMask;
|
||||
import top.charles7c.continew.starter.security.mask.strategy.IMaskStrategy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* JSON 脱敏序列化器
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class JsonMaskSerializer extends JsonSerializer<String> implements ContextualSerializer {
|
||||
|
||||
private JsonMask jsonMask;
|
||||
|
||||
public JsonMaskSerializer(JsonMask jsonMask) {
|
||||
this.jsonMask = jsonMask;
|
||||
}
|
||||
|
||||
public JsonMaskSerializer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(String str,
|
||||
JsonGenerator jsonGenerator,
|
||||
SerializerProvider serializerProvider) throws IOException {
|
||||
if (CharSequenceUtil.isBlank(str)) {
|
||||
jsonGenerator.writeString(StringConstants.EMPTY);
|
||||
return;
|
||||
}
|
||||
// 使用自定义脱敏策略
|
||||
Class<? extends IMaskStrategy> strategyClass = jsonMask.strategy();
|
||||
IMaskStrategy maskStrategy = strategyClass != IMaskStrategy.class
|
||||
? SpringUtil.getBean(strategyClass)
|
||||
: jsonMask.value();
|
||||
jsonGenerator.writeString(maskStrategy.mask(str, jsonMask.character(), jsonMask.left(), jsonMask.right()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider,
|
||||
BeanProperty beanProperty) throws JsonMappingException {
|
||||
if (null == beanProperty) {
|
||||
return serializerProvider.findNullValueSerializer(null);
|
||||
}
|
||||
if (!Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
|
||||
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
|
||||
}
|
||||
JsonMask jsonMaskAnnotation = ObjectUtil.defaultIfNull(beanProperty.getAnnotation(JsonMask.class), beanProperty
|
||||
.getContextAnnotation(JsonMask.class));
|
||||
if (null == jsonMaskAnnotation) {
|
||||
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
|
||||
}
|
||||
return new JsonMaskSerializer(jsonMaskAnnotation);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.security.mask.enums;
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import top.charles7c.continew.starter.core.constant.StringConstants;
|
||||
import top.charles7c.continew.starter.security.mask.strategy.IMaskStrategy;
|
||||
|
||||
/**
|
||||
* 脱敏类型
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public enum MaskType implements IMaskStrategy {
|
||||
|
||||
/**
|
||||
* 自定义脱敏
|
||||
*/
|
||||
CUSTOM {
|
||||
@Override
|
||||
public String mask(String str, char character, int left, int right) {
|
||||
return CharSequenceUtil.replace(str, left, str.length() - right, character);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 手机号码
|
||||
* <p>保留前 3 位和后 4 位,例如:135****2210</p>
|
||||
*/
|
||||
MOBILE_PHONE {
|
||||
@Override
|
||||
public String mask(String str, char character, int left, int right) {
|
||||
return CharSequenceUtil.replace(str, 3, str.length() - 4, character);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 固定电话
|
||||
* <p>
|
||||
* 保留前 4 位和后 2 位
|
||||
* </p>
|
||||
*/
|
||||
FIXED_PHONE {
|
||||
@Override
|
||||
public String mask(String str, char character, int left, int right) {
|
||||
return CharSequenceUtil.replace(str, 4, str.length() - 2, character);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 电子邮箱
|
||||
*
|
||||
* <p>
|
||||
* 邮箱前缀仅保留第 1 个字母,@ 符号及后面的地址不脱敏,例如:d**@126.com
|
||||
* </p>
|
||||
*/
|
||||
EMAIL {
|
||||
@Override
|
||||
public String mask(String str, char character, int left, int right) {
|
||||
int index = str.indexOf(StringConstants.AT);
|
||||
if (index <= 1) {
|
||||
return str;
|
||||
}
|
||||
return CharSequenceUtil.replace(str, 1, index, character);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 身份证号
|
||||
* <p>
|
||||
* 保留前 1 位和后 2 位
|
||||
* </p>
|
||||
*/
|
||||
ID_CARD {
|
||||
@Override
|
||||
public String mask(String str, char character, int left, int right) {
|
||||
return CharSequenceUtil.replace(str, 1, str.length() - 2, character);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 银行卡
|
||||
* <p>
|
||||
* 由于银行卡号长度不定,所以只保留前 4 位,后面保留的位数根据卡号决定展示 1-4 位
|
||||
* <ul>
|
||||
* <li>1234 2222 3333 4444 6789 9 => 1234 **** **** **** **** 9</li>
|
||||
* <li>1234 2222 3333 4444 6789 91 => 1234 **** **** **** **** 91</li>
|
||||
* <li>1234 2222 3333 4444 678 => 1234 **** **** **** 678</li>
|
||||
* <li>1234 2222 3333 4444 6789 => 1234 **** **** **** 6789</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
BANK_CARD {
|
||||
@Override
|
||||
public String mask(String str, char character, int left, int right) {
|
||||
String cleanStr = CharSequenceUtil.cleanBlank(str);
|
||||
if (cleanStr.length() < 9) {
|
||||
return cleanStr;
|
||||
}
|
||||
final int length = cleanStr.length();
|
||||
final int endLength = length % 4 == 0 ? 4 : length % 4;
|
||||
final int midLength = length - 4 - endLength;
|
||||
final StringBuilder buffer = new StringBuilder();
|
||||
buffer.append(cleanStr, 0, 4);
|
||||
for (int i = 0; i < midLength; ++i) {
|
||||
if (i % 4 == 0) {
|
||||
buffer.append(StringConstants.SPACE);
|
||||
}
|
||||
buffer.append(character);
|
||||
}
|
||||
buffer.append(StringConstants.SPACE).append(cleanStr, length - endLength, length);
|
||||
return buffer.toString();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 中国大陆车牌(包含普通车辆、新能源车辆)
|
||||
* <p>
|
||||
* 例如:苏D40000 => 苏D4***0
|
||||
* </p>
|
||||
*/
|
||||
CAR_LICENSE {
|
||||
@Override
|
||||
public String mask(String str, char character, int left, int right) {
|
||||
// 普通车牌
|
||||
int length = str.length();
|
||||
if (length == 7) {
|
||||
return CharSequenceUtil.replace(str, 3, 6, character);
|
||||
}
|
||||
// 新能源车牌
|
||||
if (length == 8) {
|
||||
return CharSequenceUtil.replace(str, 3, 7, character);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 中文名
|
||||
* <p>
|
||||
* 只保留第 1 个汉字,例如:李**
|
||||
* </p>
|
||||
*/
|
||||
CHINESE_NAME {
|
||||
@Override
|
||||
public String mask(String str, char character, int left, int right) {
|
||||
return CharSequenceUtil.replace(str, 1, str.length(), character);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 密码
|
||||
* <p>
|
||||
* 密码的全部字符都使用脱敏符号代替,例如:******
|
||||
* </p>
|
||||
*/
|
||||
PASSWORD {
|
||||
@Override
|
||||
public String mask(String str, char character, int left, int right) {
|
||||
return CharSequenceUtil.repeat(character, str.length());
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 地址
|
||||
* <p>
|
||||
* 只显示到地区,不显示详细地址,例如:北京市海淀区****
|
||||
* </p>
|
||||
*/
|
||||
ADDRESS {
|
||||
@Override
|
||||
public String mask(String str, char character, int left, int right) {
|
||||
int length = str.length();
|
||||
return CharSequenceUtil.replace(str, length - 8, length, character);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* IPv4 地址
|
||||
* <p>
|
||||
* 例如:192.0.2.1 => 192.*.*.*
|
||||
* </p>
|
||||
*/
|
||||
IPV4 {
|
||||
@Override
|
||||
public String mask(String str, char character, int left, int right) {
|
||||
return CharSequenceUtil.subBefore(str, StringConstants.DOT, false) + String
|
||||
.format(".%s.%s.%s", character, character, character);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* IPv6 地址
|
||||
* <p>
|
||||
* 例如:2001:0db8:86a3:08d3:1319:8a2e:0370:7344 => 2001:*:*:*:*:*:*:*
|
||||
* </p>
|
||||
*/
|
||||
IPV6 {
|
||||
@Override
|
||||
public String mask(String str, char character, int left, int right) {
|
||||
return CharSequenceUtil.subBefore(str, StringConstants.COLON, false) + String
|
||||
.format(":%s:%s:%s:%s:%s:%s:%s", character, character, character, character, character, character, character);
|
||||
}
|
||||
},;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.starter.security.mask.strategy;
|
||||
|
||||
/**
|
||||
* 脱敏策略
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public interface IMaskStrategy {
|
||||
|
||||
/**
|
||||
* 数据脱敏
|
||||
*
|
||||
* @param str 原始字符串
|
||||
* @param character 脱敏符号
|
||||
* @param left 左侧保留位数
|
||||
* @param right 右侧保留位数
|
||||
* @return 脱敏后的数据
|
||||
*/
|
||||
String mask(String str, char character, int left, int right);
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
package top.charles7c.continew.starter.security.password.autoconfigure;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -80,7 +80,8 @@ public class PasswordEncoderAutoConfiguration {
|
||||
if (CollUtil.isNotEmpty(passwordEncoderList)) {
|
||||
passwordEncoderList.forEach(passwordEncoder -> {
|
||||
String simpleName = passwordEncoder.getClass().getSimpleName();
|
||||
encoders.put(StrUtil.removeSuffix(simpleName, "PasswordEncoder").toLowerCase(), passwordEncoder);
|
||||
encoders.put(CharSequenceUtil.removeSuffix(simpleName, "PasswordEncoder")
|
||||
.toLowerCase(), passwordEncoder);
|
||||
});
|
||||
}
|
||||
String encodingId = properties.getEncodingId();
|
||||
|
||||
@@ -14,7 +14,10 @@
|
||||
<description>ContiNew Starter 安全模块</description>
|
||||
|
||||
<modules>
|
||||
<module>continew-starter-security-all</module>
|
||||
<module>continew-starter-security-password</module>
|
||||
<module>continew-starter-security-mask</module>
|
||||
<module>continew-starter-security-crypto</module>
|
||||
</modules>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package top.charles7c.continew.starter.storage.local.autoconfigure;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -57,10 +57,10 @@ public class LocalStorageAutoConfiguration implements WebMvcConfigurer {
|
||||
LocalStorageProperties.LocalStorageMapping mapping = mappingEntry.getValue();
|
||||
String pathPattern = mapping.getPathPattern();
|
||||
String location = mapping.getLocation();
|
||||
if (StrUtil.isBlank(location)) {
|
||||
if (CharSequenceUtil.isBlank(location)) {
|
||||
throw new IllegalArgumentException(String.format("Path pattern [%s] location is null.", pathPattern));
|
||||
}
|
||||
registry.addResourceHandler(StrUtil.appendIfMissing(pathPattern, StringConstants.PATH_PATTERN))
|
||||
registry.addResourceHandler(CharSequenceUtil.appendIfMissing(pathPattern, StringConstants.PATH_PATTERN))
|
||||
.addResourceLocations(!location.startsWith("file:")
|
||||
? String.format("file:%s", this.format(location))
|
||||
: this.format(location))
|
||||
|
||||
@@ -18,7 +18,7 @@ package top.charles7c.continew.starter.web.autoconfigure.exception;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
@@ -99,7 +99,8 @@ public class GlobalExceptionHandler {
|
||||
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
|
||||
public R<Void> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e,
|
||||
HttpServletRequest request) {
|
||||
String errorMsg = StrUtil.format("参数名:[{}],期望参数类型:[{}]", e.getName(), e.getParameter().getParameterType());
|
||||
String errorMsg = CharSequenceUtil.format("参数名:[{}],期望参数类型:[{}]", e.getName(), e.getParameter()
|
||||
.getParameterType());
|
||||
log.warn("请求地址 [{}],参数转换失败,{}。", request.getRequestURI(), errorMsg, e);
|
||||
return R.fail(HttpStatus.BAD_REQUEST.value(), errorMsg);
|
||||
}
|
||||
@@ -110,7 +111,7 @@ public class GlobalExceptionHandler {
|
||||
@ExceptionHandler(MaxUploadSizeExceededException.class)
|
||||
public R<Void> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e, HttpServletRequest request) {
|
||||
log.warn("请求地址 [{}],上传文件失败,文件大小超过限制。", request.getRequestURI(), e);
|
||||
String sizeLimit = StrUtil.subBetween(e.getMessage(), "The maximum size ", " for");
|
||||
String sizeLimit = CharSequenceUtil.subBetween(e.getMessage(), "The maximum size ", " for");
|
||||
String errorMsg = String.format("请上传小于 %sMB 的文件", NumberUtil.parseLong(sizeLimit) / 1024 / 1024);
|
||||
return R.fail(HttpStatus.BAD_REQUEST.value(), errorMsg);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package top.charles7c.continew.starter.web.autoconfigure.trace;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import com.yomahub.tlog.context.TLogContext;
|
||||
import jakarta.servlet.*;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
@@ -53,7 +53,7 @@ public class TLogServletFilter implements Filter {
|
||||
TLogWebCommon.loadInstance().preHandle(httpServletRequest);
|
||||
// 把 traceId 放入 response 的 header,为了方便有些人有这样的需求,从前端拿整条链路的 traceId
|
||||
String headerName = traceProperties.getHeaderName();
|
||||
if (StrUtil.isNotBlank(headerName)) {
|
||||
if (CharSequenceUtil.isNotBlank(headerName)) {
|
||||
httpServletResponse.addHeader(headerName, TLogContext.getTraceId());
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package top.charles7c.continew.starter.web.util;
|
||||
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import jakarta.servlet.ServletContext;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -55,7 +55,7 @@ public class SpringWebUtils {
|
||||
.getFieldValue(resourceHandlerMapping, "handlerMap");
|
||||
// 移除之前注册的映射
|
||||
for (Map.Entry<String, String> entry : handlerMap.entrySet()) {
|
||||
String pathPattern = StrUtil.appendIfMissing(entry.getKey(), StringConstants.PATH_PATTERN);
|
||||
String pathPattern = CharSequenceUtil.appendIfMissing(entry.getKey(), StringConstants.PATH_PATTERN);
|
||||
oldHandlerMap.remove(pathPattern);
|
||||
}
|
||||
}
|
||||
@@ -80,10 +80,10 @@ public class SpringWebUtils {
|
||||
final ResourceHandlerRegistry resourceHandlerRegistry = new ResourceHandlerRegistry(applicationContext, servletContext, contentNegotiationManager, urlPathHelper);
|
||||
for (Map.Entry<String, String> entry : handlerMap.entrySet()) {
|
||||
// 移除之前注册的映射
|
||||
String pathPattern = StrUtil.appendIfMissing(entry.getKey(), StringConstants.PATH_PATTERN);
|
||||
String pathPattern = CharSequenceUtil.appendIfMissing(entry.getKey(), StringConstants.PATH_PATTERN);
|
||||
oldHandlerMap.remove(pathPattern);
|
||||
// 重新注册映射
|
||||
String resourceLocations = StrUtil.appendIfMissing(entry.getValue(), StringConstants.SLASH);
|
||||
String resourceLocations = CharSequenceUtil.appendIfMissing(entry.getValue(), StringConstants.SLASH);
|
||||
resourceHandlerRegistry.addResourceHandler(pathPattern).addResourceLocations("file:" + resourceLocations);
|
||||
}
|
||||
final Map<String, ?> additionalUrlMap = ReflectUtil
|
||||
|
||||
Reference in New Issue
Block a user