Compare commits

..

29 Commits

Author SHA1 Message Date
4f3ee18bef release: v2.2.0 2024-06-30 22:24:42 +08:00
13788d6f57 build(security/limiter): 移除无用依赖 2024-06-30 12:26:44 +08:00
635b664d5f chore(captcha/behavior): 默认启用行为验证码自动配置 2024-06-30 12:25:14 +08:00
3e4b6ab3a9 chore(messaging/mail): 优化邮件配置服务命名 2024-06-30 12:06:44 +08:00
7bc25b2f8b fix(security/limiter): 修复默认限流器名称生成器错误 2024-06-30 11:10:38 +08:00
51c47751f4 refactor(security/limiter): 重构限流器,支持自定义限流器名称生成器 2024-06-28 23:34:51 +08:00
13b3f24845 feat(core): 新增表达式解析工具类 2024-06-28 23:03:56 +08:00
6b90880c21 chore: 优化属性前缀命名 2024-06-28 21:42:53 +08:00
0ad7b18521 refactor(core): 重构线程池自动配置 2024-06-26 22:51:09 +08:00
82574cd5ce fix(api-doc): 修复接口文档配置错误 2024-06-25 22:07:49 +08:00
491721e887 chore: 优化部分文件格式 2024-06-25 21:32:15 +08:00
de056aa0c4 refactor(core): 重构线程池自动配置 2024-06-25 21:31:42 +08:00
KAI
a89765f49e feat(security/limiter): 新增限流器 2024-06-25 01:01:43 +00:00
3e9a15295a feat(core): 新增 JSR 303 校验方法 2024-06-24 22:09:09 +08:00
jasmine
ce08f28a61 feat: 新增国际化及全局异常码配置 2024-06-24 13:44:03 +00:00
07d9fdf3e2 release: v2.1.1 2024-06-23 12:26:58 +08:00
6e73472589 build(extension/crud): 排除 SaToken Starter 中的 Web 依赖 2024-06-23 11:32:34 +08:00
6c10e80d71 refactor(messaging/websocket): 优化 WebSocket 相关配置及命名 2024-06-23 11:30:03 +08:00
a208fa59b2 chore(core): 线程池配置增加默认线程前缀配置 2024-06-23 10:29:14 +08:00
92fd0a8ab2 feat(cache/redisson): 新增 List 缓存操作方法 2024-06-23 10:09:10 +08:00
Yoofff
1bc4ba76b2 perf: 优化全局文件上传异常-超过上传大小限制的异常判断 2024-06-21 01:18:02 +00:00
ca1dd8fd43 docs: 修复加群二维码显示错误 2024-06-20 22:57:34 +08:00
ed2e3762b6 chore: 更新 GitHub 相关仓库链接 2024-06-20 22:56:11 +08:00
0bba30b9c4 fix(log/core): 兼容日志记录 IPv6 IP 归属地场景 2024-06-20 22:51:09 +08:00
332ef45c4a ci: 更新 GitHub Action 脚本 2024-06-20 22:46:07 +08:00
27b949dd9d feat(messaging/mail): 邮件支持自定义发件人
Closes #5
2024-06-18 23:27:18 +08:00
c84539b461 feat(data/mybatis-plus): 新增防全表更新与删除插件启用配置 2024-06-18 23:20:04 +08:00
91cd4d4d22 chore: 优化部分代码格式 2024-06-18 23:12:00 +08:00
cary
a235a6ea8b fix(security/crypto): 修复处理 MP Wrapper 时 无法加密的情况 (#4) 2024-06-18 14:14:55 +08:00
78 changed files with 2052 additions and 610 deletions

28
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: Build
on:
pull_request:
branches:
- dev
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
jdk-version:
- 17
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: "adopt"
java-version: ${{ matrix.jdk-version }}
cache: "maven"
- name: Compile
run: |
sed -i.bak '/<repositories>/,/<\/repositories>/d' pom.xml
sed -i.bak '/<pluginRepositories>/,/<\/pluginRepositories>/d' pom.xml
mvn -B compile --file pom.xml

View File

@@ -7,7 +7,7 @@ on:
jobs:
release:
if: github.repository == 'Charles7c/continew-starter'
if: github.repository == 'continew-org/continew-starter'
runs-on: ubuntu-latest
steps:
- name: Checkout

View File

@@ -1,26 +1,31 @@
name: Analysis Code Quality
name: Scan
on:
# 推送时执行
push:
branches: [dev]
# 可手动执行
workflow_dispatch:
branches:
- dev
pull_request:
branches:
- dev
jobs:
# Sonar 扫描
sonar-scan:
runs-on: ubuntu-latest
strategy:
matrix:
jdk-version:
- 17
steps:
- name: Checkout
uses: actions/checkout@master
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Java
uses: actions/setup-java@master
uses: actions/setup-java@v4
with:
distribution: 'adopt'
java-version: 17
distribution: "adopt"
java-version: ${{ matrix.jdk-version }}
cache: "maven"
- name: Cache SonarCloud packages
uses: actions/cache@v3
with:
@@ -34,7 +39,10 @@ jobs:
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2
- name: Analyze
run: mvn -B verify -Psonar
run: |
sed -i.bak '/<repositories>/,/<\/repositories>/d' pom.xml
sed -i.bak '/<pluginRepositories>/,/<\/pluginRepositories>/d' pom.xml
mvn -B verify -Psonar
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

BIN
.idea/icon.png generated

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -1,319 +1,359 @@
## [v2.1.0](https://github.com/Charles7c/continew-starter/compare/v2.0.2...v2.1.0) (2024-06-05)
## [v2.2.0](https://github.com/continew-org/continew-starter/compare/v2.1.1...v2.2.0) (2024-06-30)
### ✨ 新特性
- 【messaging/mail】新增动态邮箱配置 ([Gitee#19](https://gitee.com/continew/continew-starter/pulls/19)) ([ee30e86](https://github.com/Charles7c/continew-starter/commit/ee30e861ff536ee3ed6f14ff5ded5af7a513941d)) ([7feda79](https://github.com/Charles7c/continew-starter/commit/7feda79359ea40331eee1e3d4d5fd12000f027c5))
-data/mybatis-flex】新增 continew-starter-data-mybatis-flex 数据访问模块Mybatis Flex 自动配置) ([Gitee#18](https://gitee.com/continew/continew-starter/pulls/18)) ([124c7ff](https://github.com/Charles7c/continew-starter/commit/124c7ffe11a0e6563d9b513036c53ff66edbb9b3))
-extension/crud】新增查询字典列表方法 ([3d2a427](https://github.com/Charles7c/continew-starter/commit/3d2a4271d5eed676f16f4728b461dc3b298a65a9))
-messaging/websocket】新增 continew-starter-messaging-websocket 消息模块 ([cc079e8](https://github.com/Charles7c/continew-starter/commit/cc079e8bf422825bf9a96ddbd4329fc77d3cbf2c))
- 新增国际化及全局异常码配置 (Gitee#25) ([ce08f28](https://github.com/continew-org/continew-starter/commit/ce08f28a618bbeb2c501defe71f9018972a4828b))
-core】新增 JSR 303 校验方法 ([3e9a152](https://github.com/continew-org/continew-starter/commit/3e9a15295a79901cf1c5fa603d6a7407e7e2a2ec))
-security/limiter】新增限流器 ([a89765f](https://github.com/continew-org/continew-starter/commit/a89765f49ef9d3b1ce4b3a420507b43792ed69a1)) ([51c4775](https://github.com/continew-org/continew-starter/commit/51c47751f4ef92bb111619ee9ceb7c3ce4e2dba4)) ([7bc25b2](https://github.com/continew-org/continew-starter/commit/7bc25b2f8bdb74ad295c54ab82cdae88f6264096)) ([13788d6](https://github.com/continew-org/continew-starter/commit/13788d6f5796e87900dd83ece955cd921ffc3946))
-core】新增表达式 SPEL 解析工具类 ([13b3f24](https://github.com/continew-org/continew-starter/commit/13b3f2484555b69dd25b280806f98d98d53f75fe))
## [v2.0.2](https://github.com/Charles7c/continew-starter/compare/v2.0.1...v2.0.2) (2024-05-20)
### 🐛 问题修复
- 【api-doc】修复接口文档配置错误 ([82574cd](https://github.com/continew-org/continew-starter/commit/82574cd5cee923d6dfe447414c0a2453defc8790))
### 💎 功能优化
- 【core】重构线程池自动配置 ([de056aa](https://github.com/continew-org/continew-starter/commit/de056aa0c42621f5d5cf7690f2a42f54ffa1cd7e)) ([0ad7b18](https://github.com/continew-org/continew-starter/commit/0ad7b185212da31d8b6afdab6dd9cd8f72f83acb))
- 优化属性前缀命名 ([6b90880](https://github.com/continew-org/continew-starter/commit/6b90880c21d4fd7e603397692cf88a98f30194a0))
- 【captcha/behavior】默认启用行为验证码自动配置 ([635b664](https://github.com/continew-org/continew-starter/commit/635b664d5f92e5d01cadef4c868753eb41279c7d))
- 【messaging/mail】优化邮件配置服务命名 ([3e4b6ab](https://github.com/continew-org/continew-starter/commit/3e4b6ab3a9590639e1fa606b0d52b29e83ecb890))
## [v2.1.1](https://github.com/continew-org/continew-starter/compare/v2.1.0...v2.1.1) (2024-06-23)
### ✨ 新特性
-file/excel】新增 Easy Excel List 集合转换器 ([1faa46e](https://github.com/Charles7c/continew-starter/commit/1faa46e12505c025e5ca6f1a45158324ac210799))
-data/mybatis-plus】新增防全表更新与删除插件启用配置 ([c84539b](https://github.com/continew-org/continew-starter/commit/c84539b4619d2064c8996cd3e574fa5141e124da))
- 【messaging/mail】邮件支持自定义发件人 ([27b949d](https://github.com/continew-org/continew-starter/commit/27b949dd9d62f68c74d8a12729c4de5fdcc414c9))
- 【cache/redisson】RedisUtils 新增 List 缓存操作方法 ([92fd0a8](https://github.com/continew-org/continew-starter/commit/92fd0a8ab2c0250981aceb006a86c1e911719970))
### 🐛 问题修复
- 【security/crypto】修复处理 MP Wrapper 时 无法加密的情况 (GitHub#4) ([a235a6e](https://github.com/continew-org/continew-starter/commit/a235a6ea8b574c3f719857bb99d05e874d4e9bd2))
- 【log/core】兼容日志记录 IPv6 IP 归属地场景 ([0bba30b](https://github.com/continew-org/continew-starter/commit/0bba30b9c4c14e5582b034420f47d0567fdc482a))
- 【extension/crud】排除 SaToken Starter 中的 Web 依赖 ([6e73472](https://github.com/continew-org/continew-starter/commit/6e73472589f65d63ef5d397e681f5af6b31b43c6))
### 💎 功能优化
- 【web】优化全局文件上传异常-超过上传大小限制的异常判断 ([1bc4ba7](https://github.com/continew-org/continew-starter/commit/1bc4ba76b20f06b2932c1bc20948a6d88d40ab2b))
- 【core】线程池配置增加默认线程前缀配置 ([a208fa5](https://github.com/continew-org/continew-starter/commit/a208fa59b2a9457bced4dfd3b24f92ce4a73f1f6))
- 【messaging/websocket】优化 WebSocket 相关配置及命名 ([6c10e80](https://github.com/continew-org/continew-starter/commit/6c10e80d71a7ce768d4fd44006c707c599b3960e))
## [v2.1.0](https://github.com/continew-org/continew-starter/compare/v2.0.2...v2.1.0) (2024-06-05)
### ✨ 新特性
- 【messaging/mail】新增动态邮箱配置 ([Gitee#19](https://gitee.com/continew/continew-starter/pulls/19)) ([ee30e86](https://github.com/continew-org/continew-starter/commit/ee30e861ff536ee3ed6f14ff5ded5af7a513941d)) ([7feda79](https://github.com/continew-org/continew-starter/commit/7feda79359ea40331eee1e3d4d5fd12000f027c5))
- 【data/mybatis-flex】新增 continew-starter-data-mybatis-flex 数据访问模块Mybatis Flex 自动配置) ([Gitee#18](https://gitee.com/continew/continew-starter/pulls/18)) ([124c7ff](https://github.com/continew-org/continew-starter/commit/124c7ffe11a0e6563d9b513036c53ff66edbb9b3))
- 【extension/crud】新增查询字典列表方法 ([3d2a427](https://github.com/continew-org/continew-starter/commit/3d2a4271d5eed676f16f4728b461dc3b298a65a9))
- 【messaging/websocket】新增 continew-starter-messaging-websocket 消息模块 ([cc079e8](https://github.com/continew-org/continew-starter/commit/cc079e8bf422825bf9a96ddbd4329fc77d3cbf2c))
## [v2.0.2](https://github.com/continew-org/continew-starter/compare/v2.0.1...v2.0.2) (2024-05-20)
### ✨ 新特性
- 【file/excel】新增 Easy Excel List 集合转换器 ([1faa46e](https://github.com/continew-org/continew-starter/commit/1faa46e12505c025e5ca6f1a45158324ac210799))
### 🐛 问题修复
- 【captcha/behavior】修复行为验证码接口请求次数限制 ([Gitee#17](https://gitee.com/continew/continew-starter/pulls/17))
- 【extension/crud】修复封装分页 dataList 索引计算错误 ([3e60197](https://github.com/Charles7c/continew-starter/commit/3e60197a31f140c863868440944df0427a9cf8e8))
- 移除部分错误依赖声明 ([881fd37](https://github.com/Charles7c/continew-starter/commit/881fd37dd61836daf3343d9053e9a7c81b005923))
- 【extension/crud】修复封装分页 dataList 索引计算错误 ([3e60197](https://github.com/continew-org/continew-starter/commit/3e60197a31f140c863868440944df0427a9cf8e8))
- 移除部分错误依赖声明 ([881fd37](https://github.com/continew-org/continew-starter/commit/881fd37dd61836daf3343d9053e9a7c81b005923))
## [v2.0.1](https://github.com/Charles7c/continew-starter/compare/v2.0.0...v2.0.1) (2024-05-13)
## [v2.0.1](https://github.com/continew-org/continew-starter/compare/v2.0.0...v2.0.1) (2024-05-13)
### ✨ 新特性
- 【cache/redisson】RedisUtils 新增递增、递减方法 ([596605b](https://github.com/Charles7c/continew-starter/commit/596605b27b046fa0488b113b1c3d87c60277e4ec))
- 【core】新增字符串工具类 ([ca6c709](https://github.com/Charles7c/continew-starter/commit/ca6c7098b124c3121fe626811ea61d53c80a9a4e))
- 【extension/crud】新增多种实体 Base 模型降低 BaseService 耦合 ([5b76534](https://github.com/Charles7c/continew-starter/commit/5b76534df7a54aa6d696515cfbf8059fcdc4a067))
- 【cache/redisson】RedisUtils 新增递增、递减方法 ([596605b](https://github.com/continew-org/continew-starter/commit/596605b27b046fa0488b113b1c3d87c60277e4ec))
- 【core】新增字符串工具类 ([ca6c709](https://github.com/continew-org/continew-starter/commit/ca6c7098b124c3121fe626811ea61d53c80a9a4e))
- 【extension/crud】新增多种实体 Base 模型降低 BaseService 耦合 ([5b76534](https://github.com/continew-org/continew-starter/commit/5b76534df7a54aa6d696515cfbf8059fcdc4a067))
### 🐛 问题修复
- 【extension/crud】修复查询树列表方法中的错误判断 ([f138e5c](https://github.com/Charles7c/continew-starter/commit/f138e5cd4526d8be0fe5e7ae54833b4541748346))
- 【extension/crud】修复查询树列表方法中的错误判断 ([f138e5c](https://github.com/continew-org/continew-starter/commit/f138e5cd4526d8be0fe5e7ae54833b4541748346))
### 💎 功能优化
- 【web】优化部分方法使用 ([57eef27](https://github.com/Charles7c/continew-starter/commit/57eef274a3e34e8bb4de6073452080b2bbdbee53))
- 【web】优化部分方法使用 ([57eef27](https://github.com/continew-org/continew-starter/commit/57eef274a3e34e8bb4de6073452080b2bbdbee53))
### 📦 依赖升级
- 【dependencies】Spring Boot 3.1.10 => 3.1.11 ([GitHub#2](https://github.com/Charles7c/continew-starter/pull/2))
- 【dependencies】SaToken 1.37.0 => 1.38.0 ([b5dd5c7](https://github.com/Charles7c/continew-starter/commit/b5dd5c7f18603b5bd5a509c04ef410c1d64bc2e9))
- 【dependencies】Redisson 3.28.0 => 3.30.0 ([b5dd5c7](https://github.com/Charles7c/continew-starter/commit/b5dd5c7f18603b5bd5a509c04ef410c1d64bc2e9))
- 【dependencies】Crane4j 2.7.0 => 2.8.0 ([b5dd5c7](https://github.com/Charles7c/continew-starter/commit/b5dd5c7f18603b5bd5a509c04ef410c1d64bc2e9))
- 【dependencies】AWS S3 1.12.702 => 1.12.720 ([b5dd5c7](https://github.com/Charles7c/continew-starter/commit/b5dd5c7f18603b5bd5a509c04ef410c1d64bc2e9))
- 【dependencies】IP2Region 3.1.10 => 3.1.11 ([b5dd5c7](https://github.com/Charles7c/continew-starter/commit/b5dd5c7f18603b5bd5a509c04ef410c1d64bc2e9))
- 【dependencies】Spring Boot 3.1.10 => 3.1.11 ([GitHub#2](https://github.com/continew-org/continew-starter/pull/2))
- 【dependencies】SaToken 1.37.0 => 1.38.0 ([b5dd5c7](https://github.com/continew-org/continew-starter/commit/b5dd5c7f18603b5bd5a509c04ef410c1d64bc2e9))
- 【dependencies】Redisson 3.28.0 => 3.30.0 ([b5dd5c7](https://github.com/continew-org/continew-starter/commit/b5dd5c7f18603b5bd5a509c04ef410c1d64bc2e9))
- 【dependencies】Crane4j 2.7.0 => 2.8.0 ([b5dd5c7](https://github.com/continew-org/continew-starter/commit/b5dd5c7f18603b5bd5a509c04ef410c1d64bc2e9))
- 【dependencies】AWS S3 1.12.702 => 1.12.720 ([b5dd5c7](https://github.com/continew-org/continew-starter/commit/b5dd5c7f18603b5bd5a509c04ef410c1d64bc2e9))
- 【dependencies】IP2Region 3.1.10 => 3.1.11 ([b5dd5c7](https://github.com/continew-org/continew-starter/commit/b5dd5c7f18603b5bd5a509c04ef410c1d64bc2e9))
## [v2.0.0](https://github.com/Charles7c/continew-starter/compare/v1.5.1...v2.0.0) (2024-04-17)
## [v2.0.0](https://github.com/continew-org/continew-starter/compare/v1.5.1...v2.0.0) (2024-04-17)
### ✨ 新特性
- 【web】新增 XSS 过滤器 ([2656da4](https://github.com/Charles7c/continew-starter/commit/2656da450c866681c270c30131c028847e1e21d4)) ([2573fb0](https://github.com/Charles7c/continew-starter/commit/2573fb04f0698db3ab662a0e7bf762c04300468b)) ([Gitee PR#13](https://gitee.com/continew/continew-starter/pulls/13))
- 【web】新增 XSS 过滤器 ([2656da4](https://github.com/continew-org/continew-starter/commit/2656da450c866681c270c30131c028847e1e21d4)) ([2573fb0](https://github.com/continew-org/continew-starter/commit/2573fb04f0698db3ab662a0e7bf762c04300468b)) ([Gitee PR#13](https://gitee.com/continew/continew-starter/pulls/13))
### 🐛 问题修复
- 【cache/redisson】修复Failed to submit a listener notification task. Event loop shut down? 问题 ([2d71eca](https://github.com/Charles7c/continew-starter/commit/2d71eca07505f143c82cca8d24bc6f54105d0bbb))
- 【cache/redisson】修复Failed to submit a listener notification task. Event loop shut down? 问题 ([2d71eca](https://github.com/continew-org/continew-starter/commit/2d71eca07505f143c82cca8d24bc6f54105d0bbb))
### 💎 功能优化
- 【core】应用关闭支持优雅关闭自定义线程池 ScheduledExecutorService ([c4051a6](https://github.com/Charles7c/continew-starter/commit/c4051a6465e0d70d119ec27c6ae4eb4d1893339a))
- 【extension/crud】优化部分注释 ([fe310bc](https://github.com/Charles7c/continew-starter/commit/fe310bcb879d3f20eb8ead4b39436ec96b99e7f6))
- 移除 Qodana 扫描License 已过期 ([b0e567d](https://github.com/Charles7c/continew-starter/commit/b0e567d749b988e3f45772742a273a422a661edb))
- 【core】应用关闭支持优雅关闭自定义线程池 ScheduledExecutorService ([c4051a6](https://github.com/continew-org/continew-starter/commit/c4051a6465e0d70d119ec27c6ae4eb4d1893339a))
- 【extension/crud】优化部分注释 ([fe310bc](https://github.com/continew-org/continew-starter/commit/fe310bcb879d3f20eb8ead4b39436ec96b99e7f6))
- 移除 Qodana 扫描License 已过期 ([b0e567d](https://github.com/continew-org/continew-starter/commit/b0e567d749b988e3f45772742a273a422a661edb))
### 📦 依赖升级
- 【dependencies】Spring Boot 3.1.9 => 3.1.10 ([2d71eca](https://github.com/Charles7c/continew-starter/commit/2d71eca07505f143c82cca8d24bc6f54105d0bbb))
- 【dependencies】Redisson 3.27.2 => 3.28.0 ([2d71eca](https://github.com/Charles7c/continew-starter/commit/2d71eca07505f143c82cca8d24bc6f54105d0bbb))
- 【dependencies】CosId 2.6.6 => 2.6.8 ([2d71eca](https://github.com/Charles7c/continew-starter/commit/2d71eca07505f143c82cca8d24bc6f54105d0bbb))
- 【dependencies】SMS4J 3.1.1 => 3.2.1 ([2d71eca](https://github.com/Charles7c/continew-starter/commit/2d71eca07505f143c82cca8d24bc6f54105d0bbb))
- 【dependencies】Easy Excel 3.3.3 => 3.3.4 ([2d71eca](https://github.com/Charles7c/continew-starter/commit/2d71eca07505f143c82cca8d24bc6f54105d0bbb))
- 【dependencies】AWS S3 1.12.675 => 1.12.702 ([2d71eca](https://github.com/Charles7c/continew-starter/commit/2d71eca07505f143c82cca8d24bc6f54105d0bbb))
- 【dependencies】Crane4j 2.6.1 => 2.7.0 ([2d71eca](https://github.com/Charles7c/continew-starter/commit/2d71eca07505f143c82cca8d24bc6f54105d0bbb))
- 【dependencies】TLog 1.5.1 => 1.5.2 ([2d71eca](https://github.com/Charles7c/continew-starter/commit/2d71eca07505f143c82cca8d24bc6f54105d0bbb))
- 【dependencies】Hutool 5.8.26 => 5.8.27 ([2d71eca](https://github.com/Charles7c/continew-starter/commit/2d71eca07505f143c82cca8d24bc6f54105d0bbb))
- 【dependencies】IP2Region 3.1.9 => 3.1.10 ([2d71eca](https://github.com/Charles7c/continew-starter/commit/2d71eca07505f143c82cca8d24bc6f54105d0bbb))
- 【dependencies】Spring Boot 3.1.9 => 3.1.10 ([2d71eca](https://github.com/continew-org/continew-starter/commit/2d71eca07505f143c82cca8d24bc6f54105d0bbb))
- 【dependencies】Redisson 3.27.2 => 3.28.0 ([2d71eca](https://github.com/continew-org/continew-starter/commit/2d71eca07505f143c82cca8d24bc6f54105d0bbb))
- 【dependencies】CosId 2.6.6 => 2.6.8 ([2d71eca](https://github.com/continew-org/continew-starter/commit/2d71eca07505f143c82cca8d24bc6f54105d0bbb))
- 【dependencies】SMS4J 3.1.1 => 3.2.1 ([2d71eca](https://github.com/continew-org/continew-starter/commit/2d71eca07505f143c82cca8d24bc6f54105d0bbb))
- 【dependencies】Easy Excel 3.3.3 => 3.3.4 ([2d71eca](https://github.com/continew-org/continew-starter/commit/2d71eca07505f143c82cca8d24bc6f54105d0bbb))
- 【dependencies】AWS S3 1.12.675 => 1.12.702 ([2d71eca](https://github.com/continew-org/continew-starter/commit/2d71eca07505f143c82cca8d24bc6f54105d0bbb))
- 【dependencies】Crane4j 2.6.1 => 2.7.0 ([2d71eca](https://github.com/continew-org/continew-starter/commit/2d71eca07505f143c82cca8d24bc6f54105d0bbb))
- 【dependencies】TLog 1.5.1 => 1.5.2 ([2d71eca](https://github.com/continew-org/continew-starter/commit/2d71eca07505f143c82cca8d24bc6f54105d0bbb))
- 【dependencies】Hutool 5.8.26 => 5.8.27 ([2d71eca](https://github.com/continew-org/continew-starter/commit/2d71eca07505f143c82cca8d24bc6f54105d0bbb))
- 【dependencies】IP2Region 3.1.9 => 3.1.10 ([2d71eca](https://github.com/continew-org/continew-starter/commit/2d71eca07505f143c82cca8d24bc6f54105d0bbb))
### 💥 破坏性变更
- groupId 及基础包名调整更短的包名聚合品牌形象。top.charles7c.continew => top.continew ([dbb7a63](https://github.com/Charles7c/continew-starter/commit/dbb7a6319e9440e7a05f2eb4aab3b445f43197f7))
- groupId 及基础包名调整更短的包名聚合品牌形象。top.charles7c.continew => top.continew ([dbb7a63](https://github.com/continew-org/continew-starter/commit/dbb7a6319e9440e7a05f2eb4aab3b445f43197f7))
## [v1.5.1](https://github.com/Charles7c/continew-starter/compare/v1.5.0...v1.5.1) (2024-03-23)
## [v1.5.1](https://github.com/continew-org/continew-starter/compare/v1.5.0...v1.5.1) (2024-03-23)
### ✨ 新特性
- 【web】FileUploadUtils 新增文件下载方法 ([171040b](https://github.com/Charles7c/continew-starter/commit/171040b674ae18dd6560f1501f7c0d47a1f4b8ba))
- 【web】FileUploadUtils 新增文件下载方法 ([171040b](https://github.com/continew-org/continew-starter/commit/171040b674ae18dd6560f1501f7c0d47a1f4b8ba))
### 🐛 问题修复
- 【api-doc】修复接口级鉴权配置不生效的问题 ([ab4a72e](https://github.com/Charles7c/continew-starter/commit/ab4a72e0fffe20b492c2250cc997ba3e94794118))
- 【api-doc】修复接口级鉴权配置不生效的问题 ([ab4a72e](https://github.com/continew-org/continew-starter/commit/ab4a72e0fffe20b492c2250cc997ba3e94794118))
### 📦 依赖升级
- 【dependencies】Redisson 3.27.1 => 3.27.2 ([0f43e1c](https://github.com/Charles7c/continew-starter/commit/0f43e1cff7f2fc58273de300acf4432f3a300af4))
- 【dependencies】Crane4j 2.6.0 => 2.6.1 ([0f43e1c](https://github.com/Charles7c/continew-starter/commit/0f43e1cff7f2fc58273de300acf4432f3a300af4))
- 【dependencies】Redisson 3.27.1 => 3.27.2 ([0f43e1c](https://github.com/continew-org/continew-starter/commit/0f43e1cff7f2fc58273de300acf4432f3a300af4))
- 【dependencies】Crane4j 2.6.0 => 2.6.1 ([0f43e1c](https://github.com/continew-org/continew-starter/commit/0f43e1cff7f2fc58273de300acf4432f3a300af4))
## [v1.5.0](https://github.com/Charles7c/continew-starter/compare/v1.4.1...v1.5.0) (2024-03-08)
## [v1.5.0](https://github.com/continew-org/continew-starter/compare/v1.4.1...v1.5.0) (2024-03-08)
### 💎 功能优化
- 【data/mybatis-plus】重构 IService 及 BaseService 体系结构 ([36ce07b](https://github.com/Charles7c/continew-starter/commit/36ce07b600fc54eeca3682d09aa5992cb2b35c17))
- 【json/jackson】优化 Jackson 默认配置 ([a54294d](https://github.com/Charles7c/continew-starter/commit/a54294df6e24dc36d36cf6a94559af2ed4c32d44))
- 【extension/crud】调整 BaseController checkPermission 方法的访问修饰符 private => protected ([12bdf3e](https://github.com/Charles7c/continew-starter/commit/12bdf3e44a10e9683b8605642eac9c463c590af4))
- 【data/mybatis-plus】重构 IService 及 BaseService 体系结构 ([36ce07b](https://github.com/continew-org/continew-starter/commit/36ce07b600fc54eeca3682d09aa5992cb2b35c17))
- 【json/jackson】优化 Jackson 默认配置 ([a54294d](https://github.com/continew-org/continew-starter/commit/a54294df6e24dc36d36cf6a94559af2ed4c32d44))
- 【extension/crud】调整 BaseController checkPermission 方法的访问修饰符 private => protected ([12bdf3e](https://github.com/continew-org/continew-starter/commit/12bdf3e44a10e9683b8605642eac9c463c590af4))
### 📦 依赖升级
- 【dependencies】Spring Boot 3.1.8 => 3.1.9 ([98261b9](https://github.com/Charles7c/continew-starter/commit/98261b96dacdba9d210790112248d798e292ae0b))
- 【dependencies】Redisson 3.26.0 => 3.27.1 ([98261b9](https://github.com/Charles7c/continew-starter/commit/98261b96dacdba9d210790112248d798e292ae0b))
- 【dependencies】Crane4j 2.5.0 => 2.6.0 ([98261b9](https://github.com/Charles7c/continew-starter/commit/98261b96dacdba9d210790112248d798e292ae0b))
- 【dependencies】Hutool 5.8.25 => 5.8.26 ([98261b9](https://github.com/Charles7c/continew-starter/commit/98261b96dacdba9d210790112248d798e292ae0b))
- 【dependencies】CosId 2.6.5 => 2.6.6 ([98261b9](https://github.com/Charles7c/continew-starter/commit/98261b96dacdba9d210790112248d798e292ae0b))
- 【dependencies】Amazon S3 1.12.651 => 1.12.675 ([98261b9](https://github.com/Charles7c/continew-starter/commit/98261b96dacdba9d210790112248d798e292ae0b))
- 【dependencies】Ip2region 3.1.7 => 3.1.9 ([98261b9](https://github.com/Charles7c/continew-starter/commit/98261b96dacdba9d210790112248d798e292ae0b))
- 【dependencies】TTL 2.14.4 => 2.14.5 ([98261b9](https://github.com/Charles7c/continew-starter/commit/98261b96dacdba9d210790112248d798e292ae0b))
- 【dependencies】Spring Boot 3.1.8 => 3.1.9 ([98261b9](https://github.com/continew-org/continew-starter/commit/98261b96dacdba9d210790112248d798e292ae0b))
- 【dependencies】Redisson 3.26.0 => 3.27.1 ([98261b9](https://github.com/continew-org/continew-starter/commit/98261b96dacdba9d210790112248d798e292ae0b))
- 【dependencies】Crane4j 2.5.0 => 2.6.0 ([98261b9](https://github.com/continew-org/continew-starter/commit/98261b96dacdba9d210790112248d798e292ae0b))
- 【dependencies】Hutool 5.8.25 => 5.8.26 ([98261b9](https://github.com/continew-org/continew-starter/commit/98261b96dacdba9d210790112248d798e292ae0b))
- 【dependencies】CosId 2.6.5 => 2.6.6 ([98261b9](https://github.com/continew-org/continew-starter/commit/98261b96dacdba9d210790112248d798e292ae0b))
- 【dependencies】Amazon S3 1.12.651 => 1.12.675 ([98261b9](https://github.com/continew-org/continew-starter/commit/98261b96dacdba9d210790112248d798e292ae0b))
- 【dependencies】Ip2region 3.1.7 => 3.1.9 ([98261b9](https://github.com/continew-org/continew-starter/commit/98261b96dacdba9d210790112248d798e292ae0b))
- 【dependencies】TTL 2.14.4 => 2.14.5 ([98261b9](https://github.com/continew-org/continew-starter/commit/98261b96dacdba9d210790112248d798e292ae0b))
## [v1.4.1](https://github.com/Charles7c/continew-starter/compare/v1.4.0...v1.4.1) (2024-02-21)
## [v1.4.1](https://github.com/continew-org/continew-starter/compare/v1.4.0...v1.4.1) (2024-02-21)
### ✨ 新特性
- 【data/core】新增 continew-starter-data-core 模块 ([4ffc5dc](https://github.com/Charles7c/continew-starter/commit/4ffc5dc1d4cdbb572070312eef172a197e216318))
- 【data/core】新增获取数据库类型带默认类型方法 ([31ca1fd](https://github.com/Charles7c/continew-starter/commit/31ca1fda528629906958d8422897ac8ae179daab))
- 【data/core】新增 continew-starter-data-core 模块 ([4ffc5dc](https://github.com/continew-org/continew-starter/commit/4ffc5dc1d4cdbb572070312eef172a197e216318))
- 【data/core】新增获取数据库类型带默认类型方法 ([31ca1fd](https://github.com/continew-org/continew-starter/commit/31ca1fda528629906958d8422897ac8ae179daab))
### 💎 功能优化
- 【security/crypto】调整部分 StrUtil => CharSequenceUtil ([2e5788f](https://github.com/Charles7c/continew-starter/commit/2e5788f007f61f0ec531aa69834229d128311398))
- 【data/mybatis-plus】移除 ` 符号的使用,保持数据库无关性 ([557ea13](https://github.com/Charles7c/continew-starter/commit/557ea1375728695c027004e64e2fa8d2d66215e6))
- 【core】完善自定义异常类构造方法 ([d42585c](https://github.com/Charles7c/continew-starter/commit/d42585cb4d6660724db004893f57a6c67b4690e1))
- 【cache/redisson】完善 Redisson 工具类 ([9ed2dac](https://github.com/Charles7c/continew-starter/commit/9ed2dac00c58621cbc133e3e072c100306cf170a))
- 优化字符串模板方法 API 使用 ([c986784](https://github.com/Charles7c/continew-starter/commit/c9867844b650a69f6b3b3ea4f9af67807091eb1b))
- 【security/crypto】调整部分 StrUtil => CharSequenceUtil ([2e5788f](https://github.com/continew-org/continew-starter/commit/2e5788f007f61f0ec531aa69834229d128311398))
- 【data/mybatis-plus】移除 ` 符号的使用,保持数据库无关性 ([557ea13](https://github.com/continew-org/continew-starter/commit/557ea1375728695c027004e64e2fa8d2d66215e6))
- 【core】完善自定义异常类构造方法 ([d42585c](https://github.com/continew-org/continew-starter/commit/d42585cb4d6660724db004893f57a6c67b4690e1))
- 【cache/redisson】完善 Redisson 工具类 ([9ed2dac](https://github.com/continew-org/continew-starter/commit/9ed2dac00c58621cbc133e3e072c100306cf170a))
- 优化字符串模板方法 API 使用 ([c986784](https://github.com/continew-org/continew-starter/commit/c9867844b650a69f6b3b3ea4f9af67807091eb1b))
### 🐛 问题修复
- 【data/mybatis-plus】修复 Query In、Not In 查询数据类型转换错误 ([9bd4583](https://github.com/Charles7c/continew-starter/commit/9bd458322339f3197f1925347ff16f53fe0f3856))
- 【data/mybatis-plus】修复 Query In、Not In 查询数据类型转换错误 ([9bd4583](https://github.com/continew-org/continew-starter/commit/9bd458322339f3197f1925347ff16f53fe0f3856))
### 💥 破坏性变更
- 【log/core】continew-starter-log-common => continew-starter-log-core ([56a22c4](https://github.com/Charles7c/continew-starter/commit/56a22c4bce2445fb135d1fce7b6155fd5b48051e))
- 【data/mybatis-plus】调整 Query 相关类到 data-core ([3f2a306](https://github.com/Charles7c/continew-starter/commit/3f2a306cad1d15436ae36c1b2eb54f28b50e84b9))
- 【extension/crud】调整 IService 到 data-core ([ab7e987](https://github.com/Charles7c/continew-starter/commit/ab7e987672202f3e80c0e4f64ea0c576ff7cc89f))
- 更新 Gitee 项目链接 ([89108ad](https://github.com/Charles7c/continew-starter/commit/89108ad55addeaf47f224f4ed90ecb42244dbfd8))
- 【log/core】continew-starter-log-common => continew-starter-log-core ([56a22c4](https://github.com/continew-org/continew-starter/commit/56a22c4bce2445fb135d1fce7b6155fd5b48051e))
- 【data/mybatis-plus】调整 Query 相关类到 data-core ([3f2a306](https://github.com/continew-org/continew-starter/commit/3f2a306cad1d15436ae36c1b2eb54f28b50e84b9))
- 【extension/crud】调整 IService 到 data-core ([ab7e987](https://github.com/continew-org/continew-starter/commit/ab7e987672202f3e80c0e4f64ea0c576ff7cc89f))
- 更新 Gitee 项目链接 ([89108ad](https://github.com/continew-org/continew-starter/commit/89108ad55addeaf47f224f4ed90ecb42244dbfd8))
## [v1.4.0](https://github.com/Charles7c/continew-starter/compare/v1.3.0...v1.4.0) (2024-02-14)
## [v1.4.0](https://github.com/continew-org/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))
- 【captcha/graphic】新增图形验证码服务接口并调整验证码默认启用 ([3184faa](https://github.com/continew-org/continew-starter/commit/3184faaa27111845867d2210f0db16381d53d800))
- 【log/httptrace-pro】Log 注解新增 include、exclude 属性,用于扩展或减少日志包含信息 ([669ea85](https://github.com/continew-org/continew-starter/commit/669ea85658c89c631518def8f84d4f5d60059ad7)) ([2afb0b6](https://github.com/continew-org/continew-starter/commit/2afb0b625fc936364c6dacacc735ce421c5cb37c))
- 【security/mask】新增安全模块-脱敏,支持 JSON 数据脱敏 ([7b79519](https://github.com/continew-org/continew-starter/commit/7b795194d3db979c239ab30d78fdb61d95f06896)) ([111e732](https://github.com/continew-org/continew-starter/commit/111e7329673778c475c4ff4aa5ba6eef9f43f506))
- 【security/crypto】新增安全模块-加密,支持 MyBatis ORM 框架字段加密 ([5ccdd9e](https://github.com/continew-org/continew-starter/commit/5ccdd9e5da2a81d6a1f69bdf3f0e4eb1475b68a0)) ([88f82d1](https://github.com/continew-org/continew-starter/commit/88f82d1c0aa5abf8f094564f4b84ae84efd80946)) ([b604f2f](https://github.com/continew-org/continew-starter/commit/b604f2fc7eb938a52338ee41cf1823af374a14da)) ([74a1166](https://github.com/continew-org/continew-starter/commit/74a1166b5f250c2ba8aab027d98bc11e59860c01)) ([9ebcd14](https://github.com/continew-org/continew-starter/commit/9ebcd14878b499039a70380b0773b00b9f8dc111))
- 【security/all】新增 continew-starter-security-all 模块,统一引入加密、脱敏、密码编码器能力 ([12c3d64](https://github.com/continew-org/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))
- 【log/httptrace-pro】默认启用日志 ([2aea8ba](https://github.com/continew-org/continew-starter/commit/2aea8ba8318dded142a274221af7de2b62d4ced9))
- 【log/httptrace-pro】兼容小写 user-agent 情况 ([18b9d1b](https://github.com/continew-org/continew-starter/commit/18b9d1ba799ce96d8831b7243508b2517ff5c5c7))
- 【auth/satoken】JWT 配置支持启用/关闭 ([c33a670](https://github.com/continew-org/continew-starter/commit/c33a6709f50c2240cc9826c4ee2e83d88db5fb07))
- 【cache/redisson】优化协议前缀变量命名 ([00798bd](https://github.com/continew-org/continew-starter/commit/00798bdb4c82c8ec8b3cf1110a0afaaa94ad2b27))
- 【auth】调整 Redisson 模块为可选依赖 ([00bba33](https://github.com/continew-org/continew-starter/commit/00bba33517c15936ec2f40a8a7f3213d25a223aa))
- 【data/mybatis-plus】重构 ID 生成器配置支持默认、CosId、自定义 ([c9311df](https://github.com/continew-org/continew-starter/commit/c9311df093d4524b272535640333c413a2eda86f)) ([58dc51f](https://github.com/continew-org/continew-starter/commit/58dc51f66c3a77f7f1621557cdd065243b6ae5a9))
- 【message/sms】精简部分依赖 ([f67f278](https://github.com/continew-org/continew-starter/commit/f67f278784002de553c923f399d585e35f0a6356))
- 根据 Sonar 建议调整StrUtil => CharSequenceUtil ([ea71cf5](https://github.com/continew-org/continew-starter/commit/ea71cf573b7b6452b9315c67967f29e25468a04a))
### 🐛 问题修复
- 【extension/crud】修复删除后置处理方法访问修饰符使用错误 ([24f9975](https://github.com/Charles7c/continew-starter/commit/24f99754d041e113f07eb43570d6a49c4ff24008))
- 【extension/crud】修复删除后置处理方法访问修饰符使用错误 ([24f9975](https://github.com/continew-org/continew-starter/commit/24f99754d041e113f07eb43570d6a49c4ff24008))
- 【message/mail】修复发送邮件收件人不为空判断错误 ([Gitee PR#12](https://gitee.com/continew/continew-starter/pulls/12))
- 【auth/satoken】修复 SaInterceptor Bean 获取方式错误 ([1ba1596](https://github.com/Charles7c/continew-starter/commit/1ba1596f4e4b31d82e174e981711e45a1df67ee7))
- 【auth/satoken】修复 SaInterceptor Bean 获取方式错误 ([1ba1596](https://github.com/continew-org/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))
- 【dependencies】Spring Boot 3.1.7 => 3.1.8 ([ab76665](https://github.com/continew-org/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
- 【dependencies】Dynamic Datasource 4.2.0 => 4.3.0 ([ab76665](https://github.com/continew-org/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
- 【dependencies】JetCache 2.7.4 => 2.7.5 ([ab76665](https://github.com/continew-org/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
- 【dependencies】Redisson 3.25.2 => 3.26.0 ([ab76665](https://github.com/continew-org/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
- 【dependencies】SMS4J 3.0.4 => 3.1.1 ([ab76665](https://github.com/continew-org/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
- 【dependencies】X File Storage 2.0.0 => 2.1.0 ([ab76665](https://github.com/continew-org/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
- 【dependencies】Amazon S3 1.12.626 => 1.12.651 ([ab76665](https://github.com/continew-org/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
- 【dependencies】Crane4j 2.4.0 => 2.5.0 ([ab76665](https://github.com/continew-org/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8)) ([c963978](https://github.com/continew-org/continew-starter/commit/c96397898027140c243b034dc3d23bd3d60695e7))
- 【dependencies】Knife4j 4.4.0 => 4.5.0 ([ab76665](https://github.com/continew-org/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
- 【dependencies】Hutool 5.8.24 => 5.8.25 ([ab76665](https://github.com/continew-org/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
- 【dependencies】ip2region 3.1.6 => 3.1.7 ([ab76665](https://github.com/continew-org/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
- 【dependencies】flatten-maven-plugin 1.5.0 => 1.6.0 ([ab76665](https://github.com/continew-org/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
- 【dependencies】spotless-maven-plugin 2.40.0 => 2.43.0 ([ab76665](https://github.com/continew-org/continew-starter/commit/ab76665aab8cf06e508f039dbce13959e171d0c8))
## [v1.3.0](https://github.com/Charles7c/continew-starter/compare/v1.2.0...v1.3.0) (2024-02-03)
## [v1.3.0](https://github.com/continew-org/continew-starter/compare/v1.2.0...v1.3.0) (2024-02-03)
### ✨ 新特性
* 【data/mybatis-plus】新增 QueryIgnore 忽略查询解析注解 ([91651b0](https://github.com/Charles7c/continew-starter/commit/91651b0b59cf642cd59aca068d8bca4554dc9895))
* 【security/password】新增安全模块-密码编码器自动配置 ([47a4d57](https://github.com/Charles7c/continew-starter/commit/47a4d57dee3739de12ccbe9e15e25aef5b9ae558)) ([Gitee PR#9](https://gitee.com/continew/continew-starter/pulls/9))
* 【web】新增链路跟踪自动配置 ([8fc19ab](https://github.com/Charles7c/continew-starter/commit/8fc19ab9b87b1a1b6d290ee9a40d0157de267324))
* 【data/mybatis-plus】新增 QueryIgnore 忽略查询解析注解 ([91651b0](https://github.com/continew-org/continew-starter/commit/91651b0b59cf642cd59aca068d8bca4554dc9895))
* 【security/password】新增安全模块-密码编码器自动配置 ([47a4d57](https://github.com/continew-org/continew-starter/commit/47a4d57dee3739de12ccbe9e15e25aef5b9ae558)) ([Gitee PR#9](https://gitee.com/continew/continew-starter/pulls/9))
* 【web】新增链路跟踪自动配置 ([8fc19ab](https://github.com/continew-org/continew-starter/commit/8fc19ab9b87b1a1b6d290ee9a40d0157de267324))
### 💎 功能优化
- 【extension/crud】排序字段增加是否存在校验 ([Gitee PR#7](https://gitee.com/continew/continew-starter/pulls/7))
- 【data/mybatis-plus】优化数据权限处理器代码结构 ([aecefa1](https://github.com/Charles7c/continew-starter/commit/aecefa15ecbb9660f2ffa2f3bef3ad9eeb810916))
- 【auth/satoken】支持更灵活的动态化路由拦截鉴权 ([31f29db](https://github.com/Charles7c/continew-starter/commit/31f29db19dede2cbf6988946b0dd8c8f153d1bd9))
- 【auth/satoken】优化 SaToken 持久层配置 ([e6f8ac8](https://github.com/Charles7c/continew-starter/commit/e6f8ac8afa1b6c487343dc88d8ac7fdfde40e58b))
- 【captcha/behavior】优化行为验证码缓存配置 ([8598e6d](https://github.com/Charles7c/continew-starter/commit/8598e6d109c1ca6be3e973ceb41c6dd7bd93c333))
- 【storage/local】优化存储模块依赖 ([dcb6568](https://github.com/Charles7c/continew-starter/commit/dcb6568916cd549f1c403ece1c4f4d29ecc320b9))
- 移除 Lombok 私有构造注解使用 ([11d0798](https://github.com/Charles7c/continew-starter/commit/11d0798f92a5fe4eda6300a7e6065f2d3afef0df))
- 移除 Lombok 依赖,再度精简依赖 ([0eb6afa](https://github.com/Charles7c/continew-starter/commit/0eb6afabb6ccaa9d421981280c896e381f68b9a6)) ([Gitee PR#9](https://gitee.com/continew/continew-starter/pulls/9))
- 新增 Qodana、Sonar 扫描 ([ab1e999](https://github.com/Charles7c/continew-starter/commit/ab1e999094d9349a24eff51382a940f0ec682801)) ([1a8c589](https://github.com/Charles7c/continew-starter/commit/1a8c589083f80eddd2fe7e4c99751c699dd4d357))
- 【data/mybatis-plus】优化数据权限处理器代码结构 ([aecefa1](https://github.com/continew-org/continew-starter/commit/aecefa15ecbb9660f2ffa2f3bef3ad9eeb810916))
- 【auth/satoken】支持更灵活的动态化路由拦截鉴权 ([31f29db](https://github.com/continew-org/continew-starter/commit/31f29db19dede2cbf6988946b0dd8c8f153d1bd9))
- 【auth/satoken】优化 SaToken 持久层配置 ([e6f8ac8](https://github.com/continew-org/continew-starter/commit/e6f8ac8afa1b6c487343dc88d8ac7fdfde40e58b))
- 【captcha/behavior】优化行为验证码缓存配置 ([8598e6d](https://github.com/continew-org/continew-starter/commit/8598e6d109c1ca6be3e973ceb41c6dd7bd93c333))
- 【storage/local】优化存储模块依赖 ([dcb6568](https://github.com/continew-org/continew-starter/commit/dcb6568916cd549f1c403ece1c4f4d29ecc320b9))
- 移除 Lombok 私有构造注解使用 ([11d0798](https://github.com/continew-org/continew-starter/commit/11d0798f92a5fe4eda6300a7e6065f2d3afef0df))
- 移除 Lombok 依赖,再度精简依赖 ([0eb6afa](https://github.com/continew-org/continew-starter/commit/0eb6afabb6ccaa9d421981280c896e381f68b9a6)) ([Gitee PR#9](https://gitee.com/continew/continew-starter/pulls/9))
- 新增 Qodana、Sonar 扫描 ([ab1e999](https://github.com/continew-org/continew-starter/commit/ab1e999094d9349a24eff51382a940f0ec682801)) ([1a8c589](https://github.com/continew-org/continew-starter/commit/1a8c589083f80eddd2fe7e4c99751c699dd4d357))
- 优化大量代码,解决 [Sonar](https://sonarcloud.io/organizations/charles7c/projects)、[Codacy](https://app.codacy.com/gh/Charles7c/continew-admin/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)、[Qodana](https://qodana.cloud/organizations/pQDPD/teams/p5jqd/) 扫描问题点击各链接查看对应实时质量分析报告Codacy 已达到 A
### 🐛 问题修复
- 【web】配置 Validator 失败立即返回模式 ([1223f60](https://github.com/Charles7c/continew-starter/commit/1223f6052d459087419b7373b8a2d7cfa36ea45c))
- 【web】配置 Validator 失败立即返回模式 ([1223f60](https://github.com/continew-org/continew-starter/commit/1223f6052d459087419b7373b8a2d7cfa36ea45c))
### 💥 破坏性变更
- 【data/mybatis-plus】重构 QueryHelper => QueryWrapperHelper支持多列查询并删除 blurry 属性 ([6dc20e8](https://github.com/Charles7c/continew-starter/commit/6dc20e8909073f771c33736262290fe14095b2e7)) ([f16b968](https://github.com/Charles7c/continew-starter/commit/f16b968b3f161c58144e59c67629b5787ba2d60d)) ([13a6809](https://github.com/Charles7c/continew-starter/commit/13a6809e2aa9744b3c5ca3558d5709af7cde4698))
- 【extension/crud】优化包结构 ([eabedd8](https://github.com/Charles7c/continew-starter/commit/eabedd861b533068d4fed31c412401fdba50aa63))
- 【captcha/graphic】优化图形验证码自动配置提供 Captcha Bean ([30d7631](https://github.com/Charles7c/continew-starter/commit/30d76314d66c392e36411229afeaed045f491d7a))
- 【data/mybatis-plus】重构 QueryHelper => QueryWrapperHelper支持多列查询并删除 blurry 属性 ([6dc20e8](https://github.com/continew-org/continew-starter/commit/6dc20e8909073f771c33736262290fe14095b2e7)) ([f16b968](https://github.com/continew-org/continew-starter/commit/f16b968b3f161c58144e59c67629b5787ba2d60d)) ([13a6809](https://github.com/continew-org/continew-starter/commit/13a6809e2aa9744b3c5ca3558d5709af7cde4698))
- 【extension/crud】优化包结构 ([eabedd8](https://github.com/continew-org/continew-starter/commit/eabedd861b533068d4fed31c412401fdba50aa63))
- 【captcha/graphic】优化图形验证码自动配置提供 Captcha Bean ([30d7631](https://github.com/continew-org/continew-starter/commit/30d76314d66c392e36411229afeaed045f491d7a))
## [v1.2.0](https://github.com/Charles7c/continew-starter/compare/v1.1.1...v1.2.0) (2024-01-20)
## [v1.2.0](https://github.com/continew-org/continew-starter/compare/v1.1.1...v1.2.0) (2024-01-20)
### ✨ 新特性
* 【extension/crud】新增 Easy Excel 枚举接口转换器 ([8936268](https://github.com/Charles7c/continew-starter/commit/8936268038b4f554d00f738a2311b560bda205d8))
* 【extension/crud】适配 Crane4j 数据填充组件 ([5d26f34](https://github.com/Charles7c/continew-starter/commit/5d26f343da7c467905fd08dfd06aaa2c50e8bcce))
* 【extension/crud】新增钩子方法用于增强增、删、改方法 ([43dba72](https://github.com/Charles7c/continew-starter/commit/43dba72cee9cb148a53ec2df23b0ac2854a0a42d))
* 【extension/crud】新增 IService 通用业务接口 ([926c92c](https://github.com/Charles7c/continew-starter/commit/926c92cc321e5da9279400741986f71173a3eda3))
* 【extension/crud】新增启用注解便于灵活控制启用/关闭 CRUD REST API、全局异常处理器增强 ([9398d68](https://github.com/Charles7c/continew-starter/commit/9398d686bbd3b87a2a82e273a5bda37d05ca6f30))
* 【cache/springcache】新增 Spring Cache 自动配置 ([e090083](https://github.com/Charles7c/continew-starter/commit/e090083ba26342aaf8378206949d6350f4f1444f))
* 【cache/jetcache】新增 JetCache 自动配置 ([156b02b](https://github.com/Charles7c/continew-starter/commit/156b02b3d77fa9f0476c23182d35df030a3ea66a))
* 【web】新增 Web 模块,从核心模块拆分 Web 相关自动配置 ([9cf76fe](https://github.com/Charles7c/continew-starter/commit/9cf76fe61f2368244a501c1c036c0a55502f5c0a))
* 【extension/crud】新增 Easy Excel 枚举接口转换器 ([8936268](https://github.com/continew-org/continew-starter/commit/8936268038b4f554d00f738a2311b560bda205d8))
* 【extension/crud】适配 Crane4j 数据填充组件 ([5d26f34](https://github.com/continew-org/continew-starter/commit/5d26f343da7c467905fd08dfd06aaa2c50e8bcce))
* 【extension/crud】新增钩子方法用于增强增、删、改方法 ([43dba72](https://github.com/continew-org/continew-starter/commit/43dba72cee9cb148a53ec2df23b0ac2854a0a42d))
* 【extension/crud】新增 IService 通用业务接口 ([926c92c](https://github.com/continew-org/continew-starter/commit/926c92cc321e5da9279400741986f71173a3eda3))
* 【extension/crud】新增启用注解便于灵活控制启用/关闭 CRUD REST API、全局异常处理器增强 ([9398d68](https://github.com/continew-org/continew-starter/commit/9398d686bbd3b87a2a82e273a5bda37d05ca6f30))
* 【cache/springcache】新增 Spring Cache 自动配置 ([e090083](https://github.com/continew-org/continew-starter/commit/e090083ba26342aaf8378206949d6350f4f1444f))
* 【cache/jetcache】新增 JetCache 自动配置 ([156b02b](https://github.com/continew-org/continew-starter/commit/156b02b3d77fa9f0476c23182d35df030a3ea66a))
* 【web】新增 Web 模块,从核心模块拆分 Web 相关自动配置 ([9cf76fe](https://github.com/continew-org/continew-starter/commit/9cf76fe61f2368244a501c1c036c0a55502f5c0a))
### 💎 功能优化
- 新增部分 Maven 插件版本锁定 ([be14bca](https://github.com/Charles7c/continew-starter/commit/be14bca2ca6ba5a808f7feebaafcf9356d338643))
- 移除部分无用 Maven 配置 ([6d9e8b4](https://github.com/Charles7c/continew-starter/commit/6d9e8b43ebe8d891ab459a2c2f21e06936abdc1d))
- 新增部分 Maven 插件版本锁定 ([be14bca](https://github.com/continew-org/continew-starter/commit/be14bca2ca6ba5a808f7feebaafcf9356d338643))
- 移除部分无用 Maven 配置 ([6d9e8b4](https://github.com/continew-org/continew-starter/commit/6d9e8b43ebe8d891ab459a2c2f21e06936abdc1d))
- 全局统一 Hutool 版本,精简各模块 Hutool 依赖 ([Gitee PR#6](https://gitee.com/continew/continew-starter/pulls/6))
- 调整部分类的所在包 ([b4b40b4](https://github.com/Charles7c/continew-starter/commit/b4b40b4cb929824e44bc7ad8737cbe73b283b34d))
- 调整部分类的所在包 ([b4b40b4](https://github.com/continew-org/continew-starter/commit/b4b40b4cb929824e44bc7ad8737cbe73b283b34d))
### 🐛 问题修复
- 【log/httptrace-pro】修复隐藏接口仍然被记录请求日志的问题 ([f3ad2c4](https://github.com/Charles7c/continew-starter/commit/f3ad2c48a9511ef611d414596539e838adef8e45))
- 【log/httptrace-pro】修复隐藏接口仍然被记录请求日志的问题 ([f3ad2c4](https://github.com/continew-org/continew-starter/commit/f3ad2c48a9511ef611d414596539e838adef8e45))
### 💥 破坏性变更
- 【extension/crud】移动全局异常处理器到 Web 模块 ([ec0ebd0](https://github.com/Charles7c/continew-starter/commit/ec0ebd00e49a2e67daa97d4a4f531f49acd5d89d))
- 【extension/crud】移动全局异常处理器到 Web 模块 ([ec0ebd0](https://github.com/continew-org/continew-starter/commit/ec0ebd00e49a2e67daa97d4a4f531f49acd5d89d))
## [v1.1.2](https://github.com/Charles7c/continew-starter/compare/v1.1.0...v1.1.2) (2024-01-11)
## [v1.1.2](https://github.com/continew-org/continew-starter/compare/v1.1.0...v1.1.2) (2024-01-11)
> 由于发布 `v1.1.1` 至 Maven 仓库时出现异常,且按其规则无法修改错误数据,改为递增版本号为 `v1.1.2` 并发布。
### ✨ 新特性
* 【extension/crud】BaseService 增加 list 查询列表方法重载 ([81ed292](https://github.com/Charles7c/continew-starter/commit/81ed29284090edcfc5ea5351442b5de2ce1622df))
* 【core】新增 SpringUtils 工具类 ([3de75cf](https://github.com/Charles7c/continew-starter/commit/3de75cf7fe79bc86ca5022d56e5f46be4d90d623))
* 【extension/crud】BaseService 增加 list 查询列表方法重载 ([81ed292](https://github.com/continew-org/continew-starter/commit/81ed29284090edcfc5ea5351442b5de2ce1622df))
* 【core】新增 SpringUtils 工具类 ([3de75cf](https://github.com/continew-org/continew-starter/commit/3de75cf7fe79bc86ca5022d56e5f46be4d90d623))
### 💎 功能优化
- 【log/httptrace-pro】优化日志过滤器仅在需要记录请求体、响应体时进行过滤 ([d68d88d](https://github.com/Charles7c/continew-starter/commit/d68d88db218d5008140c3056827dd6ac608a8b62))
- 【log/httptrace-pro】优化日志过滤器仅在需要记录请求体、响应体时进行过滤 ([d68d88d](https://github.com/continew-org/continew-starter/commit/d68d88db218d5008140c3056827dd6ac608a8b62))
- 【log/httptrace-pro】优化 @Log 注解信息获取优先级逻辑 ([Gitee PR#5](https://gitee.com/continew/continew-starter/pulls/5))
- 【extension/crud】优化 BaseServiceImpl 中获取各泛型参数类型的方式 ([6fc0b51](https://github.com/Charles7c/continew-starter/commit/6fc0b51a574434db9d21d1f254b3fce344c9f2f6))
- 【extension/crud】减少查询列表时可能的无用转换 ([0565372](https://github.com/Charles7c/continew-starter/commit/0565372e9aa8010a1c4625be4cf85d557a7eed7b))
- 使用常量优化部分配置属性名 ([2025068](https://github.com/Charles7c/continew-starter/commit/20250681da7682de159b6259e80193b204e55047))
- 优化日志级别 info => debug ([1e7d4b2](https://github.com/Charles7c/continew-starter/commit/1e7d4b2721fae3459cb6d1b57f208f0c38dbbc6f))
- 优化全局代码格式 ([57c21a9](https://github.com/Charles7c/continew-starter/commit/57c21a9109a412ed78c6c9b8aa0cd0f0b5724432))
- 【extension/crud】优化 BaseServiceImpl 中获取各泛型参数类型的方式 ([6fc0b51](https://github.com/continew-org/continew-starter/commit/6fc0b51a574434db9d21d1f254b3fce344c9f2f6))
- 【extension/crud】减少查询列表时可能的无用转换 ([0565372](https://github.com/continew-org/continew-starter/commit/0565372e9aa8010a1c4625be4cf85d557a7eed7b))
- 使用常量优化部分配置属性名 ([2025068](https://github.com/continew-org/continew-starter/commit/20250681da7682de159b6259e80193b204e55047))
- 优化日志级别 info => debug ([1e7d4b2](https://github.com/continew-org/continew-starter/commit/1e7d4b2721fae3459cb6d1b57f208f0c38dbbc6f))
- 优化全局代码格式 ([57c21a9](https://github.com/continew-org/continew-starter/commit/57c21a9109a412ed78c6c9b8aa0cd0f0b5724432))
### 💥 破坏性变更
- 【extension/crud】PageDataResp => PageResp ([38d2800](https://github.com/Charles7c/continew-starter/commit/38d28004d63a0218bfcae5689f9909ce6dcd824f))
- 【extension/crud】PageDataResp => PageResp ([38d2800](https://github.com/continew-org/continew-starter/commit/38d28004d63a0218bfcae5689f9909ce6dcd824f))
## [v1.1.0](https://github.com/Charles7c/continew-starter/compare/v1.0.1...v1.1.0) (2023-12-31)
## [v1.1.0](https://github.com/continew-org/continew-starter/compare/v1.0.1...v1.1.0) (2023-12-31)
### ✨ 新特性
* 【log/httptrace-pro】新增 continew-starter-log-httptrace-pro 日志模块Spring Boot Actuator HttpTrace 重置增强版)
* 【storage/local】新增 continew-starter-storage-local 本地存储模块 ([cd6826a](https://github.com/Charles7c/continew-starter/commit/cd6826a0abe0666f9fe867e92bf70abb47e5ff2e))
* 【cache/redisson】RedisUtils 新增限流方法 ([9cf3ae8](https://github.com/Charles7c/continew-starter/commit/9cf3ae87a1a20db9ee8b2b7272e8328b5fc5c20c))
* 【data/mybatis-plus】新增数据权限默认解决方案 ([621a5e3](https://github.com/Charles7c/continew-starter/commit/621a5e3b22db9b81d31c65b39ad387a8531e09af))
* 【storage/local】新增 continew-starter-storage-local 本地存储模块 ([cd6826a](https://github.com/continew-org/continew-starter/commit/cd6826a0abe0666f9fe867e92bf70abb47e5ff2e))
* 【cache/redisson】RedisUtils 新增限流方法 ([9cf3ae8](https://github.com/continew-org/continew-starter/commit/9cf3ae87a1a20db9ee8b2b7272e8328b5fc5c20c))
* 【data/mybatis-plus】新增数据权限默认解决方案 ([621a5e3](https://github.com/continew-org/continew-starter/commit/621a5e3b22db9b81d31c65b39ad387a8531e09af))
* 【captcha/behavior】新增 continew-starter-captcha-behavior 行为验证码模块 ([Gitee PR#1](https://gitee.com/continew/continew-starter/pulls/1))
* 【core】新增 PATH_PATTERN 字符串常量 ([76e282c](https://github.com/Charles7c/continew-starter/commit/76e282c7965fdfa39854fe77397687bbc40d0f7f))
* 【core】新增 PATH_PATTERN 字符串常量 ([76e282c](https://github.com/continew-org/continew-starter/commit/76e282c7965fdfa39854fe77397687bbc40d0f7f))
### 💎 功能优化
- 【core】优化跨域配置默认值 ([65f5fbd](https://github.com/Charles7c/continew-starter/commit/65f5fbd6daa9ae2c8aedd13c487e8985523233ce))
- 【extension/crud】新增全局异常处理器 ([c4459d1](https://github.com/Charles7c/continew-starter/commit/c4459d1b8d701a4405f74ea92cfc87752a285b55))
- 【extension/crud】移除部分方法中仅有单个非读操作的事务处理 ([70ae383](https://github.com/Charles7c/continew-starter/commit/70ae383de62bc3c6ae0d2e1c3cf5c005d54f83f5))
- 【core】优化跨域配置默认值 ([65f5fbd](https://github.com/continew-org/continew-starter/commit/65f5fbd6daa9ae2c8aedd13c487e8985523233ce))
- 【extension/crud】新增全局异常处理器 ([c4459d1](https://github.com/continew-org/continew-starter/commit/c4459d1b8d701a4405f74ea92cfc87752a285b55))
- 【extension/crud】移除部分方法中仅有单个非读操作的事务处理 ([70ae383](https://github.com/continew-org/continew-starter/commit/70ae383de62bc3c6ae0d2e1c3cf5c005d54f83f5))
### 📦 依赖升级
- 【dependencies】Spring Boot 3.1.5 => 3.1.7 ([72f5569](https://github.com/Charles7c/continew-starter/commit/72f55697cc8958bf3586daed03a8d1b3c8636605))
- 【dependencies】Just Auth 1.16.5 => 1.16.6 ([72f5569](https://github.com/Charles7c/continew-starter/commit/72f55697cc8958bf3586daed03a8d1b3c8636605))
- 【dependencies】Redisson 3.24.3 => 3.25.2 ([72f5569](https://github.com/Charles7c/continew-starter/commit/72f55697cc8958bf3586daed03a8d1b3c8636605))
- 【dependencies】Easy Excel 3.3.2 => 3.3.3 ([72f5569](https://github.com/Charles7c/continew-starter/commit/72f55697cc8958bf3586daed03a8d1b3c8636605))
- 【dependencies】Knife4j 4.3.0 => 4.4.0 ([72f5569](https://github.com/Charles7c/continew-starter/commit/72f55697cc8958bf3586daed03a8d1b3c8636605))
- 【dependencies】Hutool 5.8.23 => 5.8.24 ([72f5569](https://github.com/Charles7c/continew-starter/commit/72f55697cc8958bf3586daed03a8d1b3c8636605))
- 【dependencies】MyBatis Plus 3.5.4.1 => 3.5.5(修复与 Spring Boot 3.1.7 的 DdlApplicationRunner冲突错误 ([556bfb9](https://github.com/Charles7c/continew-starter/commit/556bfb924a1e5834fe0a101b9ff52cc5bb36d578))
- 【dependencies】新增 X File Storage 依赖版本 2.0.0 ([be7972c](https://github.com/Charles7c/continew-starter/commit/be7972c00be8d62cc25332e053a985532016de2d))
- 【dependencies】ip2region 3.1.5.1 => 3.1.6 ([4dae89e](https://github.com/Charles7c/continew-starter/commit/4dae89e0f21ac6c532101e983ee4007f3980c929))
- 【dependencies】新增 Amazon S3 依赖版本 1.12.626 ([48f894b](https://github.com/Charles7c/continew-starter/commit/48f894b8b62f8b968091dcea51b57336b97e4a2d))
- 【dependencies】Spring Boot 3.1.5 => 3.1.7 ([72f5569](https://github.com/continew-org/continew-starter/commit/72f55697cc8958bf3586daed03a8d1b3c8636605))
- 【dependencies】Just Auth 1.16.5 => 1.16.6 ([72f5569](https://github.com/continew-org/continew-starter/commit/72f55697cc8958bf3586daed03a8d1b3c8636605))
- 【dependencies】Redisson 3.24.3 => 3.25.2 ([72f5569](https://github.com/continew-org/continew-starter/commit/72f55697cc8958bf3586daed03a8d1b3c8636605))
- 【dependencies】Easy Excel 3.3.2 => 3.3.3 ([72f5569](https://github.com/continew-org/continew-starter/commit/72f55697cc8958bf3586daed03a8d1b3c8636605))
- 【dependencies】Knife4j 4.3.0 => 4.4.0 ([72f5569](https://github.com/continew-org/continew-starter/commit/72f55697cc8958bf3586daed03a8d1b3c8636605))
- 【dependencies】Hutool 5.8.23 => 5.8.24 ([72f5569](https://github.com/continew-org/continew-starter/commit/72f55697cc8958bf3586daed03a8d1b3c8636605))
- 【dependencies】MyBatis Plus 3.5.4.1 => 3.5.5(修复与 Spring Boot 3.1.7 的 DdlApplicationRunner冲突错误 ([556bfb9](https://github.com/continew-org/continew-starter/commit/556bfb924a1e5834fe0a101b9ff52cc5bb36d578))
- 【dependencies】新增 X File Storage 依赖版本 2.0.0 ([be7972c](https://github.com/continew-org/continew-starter/commit/be7972c00be8d62cc25332e053a985532016de2d))
- 【dependencies】ip2region 3.1.5.1 => 3.1.6 ([4dae89e](https://github.com/continew-org/continew-starter/commit/4dae89e0f21ac6c532101e983ee4007f3980c929))
- 【dependencies】新增 Amazon S3 依赖版本 1.12.626 ([48f894b](https://github.com/continew-org/continew-starter/commit/48f894b8b62f8b968091dcea51b57336b97e4a2d))
### 💥 破坏性变更
- 【captcha/graphic】优化图形验证码配置前缀 ([e0e5944](https://github.com/Charles7c/continew-starter/commit/e0e5944b45bcbf8a4b7a5066ad347459a7b3e450))
- 【data/mybatis-plus】调整 IBaseEnum 所属包 enums => base ([22fee2f](https://github.com/Charles7c/continew-starter/commit/22fee2f5bd8211e26c2f6a163a6298f5b522833c))
- 【auth/satoken】SaTokenDaoTypeEnum => SaTokenDaoType ([0a0d022](https://github.com/Charles7c/continew-starter/commit/0a0d022586dc88a773512c5761c68d62786e35c4))
- 【core】使用常量优化部分魔法值核心模块部分配置前缀调整 ([52dce2a](https://github.com/Charles7c/continew-starter/commit/52dce2acdfa0296c3f6f4875f14a0299f999f899))
- 【captcha/graphic】优化图形验证码配置前缀 ([e0e5944](https://github.com/continew-org/continew-starter/commit/e0e5944b45bcbf8a4b7a5066ad347459a7b3e450))
- 【data/mybatis-plus】调整 IBaseEnum 所属包 enums => base ([22fee2f](https://github.com/continew-org/continew-starter/commit/22fee2f5bd8211e26c2f6a163a6298f5b522833c))
- 【auth/satoken】SaTokenDaoTypeEnum => SaTokenDaoType ([0a0d022](https://github.com/continew-org/continew-starter/commit/0a0d022586dc88a773512c5761c68d62786e35c4))
- 【core】使用常量优化部分魔法值核心模块部分配置前缀调整 ([52dce2a](https://github.com/continew-org/continew-starter/commit/52dce2acdfa0296c3f6f4875f14a0299f999f899))
## [v1.0.1](https://github.com/Charles7c/continew-starter/compare/v1.0.0...v1.0.1) (2023-12-13)
## [v1.0.1](https://github.com/continew-org/continew-starter/compare/v1.0.0...v1.0.1) (2023-12-13)
### 💎 功能优化
- 【data/mybatis-plus】QueryTypeEnum => QueryType并取消实现 IBaseEnum 接口 ([bc00c9b](https://github.com/Charles7c/continew-starter/commit/bc00c9bab0ed4508fd1dc0da8a76ef96739cce1d))
- 【api-doc】新增鉴权配置 ([7997267](https://github.com/Charles7c/continew-starter/commit/7997267060b3e79f80dd73cec722bc295635a93b))
- 【data/mybatis-plus】QueryTypeEnum => QueryType并取消实现 IBaseEnum 接口 ([bc00c9b](https://github.com/continew-org/continew-starter/commit/bc00c9bab0ed4508fd1dc0da8a76ef96739cce1d))
- 【api-doc】新增鉴权配置 ([7997267](https://github.com/continew-org/continew-starter/commit/7997267060b3e79f80dd73cec722bc295635a93b))
### 🐛 问题修复
- 【extension/crud】修复使用 @CrudRequestMapping 后自定义 API 不显示的问题 ([1adfddf](https://github.com/Charles7c/continew-starter/commit/1adfddfa3b276e764b098512b2e9c75f007d13c1))
- 【extension/crud】修复使用 @CrudRequestMapping 后自定义 API 不显示的问题 ([1adfddf](https://github.com/continew-org/continew-starter/commit/1adfddfa3b276e764b098512b2e9c75f007d13c1))
### 💥 破坏性变更
- 【extension/crud】调整通用查询注解所属模块 crud => mybatis-plus ([083bc7b](https://github.com/Charles7c/continew-starter/commit/083bc7b38a861339ceb7a06acdd20ea64bc84990))
- 【extension/crud】调整校验工具类所属模块 crud => core ([083bc7b](https://github.com/Charles7c/continew-starter/commit/083bc7b38a861339ceb7a06acdd20ea64bc84990))
- 【extension/crud】调整通用查询注解所属模块 crud => mybatis-plus ([083bc7b](https://github.com/continew-org/continew-starter/commit/083bc7b38a861339ceb7a06acdd20ea64bc84990))
- 【extension/crud】调整校验工具类所属模块 crud => core ([083bc7b](https://github.com/continew-org/continew-starter/commit/083bc7b38a861339ceb7a06acdd20ea64bc84990))
## v1.0.0 (2023-12-02)

View File

@@ -1,15 +1,12 @@
# ContiNew Starter
<a href="https://github.com/Charles7c/continew-starter/blob/dev/LICENSE" target="_blank">
<a href="https://github.com/continew-org/continew-starter/blob/dev/LICENSE" target="_blank">
<img src="https://img.shields.io/badge/License-LGPL--3.0-blue.svg" alt="License" />
</a>
<a href="https://central.sonatype.com/search?q=continew-starter" target="_blank">
<img src="https://img.shields.io/maven-central/v/top.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-v2.1.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">
<a href="https://app.codacy.com/gh/continew-org/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" />
</a>
<a href="https://sonarcloud.io/summary/new_code?id=Charles7c_continew-starter" target="_blank">
@@ -18,14 +15,14 @@
<a href="https://spring.io/projects/spring-boot" target="_blank">
<img src="https://img.shields.io/badge/Spring Boot-3.1.11-%236CB52D.svg?logo=Spring-Boot" alt="Spring Boot" />
</a>
<a href="https://github.com/Charles7c/continew-starter" target="_blank">
<a href="https://github.com/continew-org/continew-starter" target="_blank">
<img src="https://img.shields.io/badge/Open JDK-17-%236CB52D.svg?logo=OpenJDK&logoColor=FFF" alt="Open JDK" />
</a>
<a href="https://github.com/Charles7c/continew-starter" target="_blank">
<img src="https://img.shields.io/github/stars/Charles7c/continew-starter?style=social" alt="GitHub stars" />
<a href="https://github.com/continew-org/continew-starter" target="_blank">
<img src="https://img.shields.io/github/stars/continew-org/continew-starter?style=social" alt="GitHub stars" />
</a>
<a href="https://github.com/Charles7c/continew-starter" target="_blank">
<img src="https://img.shields.io/github/forks/Charles7c/continew-starter?style=social" alt="GitHub forks" />
<a href="https://github.com/continew-org/continew-starter" target="_blank">
<img src="https://img.shields.io/github/forks/continew-org/continew-starter?style=social" alt="GitHub forks" />
</a>
<a href="https://gitee.com/continew/continew-starter" target="_blank">
<img src="https://gitee.com/continew/continew-starter/badge/star.svg?theme=white" alt="Gitee stars" />
@@ -65,7 +62,7 @@ ContiNew Starter 就是将脚手架项目中的通用基础配置进行了封装
| 开源平台 | 源码地址 |
| :------------ | :-------------------------------------------- |
| GitHub | https://github.com/Charles7c/continew-starter |
| GitHub | https://github.com/continew-org/continew-starter |
| Gitee码云 | https://gitee.com/continew/continew-starter |
## 像数123一样容易
@@ -160,10 +157,11 @@ continew-starter.web:
### 安全模块
| 模块名称 | 模块说明 | 依赖版本 |
| ---------------------------------- | ----------------- | -------- |
|------------------------------------|-----------| -------- |
| continew-starter-security-password | 密码编码器 | |
| continew-starter-security-mask | JSON 脱敏 | |
| continew-starter-security-crypto | 数据库字段加/解密 | |
| continew-starter-security-limiter | 限流器 | |
### Web模块
@@ -252,7 +250,7 @@ ContiNew Starter 的分支目前分为下个大版本的开发分支和上个大
2. 然后,将 fork 过来的项目(即您的项目)克隆到本地
3. 切换到当前仍在维护的分支(请务必充分了解分支使用说明,可进群联系维护者确认)
4. 开始修改代码,修改完成后,将代码 commit 并 push 到您的远程仓库
5. 在 Gitee 或 Github 上新建 pull requestpr选择好源和目标按模板要求填写说明信息后提交即可多多参考 [已批准合并的 pr 记录](https://github.com/Charles7c/continew-starter/pulls?q=is%3Apr+is%3Amerged),会大大增加批准合并率)
5. 在 Gitee 或 Github 上新建 pull requestpr选择好源和目标按模板要求填写说明信息后提交即可多多参考 [已批准合并的 pr 记录](https://github.com/continew-org/continew-starter/pulls?q=is%3Apr+is%3Amerged),会大大增加批准合并率)
6. 最后,耐心等待维护者合并您的请求即可
请记住,如果您有任何疑问或需要帮助,我们将随时提供支持。
@@ -260,7 +258,7 @@ ContiNew Starter 的分支目前分为下个大版本的开发分支和上个大
> [!IMPORTANT]
> 欢迎大家为 ContiNew Starter 贡献代码,我们非常感谢您的支持!为了更好地管理项目,维护者有一些要求:
>
> 1. 请确保代码、配置文件的结构和命名规范良好,完善的代码注释,并遵循阿里巴巴的 <a href="https://github.com/Charles7c/continew-starter/blob/dev/.style/Java%E5%BC%80%E5%8F%91%E6%89%8B%E5%86%8C(%E9%BB%84%E5%B1%B1%E7%89%88).pdf" target="_blank">《Java开发手册(黄山版)》</a> 中的代码规范,保证代码质量和可维护性
> 1. 请确保代码、配置文件的结构和命名规范良好,完善的代码注释,并遵循阿里巴巴的 <a href="https://github.com/continew-org/continew-starter/blob/dev/.style/Java%E5%BC%80%E5%8F%91%E6%89%8B%E5%86%8C(%E9%BB%84%E5%B1%B1%E7%89%88).pdf" target="_blank">《Java开发手册(黄山版)》</a> 中的代码规范,保证代码质量和可维护性
> 2. 在提交代码前,请按照 [Angular 提交规范](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular) 编写 commit 的 message建议在 IntelliJ IDEA 中下载并安装 Git Commit Template 插件,以便按照规范进行 commit
> 3. 提交代码之前,请关闭所有代码窗口,执行 `mvn compile` 命令(代码格式化插件会在项目编译时对全局代码进行格式修正),编译通过后,不要再打开查看任何代码窗口,直接提交即可
@@ -276,11 +274,11 @@ ContiNew Starter 的分支目前分为下个大版本的开发分支和上个大
- 和众多大佬互相 (huá shuǐ) 交流 (mō yú)
<div align="left">
<img src="https://doc.charles7c.top/qrcode.jpg" alt="二维码" width="230px" />
<img src="https://continew.top/qrcode.jpg" alt="二维码" width="230px" />
</div>
<details>
<summary>无加群意愿</summary>
💬 如无加群意愿,欢迎在 <a href="https://github.com/Charles7c/continew-starter/issues" target="_blank">Issues</a> 中反馈交流~ 🍻
💬 如无加群意愿,欢迎在 <a href="https://github.com/continew-org/continew-starter/issues" target="_blank">Issues</a> 中反馈交流~ 🍻
</details>
## 鸣谢
@@ -289,8 +287,8 @@ ContiNew Starter 的分支目前分为下个大版本的开发分支和上个大
感谢参与贡献的每一位小伙伴🥰
<a href="https://github.com/Charles7c/continew-starter/graphs/contributors">
<img src="https://contrib.rocks/image?repo=Charles7c/continew-starter" />
<a href="https://github.com/continew-org/continew-starter/graphs/contributors">
<img src="https://contrib.rocks/image?repo=continew-org/continew-starter" />
</a>
### 特别鸣谢
@@ -304,7 +302,7 @@ ContiNew Starter 的分支目前分为下个大版本的开发分支和上个大
## License
- 遵循 <a href="https://github.com/Charles7c/continew-starter/blob/dev/LICENSE" target="_blank">LGPL-3.0</a> 开源许可协议
- 遵循 <a href="https://github.com/continew-org/continew-starter/blob/dev/LICENSE" target="_blank">LGPL-3.0</a> 开源许可协议
- Copyright © 2022-present <a href="https://blog.charles7c.top" target="_blank">Charles7c</a>
## GitHub Star 趋势

View File

@@ -30,7 +30,6 @@ import org.slf4j.LoggerFactory;
import org.springdoc.core.customizers.GlobalOpenApiCustomizer;
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 org.springframework.context.annotation.PropertySource;
@@ -39,7 +38,6 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import top.continew.starter.core.autoconfigure.project.ProjectProperties;
import top.continew.starter.core.constant.PropertiesConstants;
import top.continew.starter.core.util.GeneralPropertySourceFactory;
import java.util.List;
@@ -54,7 +52,6 @@ import java.util.concurrent.TimeUnit;
*/
@EnableWebMvc
@AutoConfiguration
@ConditionalOnProperty(prefix = PropertiesConstants.SPRINGDOC_SWAGGER_UI, name = PropertiesConstants.ENABLED, matchIfMissing = true)
@EnableConfigurationProperties(SpringDocExtensionProperties.class)
@PropertySource(value = "classpath:default-api-doc.yml", factory = GeneralPropertySourceFactory.class)
public class SpringDocAutoConfiguration implements WebMvcConfigurer {

View File

@@ -19,7 +19,6 @@ package top.continew.starter.apidoc.autoconfigure;
import io.swagger.v3.oas.models.Components;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import top.continew.starter.core.constant.PropertiesConstants;
/**
* API 文档扩展配置属性
@@ -27,7 +26,7 @@ import top.continew.starter.core.constant.PropertiesConstants;
* @author Charles7c
* @since 1.0.1
*/
@ConfigurationProperties(prefix = PropertiesConstants.SPRINGDOC)
@ConfigurationProperties("springdoc")
public class SpringDocExtensionProperties {
/**

View File

@@ -1,6 +1,7 @@
--- ### 接口文档配置
springdoc:
swagger-ui:
enabled: true
path: /swagger-ui.html
tags-sorter: alpha
operations-sorter: alpha

View File

@@ -26,7 +26,7 @@ import top.continew.starter.auth.satoken.autoconfigure.dao.SaTokenDaoProperties;
* @author Charles7c
* @since 1.0.0
*/
@ConfigurationProperties(prefix = "sa-token.extension")
@ConfigurationProperties("sa-token.extension")
public class SaTokenExtensionProperties {
/**

View File

@@ -28,7 +28,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Charles7c
* @since 1.0.0
*/
@ConfigurationProperties(prefix = "spring.data.redisson")
@ConfigurationProperties("spring.data.redisson")
public class RedissonProperties {
/**

View File

@@ -16,12 +16,14 @@
package top.continew.starter.cache.redisson.util;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.extra.spring.SpringUtil;
import org.redisson.api.*;
import top.continew.starter.core.constant.StringConstants;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
/**
* Redis 工具类
@@ -54,11 +56,7 @@ public class RedisUtils {
* @param duration 过期时间
*/
public static <T> void set(String key, T value, Duration duration) {
RBatch batch = CLIENT.createBatch();
RBucketAsync<T> bucket = batch.getBucket(key);
bucket.setAsync(value);
bucket.expireAsync(duration);
batch.execute();
CLIENT.getBucket(key).set(value, duration);
}
/**
@@ -72,6 +70,46 @@ public class RedisUtils {
return bucket.get();
}
/**
* 设置缓存List 集合)
*
* @param key 键
* @param value 值
* @since 2.1.1
*/
public static <T> void setList(String key, List<T> value) {
RList<T> list = CLIENT.getList(key);
list.addAll(value);
}
/**
* 设置缓存List 集合)
*
* @param key 键
* @param value 值
* @param duration 过期时间
* @since 2.1.1
*/
public static <T> void setList(String key, List<T> value, Duration duration) {
RBatch batch = CLIENT.createBatch();
RListAsync<T> list = batch.getList(key);
list.addAllAsync(value);
list.expireAsync(duration);
batch.execute();
}
/**
* 查询指定缓存List 集合)
*
* @param key 键
* @return 值
* @since 2.1.1
*/
public static <T> List<T> getList(String key) {
RList<T> list = CLIENT.getList(key);
return list.readAll();
}
/**
* 删除缓存
*
@@ -113,17 +151,6 @@ public class RedisUtils {
return CLIENT.getAtomicLong(key).decrementAndGet();
}
/**
* 设置缓存过期时间
*
* @param key 键
* @param timeout 过期时间(单位:秒)
* @return true设置成功false设置失败
*/
public static boolean expire(String key, long timeout) {
return expire(key, Duration.ofSeconds(timeout));
}
/**
* 设置缓存过期时间
*
@@ -187,6 +214,6 @@ public class RedisUtils {
* @return 键
*/
public static String formatKey(String... subKeys) {
return String.join(StringConstants.COLON, subKeys);
return String.join(StringConstants.COLON, ArrayUtil.removeBlank(subKeys));
}
}

View File

@@ -49,7 +49,7 @@ import java.util.Properties;
*/
@AutoConfiguration
@EnableConfigurationProperties(BehaviorCaptchaProperties.class)
@ConditionalOnProperty(prefix = PropertiesConstants.CAPTCHA_BEHAVIOR, name = PropertiesConstants.ENABLED, havingValue = "true")
@ConditionalOnProperty(prefix = PropertiesConstants.CAPTCHA_BEHAVIOR, name = PropertiesConstants.ENABLED, matchIfMissing = true)
public class BehaviorCaptchaAutoConfiguration {
private static final Logger log = LoggerFactory.getLogger(BehaviorCaptchaAutoConfiguration.class);

View File

@@ -36,7 +36,7 @@ public class BehaviorCaptchaProperties {
/**
* 是否启用行为验证码
*/
private boolean enabled = false;
private boolean enabled = true;
/**
* 是否开启 AES 坐标加密默认true

View File

@@ -23,6 +23,13 @@
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<!-- Hibernate Validator -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<optional>true</optional>
</dependency>
<!-- 第三方封装 Ip2region离线 IP 数据管理框架和定位库支持亿级别的数据段10 微秒级别的查询性能,提供了许多主流编程语言的 xdb 数据管理引擎的实现) -->
<dependency>
<groupId>net.dreamlu</groupId>

View File

@@ -24,7 +24,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Charles7c
* @since 1.0.0
*/
@ConfigurationProperties(prefix = "project")
@ConfigurationProperties("project")
public class ProjectProperties {
/**

View File

@@ -17,6 +17,7 @@
package top.continew.starter.core.autoconfigure.threadpool;
import cn.hutool.core.util.ArrayUtil;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
@@ -25,40 +26,39 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import top.continew.starter.core.constant.PropertiesConstants;
import top.continew.starter.core.exception.BaseException;
import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
/**
* 异步任务自动配置
*
* @author Charles7c
* @author Lion Li<a href="https://gitee.com/dromara/RuoYi-Vue-Plus">RuoYi-Vue-Plus</a>
* @since 1.0.0
*/
@Lazy
@AutoConfiguration
@EnableAsync(proxyTargetClass = true)
@ConditionalOnProperty(prefix = PropertiesConstants.THREAD_POOL, name = PropertiesConstants.ENABLED, havingValue = "true")
@ConditionalOnProperty(prefix = "spring.task.execution.extension", name = PropertiesConstants.ENABLED, matchIfMissing = true)
public class AsyncAutoConfiguration implements AsyncConfigurer {
private static final Logger log = LoggerFactory.getLogger(AsyncAutoConfiguration.class);
private final ScheduledExecutorService scheduledExecutorService;
private final ThreadPoolTaskExecutor threadPoolTaskExecutor;
public AsyncAutoConfiguration(ScheduledExecutorService scheduledExecutorService) {
this.scheduledExecutorService = scheduledExecutorService;
public AsyncAutoConfiguration(ThreadPoolTaskExecutor threadPoolTaskExecutor) {
this.threadPoolTaskExecutor = threadPoolTaskExecutor;
}
/**
* 异步任务 @Async 执行时,使用 Java 内置线程池
* 异步任务线程池配置
*/
@Override
public Executor getAsyncExecutor() {
log.debug("[ContiNew Starter] - Auto Configuration 'AsyncConfigurer' completed initialization.");
return scheduledExecutorService;
return threadPoolTaskExecutor;
}
/**
@@ -79,4 +79,9 @@ public class AsyncAutoConfiguration implements AsyncConfigurer {
throw new BaseException(sb.toString());
};
}
@PostConstruct
public void postConstruct() {
log.debug("[ContiNew Starter] - Auto Configuration 'AsyncConfigurer' completed initialization.");
}
}

View File

@@ -16,143 +16,71 @@
package top.continew.starter.core.autoconfigure.threadpool;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.beans.factory.annotation.Value;
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.boot.task.TaskExecutorCustomizer;
import org.springframework.boot.task.TaskSchedulerCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.annotation.EnableScheduling;
import top.continew.starter.core.constant.PropertiesConstants;
import top.continew.starter.core.util.ExceptionUtils;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 线程池自动配置
*
* @author Charles7c
* @author Lion Li<a href="https://gitee.com/dromara/RuoYi-Vue-Plus">RuoYi-Vue-Plus</a>
* @since 1.0.0
*/
@Lazy
@AutoConfiguration
@ConditionalOnProperty(prefix = PropertiesConstants.THREAD_POOL, name = PropertiesConstants.ENABLED, havingValue = "true")
@EnableConfigurationProperties(ThreadPoolProperties.class)
@EnableConfigurationProperties(ThreadPoolExtensionProperties.class)
public class ThreadPoolAutoConfiguration {
private static final Logger log = LoggerFactory.getLogger(ThreadPoolAutoConfiguration.class);
/**
* 核心(最小)线程数 = CPU 核心数 + 1
*/
private final int corePoolSize = Runtime.getRuntime().availableProcessors() + 1;
@Value("${spring.task.execution.pool.core-size:#{T(java.lang.Runtime).getRuntime().availableProcessors() + 1}}")
private int corePoolSize;
@Value("${spring.task.execution.pool.max-size:#{T(java.lang.Runtime).getRuntime().availableProcessors() * 2}}")
private int maxPoolSize;
/**
* Spring 内置线程池ThreadPoolTaskExecutor
* 异步任务线程池配置
*/
@Bean
public ThreadPoolTaskExecutor threadPoolTaskExecutor(ThreadPoolProperties properties) {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
@ConditionalOnProperty(prefix = "spring.task.execution.extension", name = PropertiesConstants.ENABLED, matchIfMissing = true)
public TaskExecutorCustomizer taskExecutorCustomizer(ThreadPoolExtensionProperties properties) {
return executor -> {
// 核心(最小)线程数
executor.setCorePoolSize(ObjectUtil.defaultIfNull(properties.getCorePoolSize(), corePoolSize));
executor.setCorePoolSize(corePoolSize);
// 最大线程数
executor.setMaxPoolSize(ObjectUtil.defaultIfNull(properties.getMaxPoolSize(), corePoolSize * 2));
// 队列容量
executor.setQueueCapacity(properties.getQueueCapacity());
// 活跃时间
executor.setKeepAliveSeconds(properties.getKeepAliveSeconds());
// 配置当池内线程数已达到上限的时候,该如何处理新任务:不在新线程中执行任务,而是由调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 关闭线程池是否等待任务完成
executor.setWaitForTasksToCompleteOnShutdown(properties.isWaitForTasksToCompleteOnShutdown());
// 执行器在关闭时阻塞的最长毫秒数,以等待剩余任务完成执行
executor.setAwaitTerminationMillis(properties.getAwaitTerminationMillis());
log.debug("[ContiNew Starter] - Auto Configuration 'ThreadPoolTaskExecutor' completed initialization.");
return executor;
}
/**
* Java 内置线程池ScheduledExecutorService适用于执行周期性或定时任务
*/
@Bean
@ConditionalOnMissingBean
public ScheduledExecutorService scheduledExecutorService(ThreadPoolProperties properties) {
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(ObjectUtil.defaultIfNull(properties
.getCorePoolSize(), corePoolSize), ThreadUtil
.newNamedThreadFactory("schedule-pool-%d", true), new ThreadPoolExecutor.CallerRunsPolicy()) {
@Override
protected void afterExecute(Runnable runnable, Throwable throwable) {
super.afterExecute(runnable, throwable);
ExceptionUtils.printException(runnable, throwable);
}
executor.setMaxPoolSize(maxPoolSize);
// 当线程池的任务缓存队列已满并且线程池中的线程数已达到 maxPoolSize 时采取的任务拒绝策略
executor.setRejectedExecutionHandler(properties.getExecution()
.getRejectedPolicy()
.getRejectedExecutionHandler());
log.debug("[ContiNew Starter] - Auto Configuration 'TaskExecutor' completed initialization.");
};
// 应用关闭时,关闭线程池
SpringApplication.getShutdownHandlers().add(() -> shutdown(executor, properties));
log.debug("[ContiNew Starter] - Auto Configuration 'ScheduledExecutorService' completed initialization.");
return executor;
}
/**
* 根据相应的配置设置关闭 ExecutorService
*
* @see org.springframework.scheduling.concurrent.ExecutorConfigurationSupport#shutdown()
* 定时任务线程池配置
*/
public void shutdown(ExecutorService executor, ThreadPoolProperties properties) {
log.debug("[ContiNew Starter] - Shutting down ScheduledExecutorService start.");
if (executor != null) {
if (properties.isWaitForTasksToCompleteOnShutdown()) {
executor.shutdown();
} else {
for (Runnable remainingTask : executor.shutdownNow()) {
cancelRemainingTask(remainingTask);
}
}
awaitTerminationIfNecessary(executor, properties);
log.debug("[ContiNew Starter] - Shutting down ScheduledExecutorService complete.");
}
}
/**
* Cancel the given remaining task which never commenced execution,
* as returned from {@link ExecutorService#shutdownNow()}.
*
* @param task the task to cancel (typically a {@link RunnableFuture})
* @see RunnableFuture#cancel(boolean)
*/
protected void cancelRemainingTask(Runnable task) {
if (task instanceof Future<?> future) {
future.cancel(true);
}
}
/**
* Wait for the executor to terminate, according to the value of the properties
*/
private void awaitTerminationIfNecessary(ExecutorService executor, ThreadPoolProperties properties) {
if (properties.getAwaitTerminationMillis() > 0) {
try {
if (!executor.awaitTermination(properties.getAwaitTerminationMillis(), TimeUnit.MILLISECONDS)) {
if (log.isWarnEnabled()) {
log.warn("[ContiNew Starter] - Timed out while waiting for executor 'ScheduledExecutorService' to terminate.");
}
}
} catch (InterruptedException ex) {
if (log.isWarnEnabled()) {
log.warn("[ContiNew Starter] - Interrupted while waiting for executor 'ScheduledExecutorService' to terminate");
}
Thread.currentThread().interrupt();
}
@EnableScheduling
@ConditionalOnProperty(prefix = "spring.task.scheduling.extension", name = PropertiesConstants.ENABLED, matchIfMissing = true)
public static class TaskSchedulerConfiguration {
@Bean
public TaskSchedulerCustomizer taskSchedulerCustomizer(ThreadPoolExtensionProperties properties) {
return executor -> {
executor.setRejectedExecutionHandler(properties.getScheduling()
.getRejectedPolicy()
.getRejectedExecutionHandler());
log.debug("[ContiNew Starter] - Auto Configuration 'TaskScheduler' completed initialization.");
};
}
}
}

View File

@@ -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.continew.starter.core.autoconfigure.threadpool;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 线程池拒绝策略
*
* @author Charles7c
* @since 2.2.0
*/
public enum ThreadPoolExecutorRejectedPolicy {
/**
* ThreadPoolTaskExecutor 默认的拒绝策略,不执行新任务,直接抛出 RejectedExecutionException 异常
*/
ABORT {
@Override
public RejectedExecutionHandler getRejectedExecutionHandler() {
return new ThreadPoolExecutor.AbortPolicy();
}
},
/**
* 提交的任务在执行被拒绝时,会由提交任务的线程去执行
*/
CALLER_RUNS {
@Override
public RejectedExecutionHandler getRejectedExecutionHandler() {
return new ThreadPoolExecutor.CallerRunsPolicy();
}
},
/**
* 不执行新任务,也不抛出异常
*/
DISCARD {
@Override
public RejectedExecutionHandler getRejectedExecutionHandler() {
return new ThreadPoolExecutor.DiscardPolicy();
}
},
/**
* 拒绝新任务,但是会抛弃队列中最老的任务,然后尝试再次提交新任务
*/
DISCARD_OLDEST {
@Override
public RejectedExecutionHandler getRejectedExecutionHandler() {
return new ThreadPoolExecutor.DiscardOldestPolicy();
}
};
/**
* 获取拒绝处理器
*
* @return 拒绝处理器
*/
public abstract RejectedExecutionHandler getRejectedExecutionHandler();
}

View File

@@ -0,0 +1,91 @@
/*
* 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.continew.starter.core.autoconfigure.threadpool;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 线程池扩展配置属性
*
* @author Charles7c
* @since 1.0.0
*/
@ConfigurationProperties("spring.task")
public class ThreadPoolExtensionProperties {
/**
* 异步任务扩展配置属性
*/
private ExecutorExtensionProperties execution = new ExecutorExtensionProperties();
/**
* 调度任务扩展配置属性
*/
private SchedulerExtensionProperties scheduling = new SchedulerExtensionProperties();
/**
* 异步任务扩展配置属性
*/
public static class ExecutorExtensionProperties {
/**
* 拒绝策略
*/
private ThreadPoolExecutorRejectedPolicy rejectedPolicy = ThreadPoolExecutorRejectedPolicy.CALLER_RUNS;
public ThreadPoolExecutorRejectedPolicy getRejectedPolicy() {
return rejectedPolicy;
}
public void setRejectedPolicy(ThreadPoolExecutorRejectedPolicy rejectedPolicy) {
this.rejectedPolicy = rejectedPolicy;
}
}
/**
* 调度任务扩展配置属性
*/
public static class SchedulerExtensionProperties {
/**
* 拒绝策略
*/
private ThreadPoolExecutorRejectedPolicy rejectedPolicy = ThreadPoolExecutorRejectedPolicy.CALLER_RUNS;
public ThreadPoolExecutorRejectedPolicy getRejectedPolicy() {
return rejectedPolicy;
}
public void setRejectedPolicy(ThreadPoolExecutorRejectedPolicy rejectedPolicy) {
this.rejectedPolicy = rejectedPolicy;
}
}
public ExecutorExtensionProperties getExecution() {
return execution;
}
public void setExecution(ExecutorExtensionProperties execution) {
this.execution = execution;
}
public SchedulerExtensionProperties getScheduling() {
return scheduling;
}
public void setScheduling(SchedulerExtensionProperties scheduling) {
this.scheduling = scheduling;
}
}

View File

@@ -1,122 +0,0 @@
/*
* 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.continew.starter.core.autoconfigure.threadpool;
import org.springframework.boot.context.properties.ConfigurationProperties;
import top.continew.starter.core.constant.PropertiesConstants;
/**
* 线程池配置属性
*
* @author Charles7c
* @author Lion Li<a href="https://gitee.com/dromara/RuoYi-Vue-Plus">RuoYi-Vue-Plus</a>
* @since 1.0.0
*/
@ConfigurationProperties(PropertiesConstants.THREAD_POOL)
public class ThreadPoolProperties {
/**
* 是否启用线程池配置
*/
private boolean enabled = false;
/**
* 核心/最小线程数默认CPU 核心数 + 1
*/
private Integer corePoolSize;
/**
* 最大线程数(默认:(CPU 核心数 + 1) * 2
*/
private Integer maxPoolSize;
/**
* 队列容量
*/
private int queueCapacity = 128;
/**
* 活跃时间(单位:秒)
*/
private int keepAliveSeconds = 300;
/**
* 关闭线程池是否等待任务完成
*/
private boolean waitForTasksToCompleteOnShutdown = false;
/**
* 执行器在关闭时阻塞的最长毫秒数,以等待剩余任务完成执行
*/
private long awaitTerminationMillis = 0;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Integer getCorePoolSize() {
return corePoolSize;
}
public void setCorePoolSize(Integer corePoolSize) {
this.corePoolSize = corePoolSize;
}
public Integer getMaxPoolSize() {
return maxPoolSize;
}
public void setMaxPoolSize(Integer maxPoolSize) {
this.maxPoolSize = maxPoolSize;
}
public int getQueueCapacity() {
return queueCapacity;
}
public void setQueueCapacity(int queueCapacity) {
this.queueCapacity = queueCapacity;
}
public int getKeepAliveSeconds() {
return keepAliveSeconds;
}
public void setKeepAliveSeconds(int keepAliveSeconds) {
this.keepAliveSeconds = keepAliveSeconds;
}
public boolean isWaitForTasksToCompleteOnShutdown() {
return waitForTasksToCompleteOnShutdown;
}
public void setWaitForTasksToCompleteOnShutdown(boolean waitForTasksToCompleteOnShutdown) {
this.waitForTasksToCompleteOnShutdown = waitForTasksToCompleteOnShutdown;
}
public long getAwaitTerminationMillis() {
return awaitTerminationMillis;
}
public void setAwaitTerminationMillis(long awaitTerminationMillis) {
this.awaitTerminationMillis = awaitTerminationMillis;
}
}

View File

@@ -34,21 +34,6 @@ public class PropertiesConstants {
*/
public static final String ENABLED = "enabled";
/**
* 线程池配置
*/
public static final String THREAD_POOL = CONTINEW_STARTER + StringConstants.DOT + "thread-pool";
/**
* Spring Doc 配置
*/
public static final String SPRINGDOC = "springdoc";
/**
* Spring Doc Swagger UI 配置
*/
public static final String SPRINGDOC_SWAGGER_UI = SPRINGDOC + StringConstants.DOT + "swagger-ui";
/**
* 安全配置
*/
@@ -57,12 +42,17 @@ public class PropertiesConstants {
/**
* 密码编解码配置
*/
public static final String PASSWORD = SECURITY + StringConstants.DOT + "password";
public static final String SECURITY_PASSWORD = SECURITY + StringConstants.DOT + "password";
/**
* 加/解密配置
*/
public static final String CRYPTO = SECURITY + StringConstants.DOT + "crypto";
public static final String SECURITY_CRYPTO = SECURITY + StringConstants.DOT + "crypto";
/**
* 限流器配置
*/
public static final String SECURITY_LIMITER = SECURITY + StringConstants.DOT + "limiter";
/**
* Web 配置
@@ -72,17 +62,22 @@ public class PropertiesConstants {
/**
* 跨域配置
*/
public static final String CORS = WEB + StringConstants.DOT + "cors";
public static final String WEB_CORS = WEB + StringConstants.DOT + "cors";
/**
* 链路配置
*/
public static final String TRACE = WEB + StringConstants.DOT + "trace";
public static final String WEB_TRACE = WEB + StringConstants.DOT + "trace";
/**
* XSS 配置
*/
public static final String XSS = WEB + StringConstants.DOT + "xss";
public static final String WEB_XSS = WEB + StringConstants.DOT + "xss";
/**
* 国际化配置
*/
public static final String WEB_I18N = WEB + StringConstants.DOT + "i18n";
/**
* 日志配置

View File

@@ -262,6 +262,16 @@ public class StringConstants {
*/
public static final String CHINESE_COMMA = "";
/**
* 圆括号(左) {@code "("}
*/
public static final String ROUND_BRACKET_START = "(";
/**
* 圆括号(右) {@code ")"}
*/
public static final String ROUND_BRACKET_END = ")";
/**
* 路径模式
*/

View File

@@ -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.continew.starter.core.exception;
/**
* 统一错误码异常
*
* @author Jasmine
* @since 2.2.0
*/
public class GlobalException extends Exception {
private ResultInfoInterface resultInfo;
public GlobalException() {
}
public GlobalException(ResultInfoInterface resultInfo) {
this.resultInfo = resultInfo;
}
public ResultInfoInterface getResultInfo() {
return this.resultInfo;
}
public void setResultInfo(ResultInfoInterface resultInfo) {
this.resultInfo = resultInfo;
}
}

View File

@@ -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.continew.starter.core.exception;
/**
* 接口返回码 所有业务异常都要继承该接口
*
* @author Jasmine
* @since 2.2.0
*/
public enum GlobalResultInfoEnum implements ResultInfoInterface {
/**
* 操作成功
*/
SUCCESS(200, "操作成功"),
/**
* 操作失败
*/
FAILED(500, "操作失败");
private int code;
private String messageKey;
private String defaultMessage;
GlobalResultInfoEnum(int code, String defaultMessage) {
this.code = code;
this.defaultMessage = defaultMessage;
}
GlobalResultInfoEnum(int code, String messageKey, String defaultMessage) {
this.code = code;
this.messageKey = messageKey;
this.defaultMessage = defaultMessage;
}
@Override
public int getCode() {
return this.code;
}
@Override
public String getMessageKey() {
return this.messageKey;
}
@Override
public String getDefaultMessage() {
return this.defaultMessage;
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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.continew.starter.core.exception;
/**
* 接口返回码与消息 所有业务异常都要继承该接口
*
* @author Jasmine
* @since 2.2.0
*/
public interface ResultInfoInterface {
/**
* 获取编码
*
* @return String
*/
int getCode();
/**
* 国际化消息key
*
* @return
*/
default String getMessageKey() {
return "";
}
/**
* 获取默认消息 若从国际化文件里没有获取到值,就取默认值
*
* @return String
*/
String getDefaultMessage();
}

View File

@@ -44,8 +44,8 @@ public class IpUtils {
* @param ip IP 地址
* @return IP 归属地
*/
public static String getAddress(String ip) {
if (isInnerIp(ip)) {
public static String getIpv4Address(String ip) {
if (isInnerIpv4(ip)) {
return "内网IP";
}
Ip2regionSearcher ip2regionSearcher = SpringUtil.getBean(Ip2regionSearcher.class);
@@ -64,7 +64,7 @@ public class IpUtils {
* @param ip IP 地址
* @return 是否为内网 IP
*/
public static boolean isInnerIp(String ip) {
public static boolean isInnerIpv4(String ip) {
return NetUtil.isInnerIP("0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip));
}
}

View File

@@ -14,45 +14,31 @@
* limitations under the License.
*/
package top.continew.starter.messaging.websocket.model;
package top.continew.starter.core.util.expression;
import java.io.Serial;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.function.Function;
/**
* 当前登录用户信息
* 表达式解析器
*
* @author Charles7c
* @since 2.1.0
* @since 2.2.0
*/
public class CurrentUser implements Serializable {
public class ExpressionEvaluator implements Function<Object, Object> {
@Serial
private static final long serialVersionUID = 1L;
private final Function<Object, Object> evaluator;
/**
* 用户 ID
*/
private String userId;
/**
* 扩展字段
*/
private Object extend;
public String getUserId() {
return userId;
public ExpressionEvaluator(String script, Method defineMethod) {
this.evaluator = new SpelEvaluator(script, defineMethod);
}
public void setUserId(String userId) {
this.userId = userId;
@Override
public Object apply(Object rootObject) {
return evaluator.apply(rootObject);
}
public Object getExtend() {
return extend;
}
public void setExtend(Object extend) {
this.extend = extend;
Function<Object, Object> getEvaluator() {
return evaluator;
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.continew.starter.core.util.expression;
import java.lang.reflect.Method;
/**
* 表达式上下文
*
* @author Charles7c
* @since 2.2.0
*/
public class ExpressionInvokeContext {
/**
* 目标方法
*/
private Method method;
/**
* 方法参数
*/
private Object[] args;
/**
* 目标对象
*/
private Object target;
public ExpressionInvokeContext(Method method, Object[] args, Object target) {
this.method = method;
this.args = args;
this.target = target;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public void setArgs(Object[] args) {
this.args = args;
}
public Object[] getArgs() {
return args;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
}

View File

@@ -0,0 +1,60 @@
/*
* 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.continew.starter.core.util.expression;
import cn.hutool.core.text.CharSequenceUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
/**
* 表达式解析工具类
*
* @author Charles7c
* @since 2.2.0
*/
public class ExpressionUtils {
private static final Logger log = LoggerFactory.getLogger(ExpressionUtils.class);
private ExpressionUtils() {
}
/**
* 解析
*
* @param script 表达式
* @param target 目标对象
* @param method 目标方法
* @param args 方法参数
* @return 解析结果
*/
public static Object eval(String script, Object target, Method method, Object... args) {
try {
if (CharSequenceUtil.isBlank(script)) {
return null;
}
ExpressionEvaluator expressionEvaluator = new ExpressionEvaluator(script, method);
ExpressionInvokeContext invokeContext = new ExpressionInvokeContext(method, args, target);
return expressionEvaluator.apply(invokeContext);
} catch (Exception e) {
log.error("Error occurs when eval script \"{}\" in {} : {}", script, method, e.getMessage(), e);
return null;
}
}
}

View File

@@ -0,0 +1,67 @@
/*
* 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.continew.starter.core.util.expression;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import java.lang.reflect.Method;
import java.util.function.Function;
/**
* Spring EL 表达式解析器
*
* @author Charles7c
* @since 2.2.0
*/
public class SpelEvaluator implements Function<Object, Object> {
private static final ExpressionParser PARSER;
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER;
static {
PARSER = new SpelExpressionParser();
PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
}
private final Expression expression;
private String[] parameterNames;
public SpelEvaluator(String script, Method defineMethod) {
expression = PARSER.parseExpression(script);
if (defineMethod.getParameterCount() > 0) {
parameterNames = PARAMETER_NAME_DISCOVERER.getParameterNames(defineMethod);
}
}
@Override
public Object apply(Object rootObject) {
EvaluationContext context = new StandardEvaluationContext(rootObject);
ExpressionInvokeContext invokeContext = (ExpressionInvokeContext)rootObject;
if (parameterNames != null) {
for (int i = 0; i < parameterNames.length; i++) {
context.setVariable(parameterNames[i], invokeContext.getArgs()[i]);
}
}
return expression.getValue(context);
}
}

View File

@@ -16,9 +16,14 @@
package top.continew.starter.core.util.validate;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.extra.spring.SpringUtil;
import jakarta.validation.ConstraintViolation;
import top.continew.starter.core.exception.BadRequestException;
import java.util.Set;
import java.util.function.BooleanSupplier;
/**
@@ -173,4 +178,21 @@ public class ValidationUtils extends Validator {
public static void throwIf(BooleanSupplier conditionSupplier, String template, Object... params) {
throwIf(conditionSupplier, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
}
/**
* JSR 303 校验
*
* @param obj 被校验对象
* @param groups 分组
*/
public static void validate(Object obj, Class<?>... groups) {
jakarta.validation.Validator validator = SpringUtil.getBean(jakarta.validation.Validator.class);
Set<ConstraintViolation<Object>> violations = validator.validate(obj, groups);
if (CollUtil.isEmpty(violations)) {
return;
}
throw ReflectUtil.newInstance(EXCEPTION_TYPE, violations.stream()
.map(ConstraintViolation::getMessage)
.findFirst());
}
}

View File

@@ -25,7 +25,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Charles7c
* @since 1.0.0
*/
@ConfigurationProperties(prefix = "mybatis-flex.extension")
@ConfigurationProperties("mybatis-flex.extension")
public class MyBatisFlexExtensionProperties {
/**

View File

@@ -27,7 +27,7 @@ import top.continew.starter.data.mybatis.plus.autoconfigure.idgenerator.MyBatisP
* @author Charles7c
* @since 1.0.0
*/
@ConfigurationProperties(prefix = "mybatis-plus.extension")
@ConfigurationProperties("mybatis-plus.extension")
public class MyBatisPlusExtensionProperties {
/**
@@ -59,6 +59,11 @@ public class MyBatisPlusExtensionProperties {
*/
private PaginationProperties pagination;
/**
* 启用防全表更新与删除插件
*/
private boolean blockAttackPluginEnabled = true;
/**
* 数据权限插件配置属性
*/
@@ -175,4 +180,12 @@ public class MyBatisPlusExtensionProperties {
public void setPagination(PaginationProperties pagination) {
this.pagination = pagination;
}
public boolean isBlockAttackPluginEnabled() {
return blockAttackPluginEnabled;
}
public void setBlockAttackPluginEnabled(boolean blockAttackPluginEnabled) {
this.blockAttackPluginEnabled = blockAttackPluginEnabled;
}
}

View File

@@ -77,7 +77,9 @@ public class MybatisPlusAutoConfiguration {
interceptor.addInnerInterceptor(this.paginationInnerInterceptor(paginationProperties));
}
// 防全表更新与删除插件
if (properties.isBlockAttackPluginEnabled()) {
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
}
return interceptor;
}

View File

@@ -176,7 +176,6 @@ public class DataPermissionHandlerImpl implements DataPermissionHandler {
* @param expression 处理前的表达式
* @return 处理完后的表达式
*/
//
private Expression buildSelfExpression(DataPermission dataPermission,
DataPermissionCurrentUser currentUser,
Expression expression) {

View File

@@ -15,7 +15,7 @@
<version>${revision}</version>
<packaging>pom</packaging>
<description>ContiNew Starter 依赖模块</description>
<url>https://github.com/Charles7c/continew-starter</url>
<url>https://github.com/continew-org/continew-starter</url>
<licenses>
<license>
<name>GNU LESSER GENERAL PUBLIC LICENSE</name>
@@ -36,14 +36,14 @@
</developer>
</developers>
<scm>
<connection>scm:git:git@github.com:Charles7c/continew-starter.git</connection>
<developerConnection>scm:git:git@github.com:Charles7c/continew-starter.git</developerConnection>
<url>https://github.com/Charles7c/continew-starter</url>
<connection>scm:git:git@github.com:continew-org/continew-starter.git</connection>
<developerConnection>scm:git:git@github.com:continew-org/continew-starter.git</developerConnection>
<url>https://github.com/continew-org/continew-starter</url>
</scm>
<properties>
<!-- 项目版本号 -->
<revision>2.1.0</revision>
<revision>2.2.0</revision>
<sa-token.version>1.38.0</sa-token.version>
<just-auth.version>1.16.6</just-auth.version>
<mybatis-plus.version>3.5.5</mybatis-plus.version>
@@ -473,6 +473,13 @@
<version>${revision}</version>
</dependency>
<!-- 安全模块 - 限流 -->
<dependency>
<groupId>top.continew</groupId>
<artifactId>continew-starter-security-limiter</artifactId>
<version>${revision}</version>
</dependency>
<!-- API 文档模块 -->
<dependency>
<groupId>top.continew</groupId>

View File

@@ -28,6 +28,12 @@
<dependency>
<groupId>top.continew</groupId>
<artifactId>continew-starter-auth-satoken</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 文件处理模块 - Excel -->

View File

@@ -18,6 +18,7 @@ package top.continew.starter.log.core.model;
import cn.hutool.core.text.CharSequenceUtil;
import org.springframework.http.HttpHeaders;
import top.continew.starter.core.util.ExceptionUtils;
import top.continew.starter.core.util.IpUtils;
import top.continew.starter.log.core.enums.Include;
import top.continew.starter.web.util.ServletUtils;
@@ -89,7 +90,9 @@ public class LogRequest {
} else if (includes.contains(Include.REQUEST_PARAM)) {
this.param = request.getParam();
}
this.address = (includes.contains(Include.IP_ADDRESS)) ? IpUtils.getAddress(this.ip) : null;
this.address = (includes.contains(Include.IP_ADDRESS))
? ExceptionUtils.exToNull(() -> IpUtils.getIpv4Address(this.ip))
: null;
if (null == this.headers) {
return;
}

View File

@@ -62,6 +62,11 @@ public class MailConfig {
*/
private String password;
/**
* 发件人
*/
private String from;
/**
* 是否启用 SSL 连接
*/
@@ -121,6 +126,14 @@ public class MailConfig {
this.password = password;
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public boolean isSslEnabled() {
return sslEnabled;
}
@@ -157,6 +170,7 @@ public class MailConfig {
public Properties toJavaMailProperties() {
Properties javaMailProperties = new Properties();
javaMailProperties.putAll(this.getProperties());
javaMailProperties.put("mail.from", this.getFrom());
javaMailProperties.put("mail.smtp.auth", true);
javaMailProperties.put("mail.smtp.ssl.enable", this.isSslEnabled());
if (this.isSslEnabled()) {

View File

@@ -20,12 +20,12 @@ import org.springframework.mail.javamail.JavaMailSenderImpl;
import top.continew.starter.core.util.validate.ValidationUtils;
/**
* 邮件配置服务
* 邮件配置
*
* @author Charles7c
* @since 2.1.0
*/
public interface MailConfigService {
public interface MailConfigurer {
/**
* 获取邮件配置

View File

@@ -27,7 +27,7 @@ import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.core.util.ExceptionUtils;
import top.continew.starter.messaging.mail.core.MailConfigService;
import top.continew.starter.messaging.mail.core.MailConfigurer;
import java.io.File;
import java.nio.charset.StandardCharsets;
@@ -164,7 +164,8 @@ public class MailUtils {
MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true, StandardCharsets.UTF_8
.displayName());
// 设置基本信息
messageHelper.setFrom(mailSender.getUsername());
messageHelper.setFrom(CharSequenceUtil.blankToDefault(mailSender.getJavaMailProperties()
.getProperty("mail.from"), mailSender.getUsername()));
messageHelper.setSubject(subject);
messageHelper.setText(content, isHtml);
// 设置收信人
@@ -216,10 +217,9 @@ public class MailUtils {
*/
public static JavaMailSenderImpl getMailSender() {
JavaMailSenderImpl mailSender = SpringUtil.getBean(JavaMailSenderImpl.class);
MailConfigService mailConfigService = ExceptionUtils.exToNull(() -> SpringUtil
.getBean(MailConfigService.class));
if (mailConfigService != null && mailConfigService.getMailConfig() != null) {
mailConfigService.apply(mailConfigService.getMailConfig(), mailSender);
MailConfigurer mailConfigurer = ExceptionUtils.exToNull(() -> SpringUtil.getBean(MailConfigurer.class));
if (mailConfigurer != null && mailConfigurer.getMailConfig() != null) {
mailConfigurer.apply(mailConfigurer.getMailConfig(), mailSender);
}
return mailSender;
}

View File

@@ -31,7 +31,7 @@ import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.server.HandshakeInterceptor;
import top.continew.starter.core.constant.PropertiesConstants;
import top.continew.starter.messaging.websocket.core.CurrentUserProvider;
import top.continew.starter.messaging.websocket.core.WebSocketClientService;
import top.continew.starter.messaging.websocket.core.WebSocketInterceptor;
import top.continew.starter.messaging.websocket.dao.WebSocketSessionDao;
import top.continew.starter.messaging.websocket.dao.WebSocketSessionDaoDefaultImpl;
@@ -73,7 +73,7 @@ public class WebSocketAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public HandshakeInterceptor handshakeInterceptor() {
return new WebSocketInterceptor(properties, SpringUtil.getBean(CurrentUserProvider.class));
return new WebSocketInterceptor(properties, SpringUtil.getBean(WebSocketClientService.class));
}
/**
@@ -86,12 +86,12 @@ public class WebSocketAutoConfiguration {
}
/**
* 当前用户 Provider(如不提供,则报错)
* WebSocket 客户端服务(如不提供,则报错)
*/
@Bean
@ConditionalOnMissingBean
public CurrentUserProvider currentUserProvider() {
throw new NoSuchBeanDefinitionException(CurrentUserProvider.class);
public WebSocketClientService webSocketClientService() {
throw new NoSuchBeanDefinitionException(WebSocketClientService.class);
}
@PostConstruct

View File

@@ -52,9 +52,9 @@ public class WebSocketProperties {
private List<String> allowedOrigins = new ArrayList<>(ALL);
/**
* 当前登录用户 Key
* 客户端 ID Key
*/
private String currentUserKey = "CURRENT_USER";
private String clientIdKey = "CLIENT_ID";
public boolean isEnabled() {
return enabled;
@@ -80,11 +80,11 @@ public class WebSocketProperties {
this.allowedOrigins = allowedOrigins;
}
public String getCurrentUserKey() {
return currentUserKey;
public String getClientIdKey() {
return clientIdKey;
}
public void setCurrentUserKey(String currentUserKey) {
this.currentUserKey = currentUserKey;
public void setClientIdKey(String clientIdKey) {
this.clientIdKey = clientIdKey;
}
}

View File

@@ -17,21 +17,20 @@
package top.continew.starter.messaging.websocket.core;
import org.springframework.http.server.ServletServerHttpRequest;
import top.continew.starter.messaging.websocket.model.CurrentUser;
/**
* 当前登录用户 Provider
* WebSocket 客户端服务
*
* @author Charles7c
* @since 2.1.0
*/
public interface CurrentUserProvider {
public interface WebSocketClientService {
/**
* 获取当前登录用户
* 获取当前客户端 ID
*
* @param request 请求对象
* @return 当前登录用户
* @return 当前客户端 ID
*/
CurrentUser getCurrentUser(ServletServerHttpRequest request);
String getClientId(ServletServerHttpRequest request);
}

View File

@@ -22,10 +22,12 @@ import org.slf4j.LoggerFactory;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import top.continew.starter.messaging.websocket.autoconfigure.WebSocketProperties;
import top.continew.starter.messaging.websocket.dao.WebSocketSessionDao;
import java.io.IOException;
/**
* WebSocket 处理器
*
@@ -33,7 +35,7 @@ import top.continew.starter.messaging.websocket.dao.WebSocketSessionDao;
* @author Charles7c
* @since 2.1.0
*/
public class WebSocketHandler extends AbstractWebSocketHandler {
public class WebSocketHandler extends TextWebSocketHandler {
private static final Logger log = LoggerFactory.getLogger(WebSocketHandler.class);
private final WebSocketProperties webSocketProperties;
@@ -46,26 +48,41 @@ public class WebSocketHandler extends AbstractWebSocketHandler {
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
log.info("WebSocket receive message. sessionId: {}, message: {}.", session.getId(), message.getPayload());
String clientId = this.getClientId(session);
log.info("WebSocket receive message. clientId: {}, message: {}.", clientId, message.getPayload());
super.handleTextMessage(session, message);
}
@Override
public void afterConnectionEstablished(WebSocketSession session) {
String sessionKey = Convert.toStr(session.getAttributes().get(webSocketProperties.getCurrentUserKey()));
webSocketSessionDao.add(sessionKey, session);
log.info("WebSocket connect successfully. sessionKey: {}.", sessionKey);
String clientId = this.getClientId(session);
webSocketSessionDao.add(clientId, session);
log.info("WebSocket client connect successfully. clientId: {}.", clientId);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
String sessionKey = Convert.toStr(session.getAttributes().get(webSocketProperties.getCurrentUserKey()));
webSocketSessionDao.delete(sessionKey);
log.info("WebSocket connect closed. sessionKey: {}.", sessionKey);
String clientId = this.getClientId(session);
webSocketSessionDao.delete(clientId);
log.info("WebSocket client connect closed. clientId: {}.", clientId);
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) {
log.error("WebSocket transport error. sessionId: {}.", session.getId(), exception);
public void handleTransportError(WebSocketSession session, Throwable exception) throws IOException {
String clientId = this.getClientId(session);
if (session.isOpen()) {
session.close();
}
webSocketSessionDao.delete(clientId);
}
/**
* 获取客户端 ID
*
* @param session 会话
* @return 客户端 ID
*/
private String getClientId(WebSocketSession session) {
return Convert.toStr(session.getAttributes().get(webSocketProperties.getClientIdKey()));
}
}

View File

@@ -22,7 +22,6 @@ import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
import top.continew.starter.messaging.websocket.autoconfigure.WebSocketProperties;
import top.continew.starter.messaging.websocket.model.CurrentUser;
import java.util.Map;
@@ -36,11 +35,12 @@ import java.util.Map;
public class WebSocketInterceptor extends HttpSessionHandshakeInterceptor {
private final WebSocketProperties webSocketProperties;
private final CurrentUserProvider currentUserProvider;
private final WebSocketClientService webSocketClientService;
public WebSocketInterceptor(WebSocketProperties webSocketProperties, CurrentUserProvider currentUserProvider) {
public WebSocketInterceptor(WebSocketProperties webSocketProperties,
WebSocketClientService webSocketClientService) {
this.webSocketProperties = webSocketProperties;
this.currentUserProvider = currentUserProvider;
this.webSocketClientService = webSocketClientService;
}
@Override
@@ -48,8 +48,8 @@ public class WebSocketInterceptor extends HttpSessionHandshakeInterceptor {
ServerHttpResponse response,
WebSocketHandler wsHandler,
Map<String, Object> attributes) {
CurrentUser currentUser = currentUserProvider.getCurrentUser((ServletServerHttpRequest)request);
attributes.put(webSocketProperties.getCurrentUserKey(), currentUser.getUserId());
String clientId = webSocketClientService.getClientId((ServletServerHttpRequest)request);
attributes.put(webSocketProperties.getClientIdKey(), clientId);
return true;
}

View File

@@ -44,11 +44,11 @@ public class WebSocketUtils {
/**
* 发送消息
*
* @param sessionKey 会话 Key
* @param clientId 客户端 ID
* @param message 消息内容
*/
public static void sendMessage(String sessionKey, String message) {
WebSocketSession session = SESSION_DAO.get(sessionKey);
public static void sendMessage(String clientId, String message) {
WebSocketSession session = SESSION_DAO.get(clientId);
sendMessage(session, message);
}

View File

@@ -36,7 +36,7 @@ import top.continew.starter.security.crypto.core.MyBatisEncryptInterceptor;
*/
@AutoConfiguration
@EnableConfigurationProperties(CryptoProperties.class)
@ConditionalOnProperty(prefix = PropertiesConstants.CRYPTO, name = PropertiesConstants.ENABLED, matchIfMissing = true)
@ConditionalOnProperty(prefix = PropertiesConstants.SECURITY_CRYPTO, name = PropertiesConstants.ENABLED, matchIfMissing = true)
public class CryptoAutoConfiguration {
private static final Logger log = LoggerFactory.getLogger(CryptoAutoConfiguration.class);

View File

@@ -25,7 +25,7 @@ import top.continew.starter.core.constant.PropertiesConstants;
* @author Charles7c
* @since 1.4.0
*/
@ConfigurationProperties(PropertiesConstants.CRYPTO)
@ConfigurationProperties(PropertiesConstants.SECURITY_CRYPTO)
public class CryptoProperties {
/**

View File

@@ -53,7 +53,19 @@ public abstract class AbstractMyBatisInterceptor implements Interceptor {
* @return 加密参数
*/
public Map<String, FieldEncrypt> getEncryptParams(String mappedStatementId) {
return ENCRYPT_PARAM_CACHE.computeIfAbsent(mappedStatementId, this::getEncryptParamsNoCached);
return getEncryptParams(mappedStatementId, null);
}
/**
* 获取加密参数
*
* @param mappedStatementId 映射语句 ID
* @param parameterIndex 参数索引
* @return 加密参数
*/
public Map<String, FieldEncrypt> getEncryptParams(String mappedStatementId, Integer parameterIndex) {
return ENCRYPT_PARAM_CACHE
.computeIfAbsent(mappedStatementId, key -> getEncryptParamsNoCached(mappedStatementId, parameterIndex));
}
/**
@@ -105,9 +117,10 @@ public abstract class AbstractMyBatisInterceptor implements Interceptor {
* 获取参数列表(无缓存)
*
* @param mappedStatementId 映射语句 ID
* @param parameterIndex 参数数量
* @return 参数列表
*/
private Map<String, FieldEncrypt> getEncryptParamsNoCached(String mappedStatementId) {
private Map<String, FieldEncrypt> getEncryptParamsNoCached(String mappedStatementId, Integer parameterIndex) {
try {
String className = CharSequenceUtil.subBefore(mappedStatementId, StringConstants.DOT, true);
String wrapperMethodName = CharSequenceUtil.subAfter(mappedStatementId, StringConstants.DOT, true);
@@ -117,8 +130,12 @@ public abstract class AbstractMyBatisInterceptor implements Interceptor {
.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();
Optional<Method> methodOptional = Arrays.stream(ReflectUtil.getMethods(Class.forName(className), m -> {
if (Objects.nonNull(parameterIndex)) {
return Objects.equals(m.getName(), methodName) && m.getParameterCount() == parameterIndex;
}
return Objects.equals(m.getName(), methodName);
})).findFirst();
if (methodOptional.isEmpty()) {
return Collections.emptyMap();
}
@@ -136,6 +153,8 @@ public abstract class AbstractMyBatisInterceptor implements Interceptor {
}
} else if (parameterName.startsWith(Constants.ENTITY)) {
map.put(parameterName, null);
} else if (parameterName.startsWith(Constants.WRAPPER)) {
map.put(parameterName, null);
}
}
return map;

View File

@@ -16,8 +16,15 @@
package top.continew.starter.security.crypto.core;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
@@ -28,11 +35,14 @@ 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.continew.starter.core.constant.StringConstants;
import top.continew.starter.security.crypto.annotation.FieldEncrypt;
import top.continew.starter.security.crypto.autoconfigure.CryptoProperties;
import top.continew.starter.security.crypto.encryptor.IEncryptor;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
/**
@@ -99,13 +109,18 @@ public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor {
* @throws Exception /
*/
private void encryptMap(HashMap<String, Object> parameterMap, MappedStatement mappedStatement) throws Exception {
Map<String, FieldEncrypt> encryptParamMap = super.getEncryptParams(mappedStatement.getId());
Map<String, FieldEncrypt> encryptParamMap = super.getEncryptParams(mappedStatement.getId(), parameterMap
.isEmpty() ? null : parameterMap.size() / 2);
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 if (parameterName.startsWith(Constants.WRAPPER)) {
// 处理参数为 Wrapper 的情况
Wrapper wrapper = (Wrapper)parameterMap.getOrDefault(parameterName, null);
this.doEncrypt(wrapper, mappedStatement);
} else {
FieldEncrypt fieldEncrypt = encryptParamEntry.getValue();
parameterMap.put(parameterName, this.doEncrypt(parameterMap.get(parameterName), fieldEncrypt));
@@ -113,6 +128,69 @@ public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor {
}
}
/**
* 处理加密
*
* @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);
}
}
/**
* 处理 Wrapper 加密
*
* @param wrapper Wrapper 对象
* @param mappedStatement 映射语句
* @throws Exception /
*/
private void doEncrypt(Wrapper wrapper, MappedStatement mappedStatement) throws Exception {
if (wrapper instanceof AbstractWrapper abstractWrapper) {
String sqlSet = abstractWrapper.getSqlSet();
if (CharSequenceUtil.isEmpty(sqlSet)) {
return;
}
String className = CharSequenceUtil.subBefore(mappedStatement.getId(), StringConstants.DOT, true);
Class<?> mapperClass = Class.forName(className);
Optional<Class> baseMapperGenerics = getEntityTypeByMapperClass(mapperClass, Optional.empty());
// 获取不到泛型对象 则不进行下面的逻辑
if (baseMapperGenerics.isEmpty()) {
return;
}
TableInfo tableInfo = TableInfoHelper.getTableInfo(baseMapperGenerics.get());
List<TableFieldInfo> fieldList = tableInfo.getFieldList();
// 将 name=#{ew.paramNameValuePairs.xxx},age=#{ew.paramNameValuePairs.xxx} 切出来
for (String sqlFragment : sqlSet.split(Constants.COMMA)) {
String columnName = sqlFragment.split(Constants.EQUALS)[0];
// 截取其中的 xxx 字符,例如:#{ew.paramNameValuePairs.xxx}
String paramNameVal = sqlFragment.split(Constants.EQUALS)[1].substring(25, sqlFragment
.split(Constants.EQUALS)[1].length() - 1);
Optional<TableFieldInfo> fieldInfo = fieldList.stream()
.filter(f -> f.getColumn().equals(columnName))
.findAny();
if (fieldInfo.isPresent()) {
TableFieldInfo tableFieldInfo = fieldInfo.get();
FieldEncrypt fieldEncrypt = tableFieldInfo.getField().getAnnotation(FieldEncrypt.class);
if (fieldEncrypt != null) {
Map<String, Object> paramNameValuePairs = abstractWrapper.getParamNameValuePairs();
paramNameValuePairs.put(paramNameVal, this.doEncrypt(paramNameValuePairs
.get(paramNameVal), fieldEncrypt));
}
}
}
}
}
/**
* 处理加密
*
@@ -131,21 +209,38 @@ public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor {
}
/**
* 处理加密
* 从 Mapper 获取泛型
*
* @param fieldList 加密字段列表
* @param entity 实体
* @throws Exception /
* @param mapperClass Mapper class
* @param tempResult 临时存储的泛型对象
* @return 泛型
*/
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);
private static Optional<Class> getEntityTypeByMapperClass(Class<?> mapperClass, Optional<Class> tempResult) {
Type[] genericInterfaces = mapperClass.getGenericInterfaces();
Optional<Class> result = tempResult;
for (Type genericInterface : genericInterfaces) {
if (genericInterface instanceof ParameterizedType parameterizedType) {
Type rawType = parameterizedType.getRawType();
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
// 如果匹配上 BaseMapper 且泛型参数是 Class 类型,则直接返回
if (rawType.equals(BaseMapper.class)) {
return actualTypeArguments[0] instanceof Class
? Optional.of((Class)actualTypeArguments[0])
: result;
} else if (rawType instanceof Class interfaceClass) {
// 如果泛型参数是 Class 类型,则传递给递归调用
if (actualTypeArguments[0] instanceof Class tempResultClass) {
result = Optional.of(tempResultClass);
}
// 递归调用,继续查找
Optional<Class> innerResult = getEntityTypeByMapperClass(interfaceClass, result);
if (innerResult.isPresent()) {
return innerResult;
}
}
}
}
// 如果没有找到,返回传递进来的 tempResult
return Optional.empty();
}
}

View File

@@ -0,0 +1,31 @@
<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.continew</groupId>
<artifactId>continew-starter-security</artifactId>
<version>${revision}</version>
</parent>
<artifactId>continew-starter-security-limiter</artifactId>
<description>ContiNew Starter 安全模块 - 限流</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- Web 模块 -->
<dependency>
<groupId>top.continew</groupId>
<artifactId>continew-starter-web</artifactId>
</dependency>
<!-- 缓存模块 - Redisson -->
<dependency>
<groupId>top.continew</groupId>
<artifactId>continew-starter-cache-redisson</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,69 @@
/*
* 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.continew.starter.security.limiter.annotation;
import org.redisson.api.RateIntervalUnit;
import top.continew.starter.security.limiter.enums.LimitType;
import java.lang.annotation.*;
/**
* 限流注解
*
* @author KAI
* @since 2.2.0
*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimiter {
/**
* 类型
*/
LimitType type() default LimitType.DEFAULT;
/**
* 名称
*/
String name() default "";
/**
* 键(支持 Spring EL 表达式)
*/
String key() default "";
/**
* 速率(指定时间间隔产生的令牌数)
*/
int rate() default Integer.MAX_VALUE;
/**
* 速率间隔(时间间隔)
*/
int interval() default 0;
/**
* 速率间隔时间单位(默认:毫秒)
*/
RateIntervalUnit unit() default RateIntervalUnit.MILLISECONDS;
/**
* 提示信息
*/
String message() default "操作过于频繁,请稍后再试";
}

View File

@@ -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.continew.starter.security.limiter.annotation;
import java.lang.annotation.*;
/**
* 限流组注解
*
* @author KAI
* @since 2.2.0
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RateLimiters {
/**
* 限流组
*/
RateLimiter[] value();
}

View File

@@ -0,0 +1,60 @@
/*
* 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.continew.starter.security.limiter.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 org.springframework.context.annotation.ComponentScan;
import top.continew.starter.core.constant.PropertiesConstants;
import top.continew.starter.security.limiter.core.DefaultRateLimiterNameGenerator;
import top.continew.starter.security.limiter.core.RateLimiterNameGenerator;
/**
* 限流器自动配置
*
* @author KAI
* @author Charles7c
* @since 2.2.0
*/
@AutoConfiguration
@EnableConfigurationProperties(RateLimiterProperties.class)
@ComponentScan({"top.continew.starter.security.limiter.core"})
@ConditionalOnProperty(prefix = PropertiesConstants.SECURITY_LIMITER, name = PropertiesConstants.ENABLED, matchIfMissing = true)
public class RateLimiterAutoConfiguration {
private static final Logger log = LoggerFactory.getLogger(RateLimiterAutoConfiguration.class);
/**
* 限流器名称生成器
*/
@Bean
@ConditionalOnMissingBean
public RateLimiterNameGenerator nameGenerator() {
return new DefaultRateLimiterNameGenerator();
}
@PostConstruct
public void postConstruct() {
log.debug("[ContiNew Starter] - Auto Configuration 'Security-RateLimiter' completed initialization.");
}
}

View File

@@ -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.continew.starter.security.limiter.autoconfigure;
import org.springframework.boot.context.properties.ConfigurationProperties;
import top.continew.starter.core.constant.PropertiesConstants;
/**
* 限流器配置属性
*
* @author KAI
* @since 2.2.0
*/
@ConfigurationProperties(PropertiesConstants.SECURITY_LIMITER)
public class RateLimiterProperties {
/**
* Key 前缀
*/
private String keyPrefix = "RateLimiter";
public String getKeyPrefix() {
return keyPrefix;
}
public void setKeyPrefix(String keyPrefix) {
this.keyPrefix = keyPrefix;
}
}

View File

@@ -0,0 +1,107 @@
/*
* 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.continew.starter.security.limiter.core;
import cn.hutool.core.util.ClassUtil;
import top.continew.starter.core.constant.StringConstants;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
/**
* 默认限流器名称生成器
*
* @author Charles7c
* @since 2.2.0
*/
public class DefaultRateLimiterNameGenerator implements RateLimiterNameGenerator {
protected final ConcurrentHashMap<Method, String> nameMap = new ConcurrentHashMap<>();
@Override
public String generate(Object target, Method method, Object... args) {
return nameMap.computeIfAbsent(method, key -> {
final StringBuilder nameSb = new StringBuilder();
String className = method.getDeclaringClass().getName();
nameSb.append(ClassUtil.getShortClassName(className));
nameSb.append(StringConstants.DOT);
nameSb.append(method.getName());
nameSb.append(StringConstants.ROUND_BRACKET_START);
for (Class<?> clazz : method.getParameterTypes()) {
this.getDescriptor(nameSb, clazz);
}
nameSb.append(StringConstants.ROUND_BRACKET_END);
return nameSb.toString();
});
}
/**
* 获取指定数据类型的描述
*
* @param sb 名称字符串缓存
* @param typeClass 数据类型
*/
private void getDescriptor(final StringBuilder sb, final Class<?> typeClass) {
Class<?> clazz = typeClass;
while (true) {
if (clazz.isPrimitive()) {
sb.append(this.getPrimitiveChar(clazz));
return;
} else if (clazz.isArray()) {
sb.append(StringConstants.BRACKET_START);
clazz = clazz.getComponentType();
} else {
sb.append('L');
String name = clazz.getName();
name = ClassUtil.getShortClassName(name);
sb.append(name);
sb.append(StringConstants.SEMICOLON);
return;
}
}
}
/**
* 根据基本数据获取类型字符
*
* @param clazz 数据类型
* @return 类型字符
*/
private char getPrimitiveChar(Class<?> clazz) {
char c;
if (clazz == Integer.TYPE) {
c = 'I';
} else if (clazz == Void.TYPE) {
c = 'V';
} else if (clazz == Boolean.TYPE) {
c = 'Z';
} else if (clazz == Byte.TYPE) {
c = 'B';
} else if (clazz == Character.TYPE) {
c = 'C';
} else if (clazz == Short.TYPE) {
c = 'S';
} else if (clazz == Double.TYPE) {
c = 'D';
} else if (clazz == Float.TYPE) {
c = 'F';
} else {
c = 'J';
}
return c;
}
}

View File

@@ -0,0 +1,200 @@
/*
* 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.continew.starter.security.limiter.core;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.*;
import org.springframework.stereotype.Component;
import top.continew.starter.cache.redisson.util.RedisUtils;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.core.util.expression.ExpressionUtils;
import top.continew.starter.security.limiter.annotation.RateLimiter;
import top.continew.starter.security.limiter.annotation.RateLimiters;
import top.continew.starter.security.limiter.autoconfigure.RateLimiterProperties;
import top.continew.starter.security.limiter.enums.LimitType;
import top.continew.starter.security.limiter.exception.RateLimiterException;
import top.continew.starter.web.util.ServletUtils;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* 限流器切面
*
* @author KAI
* @author Charles7c
* @since 2.2.0
*/
@Aspect
@Component
public class RateLimiterAspect {
private static final ConcurrentHashMap<String, RRateLimiter> RATE_LIMITER_CACHE = new ConcurrentHashMap<>();
private final RateLimiterProperties properties;
private final RateLimiterNameGenerator nameGenerator;
private final RedissonClient redissonClient;
public RateLimiterAspect(RateLimiterProperties properties,
RateLimiterNameGenerator nameGenerator,
RedissonClient redissonClient) {
this.properties = properties;
this.nameGenerator = nameGenerator;
this.redissonClient = redissonClient;
}
/**
* 单个限流注解切点
*/
@Pointcut("@annotation(top.continew.starter.security.limiter.annotation.RateLimiter)")
public void rateLimiterPointCut() {
}
/**
* 多个限流注解切点
*/
@Pointcut("@annotation(top.continew.starter.security.limiter.annotation.RateLimiters)")
public void rateLimitersPointCut() {
}
/**
* 单限流场景
*
* @param joinPoint 切点
* @param rateLimiter 限流注解
* @return 目标方法的执行结果
* @throws Throwable /
*/
@Around("@annotation(rateLimiter)")
public Object aroundRateLimiter(ProceedingJoinPoint joinPoint, RateLimiter rateLimiter) throws Throwable {
if (isRateLimited(joinPoint, rateLimiter)) {
throw new RateLimiterException(rateLimiter.message());
}
return joinPoint.proceed();
}
/**
* 多限流场景
*
* @param joinPoint 切点
* @param rateLimiters 限流组注解
* @return 目标方法的执行结果
* @throws Throwable /
*/
@Around("@annotation(rateLimiters)")
public Object aroundRateLimiters(ProceedingJoinPoint joinPoint, RateLimiters rateLimiters) throws Throwable {
for (RateLimiter rateLimiter : rateLimiters.value()) {
if (isRateLimited(joinPoint, rateLimiter)) {
throw new RateLimiterException(rateLimiter.message());
}
}
return joinPoint.proceed();
}
/**
* 是否需要限流
*
* @param joinPoint 切点
* @param rateLimiter 限流注解
* @return true: 需要限流false不需要限流
*/
private boolean isRateLimited(ProceedingJoinPoint joinPoint, RateLimiter rateLimiter) {
try {
String cacheKey = this.getCacheKey(joinPoint, rateLimiter);
RRateLimiter rRateLimiter = RATE_LIMITER_CACHE.computeIfAbsent(cacheKey, key -> redissonClient
.getRateLimiter(cacheKey));
// 限流器配置
RateType rateType = rateLimiter.type() == LimitType.CLUSTER ? RateType.PER_CLIENT : RateType.OVERALL;
int rate = rateLimiter.rate();
int rateInterval = rateLimiter.interval();
RateIntervalUnit rateIntervalUnit = rateLimiter.unit();
// 判断是否需要更新限流器
if (this.isConfigurationUpdateNeeded(rRateLimiter, rateType, rate, rateInterval, rateIntervalUnit)) {
// 更新限流器
rRateLimiter.setRate(rateType, rate, rateInterval, rateIntervalUnit);
}
// 尝试获取令牌
return !rRateLimiter.tryAcquire();
} catch (Exception e) {
throw new RateLimiterException("服务器限流异常,请稍候再试", e);
}
}
/**
* 获取限流缓存 Key
*
* @param joinPoint 切点
* @param rateLimiter 限流注解
* @return 限流缓存 Key
*/
private String getCacheKey(JoinPoint joinPoint, RateLimiter rateLimiter) {
Object target = joinPoint.getTarget();
MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
Method method = methodSignature.getMethod();
Object[] args = joinPoint.getArgs();
// 获取限流名称
String name = rateLimiter.name();
if (CharSequenceUtil.isBlank(name)) {
name = nameGenerator.generate(target, method, args);
}
// 解析限流 Key
String key = rateLimiter.key();
if (CharSequenceUtil.isNotBlank(key)) {
Object eval = ExpressionUtils.eval(key, target, method, args);
if (ObjectUtil.isNull(eval)) {
throw new RateLimiterException("限流 Key 解析错误");
}
key = Convert.toStr(eval);
}
// 获取后缀
String suffix = switch (rateLimiter.type()) {
case IP -> JakartaServletUtil.getClientIP(ServletUtils.getRequest());
case CLUSTER -> redissonClient.getId();
default -> StringConstants.EMPTY;
};
return RedisUtils.formatKey(properties.getKeyPrefix(), name, key, suffix);
}
/**
* 判断是否需要更新限流器配置
*
* @param rRateLimiter 限流器
* @param rateType 限流类型OVERALL全局限流PER_CLIENT单机限流
* @param rate 速率(指定时间间隔产生的令牌数)
* @param rateInterval 速率间隔
* @param rateIntervalUnit 时间单位
* @return 是否需要更新配置
*/
private boolean isConfigurationUpdateNeeded(RRateLimiter rRateLimiter,
RateType rateType,
long rate,
long rateInterval,
RateIntervalUnit rateIntervalUnit) {
RateLimiterConfig config = rRateLimiter.getConfig();
return !Objects.equals(config.getRateType(), rateType) || !Objects.equals(config.getRate(), rate) || !Objects
.equals(config.getRateInterval(), rateIntervalUnit.toMillis(rateInterval));
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.continew.starter.security.limiter.core;
import java.lang.reflect.Method;
/**
* 限流器名称生成器
*
* @author Charles7c
* @since 2.2.0
*/
@FunctionalInterface
public interface RateLimiterNameGenerator {
/**
* Generate a rate limiter name for the given method and its parameters.
*
* @param target the target instance
* @param method the method being called
* @param args the method parameters (with any var-args expanded)
* @return a generated rate limiter name
*/
String generate(Object target, Method method, Object... args);
}

View File

@@ -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.continew.starter.security.limiter.enums;
/**
* 限流类型
*
* @author KAI
* @since 2.2.0
*/
public enum LimitType {
/**
* 全局限流
*/
DEFAULT,
/**
* 根据 IP 限流
*/
IP,
/**
* 根据实例限流(支持集群多实例)
*/
CLUSTER
}

View File

@@ -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.continew.starter.security.limiter.exception;
import top.continew.starter.core.exception.BaseException;
/**
* 限流异常
*
* @author KAI
* @since 2.2.0
*/
public class RateLimiterException extends BaseException {
public RateLimiterException(String message) {
super(message);
}
public RateLimiterException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1 @@
top.continew.starter.security.limiter.autoconfigure.RateLimiterAutoConfiguration

View File

@@ -0,0 +1,7 @@
{
"top.continew.starter.security.limiter.annotation.RateLimiter@key":{
"method":{
"parameters": true
}
}
}

View File

@@ -53,7 +53,7 @@ import java.util.Map;
*/
@AutoConfiguration
@EnableConfigurationProperties(PasswordEncoderProperties.class)
@ConditionalOnProperty(prefix = PropertiesConstants.PASSWORD, name = PropertiesConstants.ENABLED, matchIfMissing = true)
@ConditionalOnProperty(prefix = PropertiesConstants.SECURITY_PASSWORD, name = PropertiesConstants.ENABLED, matchIfMissing = true)
public class PasswordEncoderAutoConfiguration {
private static final Logger log = LoggerFactory.getLogger(PasswordEncoderAutoConfiguration.class);

View File

@@ -25,7 +25,7 @@ import top.continew.starter.core.constant.PropertiesConstants;
* @author Jasmine
* @since 1.3.0
*/
@ConfigurationProperties(PropertiesConstants.PASSWORD)
@ConfigurationProperties(PropertiesConstants.SECURITY_PASSWORD)
public class PasswordEncoderProperties {
/**

View File

@@ -17,6 +17,7 @@
<module>continew-starter-security-password</module>
<module>continew-starter-security-mask</module>
<module>continew-starter-security-crypto</module>
<module>continew-starter-security-limiter</module>
</modules>
<dependencies>

View File

@@ -40,7 +40,7 @@ import top.continew.starter.core.constant.StringConstants;
@Lazy
@AutoConfiguration
@ConditionalOnWebApplication
@ConditionalOnProperty(prefix = PropertiesConstants.CORS, name = PropertiesConstants.ENABLED, havingValue = "true")
@ConditionalOnProperty(prefix = PropertiesConstants.WEB_CORS, name = PropertiesConstants.ENABLED, havingValue = "true")
@EnableConfigurationProperties(CorsProperties.class)
public class CorsAutoConfiguration {
private static final Logger log = LoggerFactory.getLogger(CorsAutoConfiguration.class);

View File

@@ -30,7 +30,7 @@ import java.util.List;
* @author Charles7c
* @since 1.0.0
*/
@ConfigurationProperties(PropertiesConstants.CORS)
@ConfigurationProperties(PropertiesConstants.WEB_CORS)
public class CorsProperties {
private static final List<String> ALL = Collections.singletonList(StringConstants.ASTERISK);

View File

@@ -19,6 +19,8 @@ package top.continew.starter.web.autoconfigure.exception;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
@@ -32,11 +34,15 @@ import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartException;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.core.exception.BadRequestException;
import top.continew.starter.core.exception.BusinessException;
import top.continew.starter.core.exception.GlobalException;
import top.continew.starter.core.exception.ResultInfoInterface;
import top.continew.starter.web.autoconfigure.i18n.I18nProperties;
import top.continew.starter.web.model.R;
import top.continew.starter.web.util.MessageSourceUtils;
/**
* 全局异常处理器
@@ -49,6 +55,8 @@ public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
private static final String PARAM_FAILED = "请求地址 [{}],参数验证失败。";
@Resource
private I18nProperties i18nProperties;
/**
* 拦截自定义验证异常-错误请求
@@ -108,11 +116,29 @@ public class GlobalExceptionHandler {
/**
* 拦截文件上传异常-超过上传大小限制
*/
@ExceptionHandler(MaxUploadSizeExceededException.class)
public R<Void> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e, HttpServletRequest request) {
log.warn("请求地址 [{}],上传文件失败,文件大小超过限制。", request.getRequestURI(), e);
String sizeLimit = CharSequenceUtil.subBetween(e.getMessage(), "The maximum size ", " for");
@ExceptionHandler(MultipartException.class)
public R<Void> handleRequestTooBigException(MultipartException e, HttpServletRequest request) {
String msg = e.getMessage();
R<Void> defaultFail = R.fail(HttpStatus.BAD_REQUEST.value(), msg);
if (CharSequenceUtil.isBlank(msg)) {
return defaultFail;
}
String sizeLimit;
Throwable cause = e.getCause();
if (null != cause) {
msg = msg.concat(cause.getMessage().toLowerCase());
}
if (msg.contains("size") && msg.contains("exceed")) {
sizeLimit = CharSequenceUtil.subBetween(msg, "maximum (", ")");
} else if (msg.contains("larger than")) {
sizeLimit = CharSequenceUtil.subAfter(msg, "larger than ", true);
} else {
return defaultFail;
}
String errorMsg = "请上传小于 %sMB 的文件".formatted(NumberUtil.parseLong(sizeLimit) / 1024 / 1024);
log.warn("请求地址 [{}],上传文件失败,文件大小超过限制。", request.getRequestURI(), e);
return R.fail(HttpStatus.BAD_REQUEST.value(), errorMsg);
}
@@ -135,6 +161,23 @@ public class GlobalExceptionHandler {
return R.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage());
}
/**
* 拦截全局应用异常
*/
@ExceptionHandler(GlobalException.class)
public R<Void> handleGlobalException(GlobalException e, HttpServletRequest request) {
log.error("请求地址 [{}],发生业务异常。", request.getRequestURI(), e);
ResultInfoInterface resultInfo = e.getResultInfo();
// 未开启,直接返回
if (!i18nProperties.getEnabled()) {
return R.fail(resultInfo.getCode(), resultInfo.getDefaultMessage());
}
// 以用户自定的messageKey优先否则枚举当messageKey
String messageKey = StrUtil.blankToDefault(resultInfo.getMessageKey(), resultInfo.toString());
String message = MessageSourceUtils.getMessage(messageKey, resultInfo.getDefaultMessage());
return R.fail(resultInfo.getCode(), message);
}
/**
* 拦截未知的运行时异常
*/
@@ -152,4 +195,5 @@ public class GlobalExceptionHandler {
log.error("请求地址 [{}],发生未知异常。", request.getRequestURI(), e);
return R.fail(e.getMessage());
}
}

View File

@@ -26,10 +26,12 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
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.validation.beanvalidation.SpringConstraintValidatorFactory;
import top.continew.starter.web.autoconfigure.i18n.I18nProperties;
/**
* 全局异常处理器自动配置
@@ -40,6 +42,7 @@ import org.springframework.validation.beanvalidation.SpringConstraintValidatorFa
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(BasicErrorController.class)
@Import({GlobalExceptionHandler.class, GlobalErrorHandler.class})
@EnableConfigurationProperties(I18nProperties.class)
public class GlobalExceptionHandlerAutoConfiguration {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandlerAutoConfiguration.class);

View File

@@ -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.continew.starter.web.autoconfigure.i18n;
import org.springframework.boot.context.properties.ConfigurationProperties;
import top.continew.starter.core.constant.PropertiesConstants;
/**
* 国际化配置属性
*
* @author Jasmine
* @since 2.2.0
*/
@ConfigurationProperties(PropertiesConstants.WEB_I18N)
public class I18nProperties {
/**
* 国际化开启 true-开启, false-关闭
*/
private Boolean enabled;
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
}

View File

@@ -43,7 +43,7 @@ import top.continew.starter.core.constant.PropertiesConstants;
@AutoConfiguration
@ConditionalOnWebApplication
@EnableConfigurationProperties(TraceProperties.class)
@ConditionalOnProperty(prefix = PropertiesConstants.TRACE, name = PropertiesConstants.ENABLED, havingValue = "true")
@ConditionalOnProperty(prefix = PropertiesConstants.WEB_TRACE, name = PropertiesConstants.ENABLED, havingValue = "true")
public class TraceAutoConfiguration {
private static final Logger log = LoggerFactory.getLogger(TraceAutoConfiguration.class);

View File

@@ -26,7 +26,7 @@ import top.continew.starter.core.constant.PropertiesConstants;
* @author Charles7c
* @since 1.3.0
*/
@ConfigurationProperties(PropertiesConstants.TRACE)
@ConfigurationProperties(PropertiesConstants.WEB_TRACE)
public class TraceProperties {
/**

View File

@@ -33,7 +33,7 @@ import top.continew.starter.core.constant.PropertiesConstants;
@AutoConfiguration
@ConditionalOnWebApplication
@EnableConfigurationProperties(XssProperties.class)
@ConditionalOnProperty(prefix = PropertiesConstants.XSS, name = PropertiesConstants.ENABLED, havingValue = "true")
@ConditionalOnProperty(prefix = PropertiesConstants.WEB_XSS, name = PropertiesConstants.ENABLED, havingValue = "true")
public class XssAutoConfiguration {
/**

View File

@@ -29,7 +29,7 @@ import java.util.List;
* @author whhya
* @since 2.0.0
*/
@ConfigurationProperties(PropertiesConstants.XSS)
@ConfigurationProperties(PropertiesConstants.WEB_XSS)
public class XssProperties {
/**

View File

@@ -0,0 +1,53 @@
/*
* 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.continew.starter.web.util;
import cn.hutool.extra.spring.SpringUtil;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
/**
* @author Jasmine
* @since 2.2.0
*/
public class MessageSourceUtils {
private static final MessageSource messageSource = SpringUtil.getBean(MessageSource.class);
private static final Object[] emptyArray = new Object[] {};
public static String getMessage(String key) {
return getMessage(key, emptyArray);
}
public static String getMessage(String key, String defaultMessage) {
return getMessage(key, defaultMessage, emptyArray);
}
public static String getMessage(String msgKey, Object... args) {
return getMessage(msgKey, msgKey, args);
}
public static String getMessage(String msgKey, String defaultMessage, Object... args) {
try {
return messageSource.getMessage(msgKey, args, LocaleContextHolder.getLocale());
} catch (Exception e) {
return defaultMessage;
}
}
}

View File

@@ -19,7 +19,7 @@
ContiNew Starter 包含了一系列经过企业实践优化的依赖包(如 MyBatis-Plus、SaToken
可轻松集成到应用中,为开发人员减少手动引入依赖及配置的麻烦,为 Spring Boot Web 项目的灵活快速构建提供支持。
</description>
<url>https://github.com/Charles7c/continew-starter</url>
<url>https://github.com/continew-org/continew-starter</url>
<licenses>
<license>
<name>GNU LESSER GENERAL PUBLIC LICENSE</name>
@@ -40,9 +40,9 @@
</developer>
</developers>
<scm>
<connection>scm:git:git@github.com:Charles7c/continew-starter.git</connection>
<developerConnection>scm:git:git@github.com:Charles7c/continew-starter.git</developerConnection>
<url>https://github.com/Charles7c/continew-starter</url>
<connection>scm:git:git@github.com:continew-org/continew-starter.git</connection>
<developerConnection>scm:git:git@github.com:continew-org/continew-starter.git</developerConnection>
<url>https://github.com/continew-org/continew-starter</url>
</scm>
<properties>