mirror of
https://github.com/continew-org/continew-starter.git
synced 2025-11-12 06:57:10 +08:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a4d1731862 | |||
| ce38377a98 | |||
| 0463c74aa4 | |||
| 8766f11eb2 | |||
| 65d3cef093 | |||
| 615dfdd03f | |||
| efaef9d7e6 | |||
| 45da758dee | |||
| a87104830f | |||
| 1c65191b8a | |||
| 08068cb9f7 | |||
|
|
49b1b6a690 | ||
|
|
1d4f3a33b9 | ||
|
|
5a2621a030 | ||
| f2ba10beae | |||
|
|
7e8a15ae8a | ||
|
|
199a83fbea | ||
|
|
ca2c88651f | ||
|
|
a6a44cd461 | ||
| 5822d073fb | |||
| e24256818d | |||
| 3e0dd83e26 | |||
| acfd1daf4b | |||
| ae2b898e57 | |||
| f662b74061 | |||
|
|
e2d8f45206 |
20
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
20
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,5 +1,5 @@
|
||||
name: "\U0001F41E Bug 报告"
|
||||
description: Create a report to help us improve
|
||||
description: 在使用 xxx 功能时出现异常
|
||||
labels: ['bug: pending triage']
|
||||
body:
|
||||
- type: markdown
|
||||
@@ -10,20 +10,22 @@ body:
|
||||
id: checkboxes
|
||||
attributes:
|
||||
label: 请您确认
|
||||
description: 在提交 Issue 之前,请确保执行过以下操作。
|
||||
description: 在提交 Bug 之前,请确保执行过以下操作。
|
||||
options:
|
||||
- label: 尝试[最新版本](https://central.sonatype.com/artifact/top.continew/continew-starter/versions),仍有相同问题
|
||||
- label: 重启项目和 IDE 后,仍然能够复现此问题
|
||||
required: true
|
||||
- label: 阅读[使用指南](https://continew.top/starter/intro/what-is.html)
|
||||
- label: 查阅过 [使用指南](https://continew.top/starter/guide/introduction.html) 和 [常见问题](https://continew.top/starter/faq.html) ,仍无解决方法
|
||||
required: true
|
||||
- label: 查找[常见问题](https://continew.top/faq.html)
|
||||
- label: 根据报错信息(自行翻译英文)百度或 Google 后,仍无法解决
|
||||
required: true
|
||||
- label: 根据报错信息(自行翻译英文)百度或 Google 一下
|
||||
- label: 尝试 [最新版本](https://central.sonatype.com/artifact/top.continew/continew-starter/versions)(还可以尝试本地编译安装 dev 分支的最新快照版本),仍有相同问题
|
||||
required: true
|
||||
- label: 阅读源码并在 IDE 中进行断点调试
|
||||
- label: 搜索了项目 Issues,没有其他人提交过类似的 Bug(如果对应 Bug 尚未解决,您可以先订阅关注该 Issue,为了方便后来者查找问题解决方法,请避免创建重复的 Issue)
|
||||
required: true
|
||||
- label: 阅读了源码并在 IDE 中进行断点调试
|
||||
required: false
|
||||
- label: 是否愿意为您提出的 Bug 提交 PR?
|
||||
required: false
|
||||
- label: 搜索是否有其他人提交过类似的 Issue,如果对应 Issue 尚未解决,您可以先订阅关注该 Issue(为了方便后来者查找问题解决方法,请尽量避免创建重复的 Issue)
|
||||
required: true
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
||||
14
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
14
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,5 +1,5 @@
|
||||
name: "\U0001F680 新 Feature 建议"
|
||||
description: Suggest an idea for this project
|
||||
description: 我希望增加 xxx 功能;现有的 xxx 功能不好用...
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@@ -9,17 +9,17 @@ body:
|
||||
id: checkboxes
|
||||
attributes:
|
||||
label: 请您确认
|
||||
description: 在提交 Feature 之前,请确保执行过以下操作。
|
||||
description: 在提交 Feature 之前,请确认执行过以下操作。
|
||||
options:
|
||||
- label: 阅读[使用指南](https://continew.top/starter/intro/what-is.html)
|
||||
- label: 尝试 [最新版本](https://central.sonatype.com/artifact/top.continew/continew-starter/versions)(还可以尝试本地编译安装 dev 分支的最新快照版本),仍没有该功能
|
||||
required: true
|
||||
- label: 查找[常见问题](https://continew.top/faq.html)
|
||||
- label: 查阅过 [使用指南](https://continew.top/starter/guide/introduction.html) 和 [常见问题](https://continew.top/starter/faq.html) ,仍然认为很有必要
|
||||
required: true
|
||||
- label: 查看[需求墙](https://continew.top/require.html)计划
|
||||
- label: 查阅过 [需求墙](https://continew.top/starter/other/feature.html),仍没有该功能计划
|
||||
required: true
|
||||
- label: 搜索是否有其他人提交过类似的 Feature,如果对应 Feature 尚未完成,您可以先订阅关注该 Feature(为了方便后来者查找问题解决方法,请尽量避免创建重复的 Feature)
|
||||
- label: 搜索了项目 Issues,没有其他人提交过类似的 Feature(如果对应 Feature 尚未实现,您可以先订阅关注该 Issue,为了方便后来者查找问题解决方法,请避免创建重复的 Issue)
|
||||
required: true
|
||||
- label: 您是否愿意为您提出的 Feature 提交 PR?
|
||||
- label: 是否愿意为您提出的 Feature 提交 PR?
|
||||
required: false
|
||||
validations:
|
||||
required: true
|
||||
|
||||
35
CHANGELOG.md
35
CHANGELOG.md
@@ -1,3 +1,38 @@
|
||||
## [v2.11.0](https://github.com/continew-org/continew-starter/compare/v2.10.0...v2.11.0) (2025-04-13)
|
||||
|
||||
### ✨ 新特性
|
||||
|
||||
- 【web】添加 Undertow 自定义配置和默认配置,默认禁止三个不安全的 HTTP 方法(如 CONNECT、TRACE、TRACK) (Gitee#50@httpsjt) ([49b1b6a](https://github.com/continew-org/continew-starter/commit/49b1b6a69073095859c7e692f4e5908144eb4ae6)) ([08068cb](https://github.com/continew-org/continew-starter/commit/08068cb9f7ad7e56ec4df662e2c1e8bf04cb45a1))
|
||||
- 新增 continew-starter-bom 模块,用于集中管理所有子模块版本 ([5822d07](https://github.com/continew-org/continew-starter/commit/5822d073fb28c7178409010e6e4f9e78c1ae2d5a))
|
||||
- 【cache/redisson】添加缓存键前缀支持 ([615dfdd](https://github.com/continew-org/continew-starter/commit/615dfdd03fb0bc8989b8a788c9babd30709ac643))
|
||||
|
||||
### 💎 功能优化
|
||||
|
||||
- 【dependencies】采取 bom 方式来管理 JetCache 依赖 (Gitee#44@jiang4yu) ([e2d8f45](https://github.com/continew-org/continew-starter/commit/e2d8f45206a55e333c26a48c501efbb82c89beea)) ([f662b74](https://github.com/continew-org/continew-starter/commit/f662b740610da3e1ff4c0fadf2e5b2a188b06d73)) ([3e0dd83](https://github.com/continew-org/continew-starter/commit/3e0dd83e2664e57d61c37e4ea7afa618c322b984))
|
||||
|
||||
- 替换 aspectjweaver 依赖为 Spring Boot Starter AOP ([ae2b898](https://github.com/continew-org/continew-starter/commit/ae2b898e57ca8e418289a2974c92447ec191e15f))
|
||||
- 【dependencies】调整 sa-token 版本锁定为 bom 方式(PR by iang4yu) ([e242568](https://github.com/continew-org/continew-starter/commit/e24256818d716c4c2bbc50d6e7bd0df394bbbd4f))
|
||||
- 【log】访问日志过滤资源路径 (Gitee#47@dom-w) ([a6a44cd](https://github.com/continew-org/continew-starter/commit/a6a44cd46131d41f8626fe67f6ad9e4d70f1d46c))
|
||||
- 【log】重构请求和响应信息获取 (Gitee#47@dom-w) ([ca2c886](https://github.com/continew-org/continew-starter/commit/ca2c88651ff84b165ed9c9389fefb346d2be92ab))
|
||||
- 【log】修复访问日志 JSON 数组打印 (Gitee#47@dom-w) ([199a83f](https://github.com/continew-org/continew-starter/commit/199a83fbea015415484a84b5e7cb535bf804e0bc))
|
||||
- 删除多余依赖,格式化代码 (Gitee#47@dom-w) ([7e8a15a](https://github.com/continew-org/continew-starter/commit/7e8a15ae8ace650ca5ccf1b807a3149fc2d1e352))
|
||||
- 优化部分代码及方法命名,移除 SpringWebUtils 部分重复方法 ([f2ba10b](https://github.com/continew-org/continew-starter/commit/f2ba10beae9d17fad7fa260924ede5cd8ca0cbe4))
|
||||
- 调整 Spring Boot 等依赖为 bom 引入 (Gitee#49@jiang4yu) ([1d4f3a3](https://github.com/continew-org/continew-starter/commit/1d4f3a33b9ea6aa84c096b4d97cbb35220c13d86))
|
||||
- 【auth/satoken】使用 satoken 官方插件替换 Redisson 缓存 DAO 支持 ([a871048](https://github.com/continew-org/continew-starter/commit/a87104830fa1660035718c4721aa5862433dad7f))
|
||||
- 【extension/crud】重构删除接口,以解决批量删除时 URL 中 ID 列表过长问题 ([45da758](https://github.com/continew-org/continew-starter/commit/45da758dee5446f19a230c47a1f5135a86bbff63))
|
||||
- 统一配置启用属性描述 ([efaef9d](https://github.com/continew-org/continew-starter/commit/efaef9d7e6a3584e92748e33bf333a2166a3b355))
|
||||
- 【extension/crud】将 @DictField 注解重命名为 @DictModel,用于更清晰地表示字典结构映射 ([8766f11](https://github.com/continew-org/continew-starter/commit/8766f11eb2ea3c2cedc5754be4d8969298f95cb3))
|
||||
- 【log】优化日志拦截器配置 ([0463c74](https://github.com/continew-org/continew-starter/commit/0463c74aa4737db69798da00c4f1975401a6659d))
|
||||
|
||||
### 🐛 问题修复
|
||||
|
||||
- 【data/mp】修改构建本部门及以下数据权限表达式 以支持PostgreSQL (Gitee#48@httpsjt) ([5a2621a](https://github.com/continew-org/continew-starter/commit/5a2621a030b8e4f437e2c37fc4021ee661f497c7))
|
||||
- 解决部分传递依赖漏洞问题 ([1c65191](https://github.com/continew-org/continew-starter/commit/1c65191b8ab8023fb5990bd532397f3571406420))
|
||||
|
||||
### 📦 依赖升级
|
||||
|
||||
- sa-token 1.40.0 => 1.41.0 ([a871048](https://github.com/continew-org/continew-starter/commit/a87104830fa1660035718c4721aa5862433dad7f))
|
||||
|
||||
## [v2.10.0](https://github.com/continew-org/continew-starter/compare/v2.9.0...v2.10.0) (2025-03-26)
|
||||
|
||||
### ✨ 新特性
|
||||
|
||||
@@ -13,16 +13,10 @@
|
||||
<description>ContiNew Starter 认证模块 - SaToken</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- Sa-Token(轻量级 Java 权限认证框架,让鉴权变得简单、优雅) -->
|
||||
<!-- Web 模块 -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot3-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合 JWT -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-jwt</artifactId>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 缓存模块 - Redisson -->
|
||||
@@ -32,10 +26,34 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 模块 -->
|
||||
<!-- Sa-Token(轻量级 Java 权限认证框架,让鉴权变得简单、优雅) -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-web</artifactId>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot3-starter</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合 JWT -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-jwt</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 集成 Redisson 客户端 -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-redisson</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -30,7 +30,7 @@ import top.continew.starter.auth.satoken.autoconfigure.dao.SaTokenDaoProperties;
|
||||
public class SaTokenExtensionProperties {
|
||||
|
||||
/**
|
||||
* 是否启用扩展
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = false;
|
||||
|
||||
|
||||
@@ -18,12 +18,13 @@ package top.continew.starter.auth.satoken.autoconfigure.dao;
|
||||
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.dao.SaTokenDaoDefaultImpl;
|
||||
import org.redisson.client.RedisClient;
|
||||
import cn.dev33.satoken.dao.SaTokenDaoForRedisson;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -63,8 +64,8 @@ public class SaTokenDaoConfiguration {
|
||||
* 自定义持久层实现-Redis(默认)
|
||||
*/
|
||||
@ConditionalOnMissingBean(SaTokenDao.class)
|
||||
@ConditionalOnClass(RedisClient.class)
|
||||
@AutoConfigureBefore(RedissonAutoConfiguration.class)
|
||||
@ConditionalOnBean(RedissonClient.class)
|
||||
@AutoConfigureAfter(RedissonAutoConfiguration.class)
|
||||
@ConditionalOnProperty(name = "sa-token.extension.dao.type", havingValue = "redis")
|
||||
public static class Redis {
|
||||
static {
|
||||
@@ -72,8 +73,8 @@ public class SaTokenDaoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SaTokenDao saTokenDao() {
|
||||
return new SaTokenDaoRedisDefaultImpl();
|
||||
public SaTokenDao saTokenDao(RedissonClient redissonClient) {
|
||||
return new SaTokenDaoForRedisson(redissonClient);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,147 +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.auth.satoken.autoconfigure.dao;
|
||||
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import top.continew.starter.cache.redisson.util.RedisUtils;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 默认 Sa-Token 持久层 Redis 实现(参考:Sa-Token/sa-token-plugin/sa-token-dao-redisx/SaTokenDaoOfRedis.java)
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class SaTokenDaoRedisDefaultImpl implements SaTokenDao {
|
||||
|
||||
@Override
|
||||
public String get(String key) {
|
||||
return RedisUtils.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(String key, String value, long timeout) {
|
||||
if (timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
// 判断是否为永不过期
|
||||
if (timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
RedisUtils.set(key, value);
|
||||
} else {
|
||||
RedisUtils.set(key, value, Duration.ofSeconds(timeout));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(String key, String value) {
|
||||
long expire = getTimeout(key);
|
||||
// -2:无此键
|
||||
if (expire == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
this.set(key, value, expire);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String key) {
|
||||
RedisUtils.delete(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimeout(String key) {
|
||||
long timeout = RedisUtils.getTimeToLive(key);
|
||||
return timeout < 0 ? timeout : timeout / 1000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTimeout(String key, long timeout) {
|
||||
// 判断是否想要设置为永久
|
||||
if (timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
long expire = getTimeout(key);
|
||||
// 如果其已经被设置为永久,则不作任何处理。如果尚未被设置为永久,那么再次 set 一次
|
||||
if (expire != SaTokenDao.NEVER_EXPIRE) {
|
||||
this.set(key, this.get(key), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
RedisUtils.expire(key, Duration.ofSeconds(timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(String key) {
|
||||
return RedisUtils.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObject(String key, Object object, long timeout) {
|
||||
if (0 == timeout || timeout <= SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
// 判断是否为永不过期
|
||||
if (timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
RedisUtils.set(key, object);
|
||||
} else {
|
||||
RedisUtils.set(key, object, Duration.ofSeconds(timeout));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateObject(String key, Object object) {
|
||||
long expire = getObjectTimeout(key);
|
||||
// -2:无此键
|
||||
if (expire == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
this.setObject(key, object, expire);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteObject(String key) {
|
||||
RedisUtils.delete(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getObjectTimeout(String key) {
|
||||
return this.getTimeout(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateObjectTimeout(String key, long timeout) {
|
||||
// 判断是否想要设置为永久
|
||||
if (timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
long expire = getObjectTimeout(key);
|
||||
// 如果其已经被设置为永久,则不作任何处理。如果尚未被设置为永久,那么再次 set 一次
|
||||
if (expire != SaTokenDao.NEVER_EXPIRE) {
|
||||
this.setObject(key, this.getObject(key), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
RedisUtils.expire(key, Duration.ofSeconds(timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) {
|
||||
Collection<String> keys = RedisUtils.keys("%s*%s*".formatted(prefix, keyword));
|
||||
List<String> list = new ArrayList<>(keys);
|
||||
return SaFoxUtil.searchList(list, start, size, sortType);
|
||||
}
|
||||
}
|
||||
422
continew-starter-bom/pom.xml
Normal file
422
continew-starter-bom/pom.xml
Normal file
@@ -0,0 +1,422 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-bom</artifactId>
|
||||
<version>${revision}</version>
|
||||
<packaging>pom</packaging>
|
||||
<description>ContiNew Starter BOM</description>
|
||||
|
||||
<properties>
|
||||
<revision>2.11.0</revision>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- 扩展模块 - CRUD - 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-extension-crud-core</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- 扩展模块 - CRUD - MyBatis Plus ORM 模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-extension-crud-mp</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- 扩展模块 - CRUD - MyBatis Flex ORM 模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-extension-crud-mf</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 扩展模块 - 数据权限 - 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-extension-datapermission-core</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- 扩展模块 - 数据权限 - MyBatis Plus ORM 模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-extension-datapermission-mp</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 扩展模块 - 多租户 - 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-extension-tenant-core</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- 扩展模块 - 多租户 - MyBatis Plus ORM 模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-extension-tenant-mp</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 认证模块 - JustAuth -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-auth-justauth</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 认证模块 - SaToken -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-auth-satoken</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 数据访问模块 - MyBatis Plus -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-data-mp</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 数据访问模块 - MyBatis Flex -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-data-mf</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 数据访问模块 - 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-data-core</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 缓存模块 - JetCache -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-cache-jetcache</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 缓存模块 - Spring Cache -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-cache-springcache</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 缓存模块 - Redisson -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-cache-redisson</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 消息模块 - WebSocket -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-messaging-websocket</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 消息模块 - 邮件 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-messaging-mail</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 验证码模块 - 行为验证码 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-captcha-behavior</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 验证码模块 - 图形验证码 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-captcha-graphic</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 文件处理模块 - Excel -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-file-excel</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 存储模块 - 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-storage-core</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 存储模块 - 本地存储 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-storage-local</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 存储模块 - 对象存储 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-storage-oss</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 日志模块 - 基于拦截器实现(Spring Boot Actuator HttpTrace 增强版) -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-log-interceptor</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 日志模块 - 基于 AOP 实现 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-log-aop</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 日志模块 - 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-log-core</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 链路追踪模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-trace</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 幂等模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-idempotent</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 限流模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-ratelimiter</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 安全模块 - XSS 过滤 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-security-xss</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 安全模块 - 敏感词 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-security-sensitivewords</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 安全模块 - 加密 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-security-crypto</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 安全模块 - 脱敏 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-security-mask</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 安全模块 - 密码编码器 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-security-password</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-web</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- API 文档模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-api-doc</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JSON 模块 - Jackson -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-json-jackson</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-core</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- 统一版本号插件 -->
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>flatten-maven-plugin</artifactId>
|
||||
<version>1.7.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<!-- Create an effective POM (with versions expanded) for the CLI and documentation -->
|
||||
<id>flatten-effective-pom</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>flatten</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<updatePomFile>false</updatePomFile>
|
||||
<outputDirectory>${project.build.directory}/effective-pom</outputDirectory>
|
||||
<flattenedPomFilename>continew-starter-dependencies.xml</flattenedPomFilename>
|
||||
<flattenMode>oss</flattenMode>
|
||||
<pomElements>
|
||||
<dependencyManagement>expand</dependencyManagement>
|
||||
<pluginManagement>expand</pluginManagement>
|
||||
<properties>remove</properties>
|
||||
<repositories>remove</repositories>
|
||||
</pomElements>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<!-- Flatten and simplify our own POM for install/deploy -->
|
||||
<id>flatten</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>flatten</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<updatePomFile>true</updatePomFile>
|
||||
<flattenMode>bom</flattenMode>
|
||||
<pomElements>
|
||||
<parent>expand</parent>
|
||||
<properties>keep</properties>
|
||||
<pluginManagement>keep</pluginManagement>
|
||||
<repositories>remove</repositories>
|
||||
</pomElements>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>flatten-clean</id>
|
||||
<phase>clean</phase>
|
||||
<goals>
|
||||
<goal>clean</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.sonatype.central</groupId>
|
||||
<artifactId>central-publishing-maven-plugin</artifactId>
|
||||
<version>0.4.0</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<publishingServerId>central</publishingServerId>
|
||||
<tokenAuth>true</tokenAuth>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<url>https://github.com/continew-org/continew-starter</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>GNU LESSER GENERAL PUBLIC LICENSE</name>
|
||||
<url>http://www.gnu.org/licenses/lgpl.html</url>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>charles7c</id>
|
||||
<name>Charles7c</name>
|
||||
<email>charles7c@126.com</email>
|
||||
<roles>
|
||||
<role>Creator</role>
|
||||
<role>Java Development Engineer</role>
|
||||
</roles>
|
||||
<timezone>+8</timezone>
|
||||
<url>https://github.com/Charles7c</url>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<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>
|
||||
</project>
|
||||
@@ -29,6 +29,12 @@
|
||||
<dependency>
|
||||
<groupId>com.alicp.jetcache</groupId>
|
||||
<artifactId>jetcache-redisson</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import top.continew.starter.cache.redisson.handler.NameMapperHandler;
|
||||
import top.continew.starter.core.constant.PropertiesConstants;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
|
||||
@@ -104,6 +105,10 @@ public class RedissonAutoConfiguration {
|
||||
if (CharSequenceUtil.isBlank(clusterServersConfig.getPassword())) {
|
||||
clusterServersConfig.setPassword(redisProperties.getPassword());
|
||||
}
|
||||
// Key 前缀
|
||||
if (CharSequenceUtil.isNotBlank(properties.getKeyPrefix())) {
|
||||
clusterServersConfig.setNameMapper(new NameMapperHandler(properties.getKeyPrefix()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,6 +135,10 @@ public class RedissonAutoConfiguration {
|
||||
if (CharSequenceUtil.isBlank(sentinelServersConfig.getMasterName())) {
|
||||
sentinelServersConfig.setMasterName(redisProperties.getSentinel().getMaster());
|
||||
}
|
||||
// Key 前缀
|
||||
if (CharSequenceUtil.isNotBlank(properties.getKeyPrefix())) {
|
||||
sentinelServersConfig.setNameMapper(new NameMapperHandler(properties.getKeyPrefix()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,5 +162,9 @@ public class RedissonAutoConfiguration {
|
||||
singleServerConfig.setAddress(protocolPrefix + redisProperties
|
||||
.getHost() + StringConstants.COLON + redisProperties.getPort());
|
||||
}
|
||||
// Key 前缀
|
||||
if (CharSequenceUtil.isNotBlank(properties.getKeyPrefix())) {
|
||||
singleServerConfig.setNameMapper(new NameMapperHandler(properties.getKeyPrefix()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,10 +33,15 @@ import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||
public class RedissonProperties {
|
||||
|
||||
/**
|
||||
* 是否启用 Redisson
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* 缓存键前缀
|
||||
*/
|
||||
private String keyPrefix;
|
||||
|
||||
/**
|
||||
* Redis 模式
|
||||
*/
|
||||
@@ -88,6 +93,14 @@ public class RedissonProperties {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getKeyPrefix() {
|
||||
return keyPrefix;
|
||||
}
|
||||
|
||||
public void setKeyPrefix(String keyPrefix) {
|
||||
this.keyPrefix = keyPrefix;
|
||||
}
|
||||
|
||||
public Mode getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.cache.redisson.handler;
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import org.redisson.api.NameMapper;
|
||||
|
||||
/**
|
||||
* 缓存名称映射处理器
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public class NameMapperHandler implements NameMapper {
|
||||
|
||||
private final String keyPrefix;
|
||||
|
||||
public NameMapperHandler(String keyPrefix) {
|
||||
this.keyPrefix = keyPrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String map(String name) {
|
||||
if (CharSequenceUtil.isNotBlank(name) && !name.startsWith(keyPrefix)) {
|
||||
return keyPrefix + name;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String unmap(String name) {
|
||||
if (CharSequenceUtil.isNotBlank(name) && name.startsWith(keyPrefix)) {
|
||||
return name.substring(keyPrefix.length());
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ import java.awt.*;
|
||||
public class BehaviorCaptchaProperties {
|
||||
|
||||
/**
|
||||
* 是否启用行为验证码
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import top.continew.starter.core.constant.PropertiesConstants;
|
||||
public class GraphicCaptchaProperties {
|
||||
|
||||
/**
|
||||
* 是否启用图形验证码
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- AOP(AspectJ) -->
|
||||
<!-- Spring AOP Starter -->
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Hibernate Validator -->
|
||||
|
||||
@@ -29,7 +29,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
public class MyBatisFlexExtensionProperties {
|
||||
|
||||
/**
|
||||
* 是否启用扩展
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = false;
|
||||
|
||||
@@ -57,7 +57,7 @@ public class MyBatisFlexExtensionProperties {
|
||||
public static class DataPermissionProperties {
|
||||
|
||||
/**
|
||||
* 是否启用数据权限插件
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = false;
|
||||
|
||||
@@ -76,7 +76,7 @@ public class MyBatisFlexExtensionProperties {
|
||||
public static class PaginationProperties {
|
||||
|
||||
/**
|
||||
* 是否启用分页插件
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import top.continew.starter.data.mp.autoconfigure.idgenerator.MyBatisPlusIdGener
|
||||
public class MyBatisPlusExtensionProperties {
|
||||
|
||||
/**
|
||||
* 是否启用扩展
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = false;
|
||||
|
||||
@@ -70,7 +70,7 @@ public class MyBatisPlusExtensionProperties {
|
||||
public static class PaginationProperties {
|
||||
|
||||
/**
|
||||
* 是否启用分页插件
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
|
||||
@@ -3,52 +3,22 @@
|
||||
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>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>3.3.9</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-dependencies</artifactId>
|
||||
<version>${revision}</version>
|
||||
<packaging>pom</packaging>
|
||||
<description>ContiNew Starter 依赖模块</description>
|
||||
<url>https://github.com/continew-org/continew-starter</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>GNU LESSER GENERAL PUBLIC LICENSE</name>
|
||||
<url>https://www.gnu.org/licenses/lgpl.html</url>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>charles7c</id>
|
||||
<name>Charles7c</name>
|
||||
<email>charles7c@126.com</email>
|
||||
<roles>
|
||||
<role>Creator</role>
|
||||
<role>Java Development Engineer</role>
|
||||
</roles>
|
||||
<timezone>+8</timezone>
|
||||
<url>https://github.com/Charles7c</url>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<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.10.0</revision>
|
||||
<revision>2.11.0</revision>
|
||||
<spring-boot.version>3.3.9</spring-boot.version>
|
||||
<spring-cloud.version>2023.0.5</spring-cloud.version>
|
||||
<redisson.version>3.45.0</redisson.version>
|
||||
<jetcache.version>2.7.7</jetcache.version>
|
||||
<cosid.version>2.11.0</cosid.version>
|
||||
<sa-token.version>1.40.0</sa-token.version>
|
||||
<sa-token.version>1.41.0</sa-token.version>
|
||||
<just-auth.version>1.16.7</just-auth.version>
|
||||
<mybatis-plus.version>3.5.8</mybatis-plus.version>
|
||||
<mybatis-flex.version>1.10.8</mybatis-flex.version>
|
||||
@@ -74,6 +44,10 @@
|
||||
<ip2region.version>3.3.6</ip2region.version>
|
||||
<hutool.version>5.8.36</hutool.version>
|
||||
<snakeyaml.version>2.4</snakeyaml.version>
|
||||
<!-- 解决部分传递依赖漏洞问题 -->
|
||||
<commons-beanutils.version>1.9.4</commons-beanutils.version>
|
||||
<commons-io.version>2.17.0</commons-io.version>
|
||||
<commons-compress.version>1.26.0</commons-compress.version>
|
||||
<!-- Maven Plugin Versions -->
|
||||
<flatten.version>1.7.0</flatten.version>
|
||||
<spotless.version>2.44.3</spotless.version>
|
||||
@@ -82,7 +56,16 @@
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- Spring Cloud(Spring 团队提供的微服务解决方案) -->
|
||||
<!-- Spring Boot(由 Pivotal 团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程)-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Cloud(由 Pivotal 团队提供的微服务解决方案) -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
@@ -101,26 +84,10 @@
|
||||
<!-- JetCache(一个基于 Java 的缓存系统封装,提供统一的 API 和注解来简化缓存的使用。提供了比 SpringCache 更加强大的注解,可以原生的支持 TTL、两级缓存、分布式自动刷新,还提供了 Cache 接口用于手工缓存操作) -->
|
||||
<dependency>
|
||||
<groupId>com.alicp.jetcache</groupId>
|
||||
<artifactId>jetcache-autoconfigure</artifactId>
|
||||
<artifactId>jetcache-bom</artifactId>
|
||||
<version>${jetcache.version}</version>
|
||||
</dependency>
|
||||
<!-- JetCache 注解 -->
|
||||
<dependency>
|
||||
<groupId>com.alicp.jetcache</groupId>
|
||||
<artifactId>jetcache-anno</artifactId>
|
||||
<version>${jetcache.version}</version>
|
||||
</dependency>
|
||||
<!-- JetCache Redisson 适配 -->
|
||||
<dependency>
|
||||
<groupId>com.alicp.jetcache</groupId>
|
||||
<artifactId>jetcache-redisson</artifactId>
|
||||
<version>${jetcache.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- CosId(通用、灵活、高性能的分布式 ID 生成器) -->
|
||||
@@ -143,14 +110,10 @@
|
||||
<!-- Sa-Token(轻量级 Java 权限认证框架,让鉴权变得简单、优雅) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot3-starter</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency>
|
||||
<!-- Sa-Token 整合 JWT -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-jwt</artifactId>
|
||||
<artifactId>sa-token-bom</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Just Auth(开箱即用的整合第三方登录的开源组件,脱离繁琐的第三方登录 SDK,让登录变得 So easy!) -->
|
||||
@@ -172,6 +135,17 @@
|
||||
</dependency>
|
||||
|
||||
<!-- MyBatis Plus(MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,简化开发、提高效率) -->
|
||||
<!-- 注意:
|
||||
1、此注释在升级到 mybatis-plus 3.5.9 之后的版本时才进行启用,同时删掉后续相对应的mybatis-plus依赖
|
||||
2、不升级版本可忽略不计,此处仅为了后续升级,做准备
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-bom</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||
@@ -191,13 +165,10 @@
|
||||
<!-- MyBatis Flex(MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,简化开发、提高效率) -->
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-spring-boot3-starter</artifactId>
|
||||
<version>${mybatis-flex.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-processor</artifactId>
|
||||
<artifactId>mybatis-flex-dependencies</artifactId>
|
||||
<version>${mybatis-flex.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Dynamic Datasource(基于 Spring Boot 的快速集成多数据源的启动器) -->
|
||||
@@ -380,266 +351,32 @@
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 解决部分传递依赖漏洞问题 -->
|
||||
<dependency>
|
||||
<groupId>commons-beanutils</groupId>
|
||||
<artifactId>commons-beanutils</artifactId>
|
||||
<version>${commons-beanutils.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>${commons-io.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
<version>${commons-compress.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- ContiNew Starter 依赖 -->
|
||||
<!-- 扩展模块 - CRUD - 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-extension-crud-core</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- 扩展模块 - CRUD - MyBatis Plus ORM 模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-extension-crud-mp</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- 扩展模块 - CRUD - MyBatis Flex ORM 模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-extension-crud-mf</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 扩展模块 - 数据权限 - 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-extension-datapermission-core</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- 扩展模块 - 数据权限 - MyBatis Plus ORM 模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-extension-datapermission-mp</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 扩展模块 - 多租户 - 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-extension-tenant-core</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- 扩展模块 - 多租户 - MyBatis Plus ORM 模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-extension-tenant-mp</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 认证模块 - JustAuth -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-auth-justauth</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 认证模块 - SaToken -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-auth-satoken</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 数据访问模块 - MyBatis Plus -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-data-mp</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 数据访问模块 - MyBatis Flex -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-data-mf</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 数据访问模块 - 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-data-core</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 缓存模块 - JetCache -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-cache-jetcache</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 缓存模块 - Spring Cache -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-cache-springcache</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 缓存模块 - Redisson -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-cache-redisson</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 消息模块 - WebSocket -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-messaging-websocket</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 消息模块 - 邮件 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-messaging-mail</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 验证码模块 - 行为验证码 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-captcha-behavior</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 验证码模块 - 图形验证码 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-captcha-graphic</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 文件处理模块 - Excel -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-file-excel</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 存储模块 - 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-storage-core</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 存储模块 - 本地存储 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-storage-local</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 存储模块 - 对象存储 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-storage-oss</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 日志模块 - 基于拦截器实现(Spring Boot Actuator HttpTrace 增强版) -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-log-interceptor</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 日志模块 - 基于 AOP 实现 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-log-aop</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 日志模块 - 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-log-core</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 链路追踪模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-trace</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 幂等模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-idempotent</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 限流模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-ratelimiter</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 安全模块 - XSS 过滤 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-security-xss</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 安全模块 - 敏感词 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-security-sensitivewords</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 安全模块 - 加密 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-security-crypto</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 安全模块 - 脱敏 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-security-mask</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 安全模块 - 密码编码器 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-security-password</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-web</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- API 文档模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-api-doc</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JSON 模块 - Jackson -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-json-jackson</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-core</artifactId>
|
||||
<artifactId>continew-starter-bom</artifactId>
|
||||
<version>${revision}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
@@ -888,4 +625,30 @@
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<url>https://github.com/continew-org/continew-starter</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>GNU LESSER GENERAL PUBLIC LICENSE</name>
|
||||
<url>https://www.gnu.org/licenses/lgpl.html</url>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>charles7c</id>
|
||||
<name>Charles7c</name>
|
||||
<email>charles7c@126.com</email>
|
||||
<roles>
|
||||
<role>Creator</role>
|
||||
<role>Java Development Engineer</role>
|
||||
</roles>
|
||||
<timezone>+8</timezone>
|
||||
<url>https://github.com/Charles7c</url>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<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>
|
||||
</project>
|
||||
@@ -19,7 +19,7 @@ package top.continew.starter.extension.crud.annotation;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 字典结构字段
|
||||
* 字典结构映射
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2.1.0
|
||||
@@ -27,7 +27,7 @@ import java.lang.annotation.*;
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface DictField {
|
||||
public @interface DictModel {
|
||||
|
||||
/**
|
||||
* 标签字段名
|
||||
@@ -30,7 +30,8 @@ import top.continew.starter.extension.crud.enums.Api;
|
||||
import top.continew.starter.extension.crud.handler.CrudApiHandler;
|
||||
import top.continew.starter.extension.crud.model.query.PageQuery;
|
||||
import top.continew.starter.extension.crud.model.query.SortQuery;
|
||||
import top.continew.starter.extension.crud.model.resp.BaseIdResp;
|
||||
import top.continew.starter.extension.crud.model.req.IdsReq;
|
||||
import top.continew.starter.extension.crud.model.resp.IdResp;
|
||||
import top.continew.starter.extension.crud.model.resp.BasePageResp;
|
||||
import top.continew.starter.extension.crud.service.BaseService;
|
||||
import top.continew.starter.extension.crud.validation.CrudValidationGroup;
|
||||
@@ -123,8 +124,8 @@ public abstract class AbstractBaseController<S extends BaseService<L, D, Q, C>,
|
||||
@Operation(summary = "创建数据", description = "创建数据")
|
||||
@ResponseBody
|
||||
@PostMapping
|
||||
public BaseIdResp<Long> create(@Validated(CrudValidationGroup.Create.class) @RequestBody C req) {
|
||||
return new BaseIdResp<>(baseService.create(req));
|
||||
public IdResp<Long> create(@Validated(CrudValidationGroup.Create.class) @RequestBody C req) {
|
||||
return new IdResp<>(baseService.create(req));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,15 +146,14 @@ public abstract class AbstractBaseController<S extends BaseService<L, D, Q, C>,
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @param ids ID 列表
|
||||
* @param req 删除参数
|
||||
*/
|
||||
@CrudApi(Api.DELETE)
|
||||
@Operation(summary = "删除数据", description = "删除数据")
|
||||
@Parameter(name = "ids", description = "ID 列表", example = "1,2", in = ParameterIn.PATH)
|
||||
@ResponseBody
|
||||
@DeleteMapping("/{ids}")
|
||||
public void delete(@PathVariable("ids") List<Long> ids) {
|
||||
baseService.delete(ids);
|
||||
@DeleteMapping
|
||||
public void delete(@Validated @RequestBody IdsReq req) {
|
||||
baseService.delete(req.getIds());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,45 +14,33 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.log.http;
|
||||
package top.continew.starter.extension.crud.model.req;
|
||||
|
||||
import java.util.Map;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 可记录的 HTTP 响应信息
|
||||
* ID 请求参数
|
||||
*
|
||||
* @author Andy Wilkinson(Spring Boot Actuator)
|
||||
* @author Charles7c
|
||||
* @see RecordableHttpRequest
|
||||
* @since 1.1.0
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public interface RecordableHttpResponse {
|
||||
public class IdReq implements Serializable {
|
||||
|
||||
/**
|
||||
* 获取状态码
|
||||
*
|
||||
* @return 状态码
|
||||
* ID
|
||||
*/
|
||||
int getStatus();
|
||||
@Schema(description = "ID", example = "1")
|
||||
@NotNull(message = "ID 不能为空")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 获取响应头
|
||||
*
|
||||
* @return 响应头
|
||||
*/
|
||||
Map<String, String> getHeaders();
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取响应体
|
||||
*
|
||||
* @return 响应体
|
||||
*/
|
||||
String getBody();
|
||||
|
||||
/**
|
||||
* 获取响应参数
|
||||
*
|
||||
* @return 响应参数
|
||||
*/
|
||||
Map<String, Object> getParam();
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.extension.crud.model.req;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ID 列表请求参数
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public class IdsReq implements Serializable {
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
@Schema(description = "ID", example = "[1,2]")
|
||||
@NotEmpty(message = "ID 不能为空")
|
||||
private List<Long> ids;
|
||||
|
||||
public List<Long> getIds() {
|
||||
return ids;
|
||||
}
|
||||
|
||||
public void setIds(List<Long> ids) {
|
||||
this.ids = ids;
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ import java.io.Serializable;
|
||||
* @author Charles7c
|
||||
* @since 2.5.0
|
||||
*/
|
||||
public class BaseIdResp<T extends Serializable> implements Serializable {
|
||||
public class IdResp<T extends Serializable> implements Serializable {
|
||||
|
||||
/**
|
||||
* ID
|
||||
@@ -42,10 +42,10 @@ public class BaseIdResp<T extends Serializable> implements Serializable {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public BaseIdResp() {
|
||||
public IdResp() {
|
||||
}
|
||||
|
||||
public BaseIdResp(final T id) {
|
||||
public IdResp(final T id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,8 @@ import top.continew.starter.core.validation.ValidationUtils;
|
||||
import top.continew.starter.data.mp.base.BaseMapper;
|
||||
import top.continew.starter.data.mp.service.impl.ServiceImpl;
|
||||
import top.continew.starter.data.mp.util.QueryWrapperHelper;
|
||||
import top.continew.starter.extension.crud.annotation.DictField;
|
||||
import top.continew.starter.extension.crud.annotation.DictModel;
|
||||
import top.continew.starter.extension.crud.annotation.DictModel;
|
||||
import top.continew.starter.extension.crud.annotation.TreeField;
|
||||
import top.continew.starter.extension.crud.autoconfigure.CrudProperties;
|
||||
import top.continew.starter.extension.crud.autoconfigure.CrudTreeProperties;
|
||||
@@ -138,18 +139,18 @@ public abstract class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseIdD
|
||||
|
||||
@Override
|
||||
public List<LabelValueResp> listDict(Q query, SortQuery sortQuery) {
|
||||
DictField dictField = super.getEntityClass().getDeclaredAnnotation(DictField.class);
|
||||
CheckUtils.throwIfNull(dictField, "请添加并配置 @DictField 字典结构信息");
|
||||
DictModel dictModel = super.getEntityClass().getDeclaredAnnotation(DictModel.class);
|
||||
CheckUtils.throwIfNull(dictModel, "请添加并配置 @DictModel 字典结构信息");
|
||||
List<L> list = this.list(query, sortQuery);
|
||||
// 解析映射
|
||||
List<LabelValueResp> respList = new ArrayList<>(list.size());
|
||||
String labelKey = dictField.labelKey().contains(StringConstants.DOT)
|
||||
? CharSequenceUtil.subAfter(dictField.labelKey(), StringConstants.DOT, true)
|
||||
: dictField.labelKey();
|
||||
String valueKey = dictField.valueKey().contains(StringConstants.DOT)
|
||||
? CharSequenceUtil.subAfter(dictField.valueKey(), StringConstants.DOT, true)
|
||||
: dictField.valueKey();
|
||||
List<String> extraFieldNames = Arrays.stream(dictField.extraKeys())
|
||||
String labelKey = dictModel.labelKey().contains(StringConstants.DOT)
|
||||
? CharSequenceUtil.subAfter(dictModel.labelKey(), StringConstants.DOT, true)
|
||||
: dictModel.labelKey();
|
||||
String valueKey = dictModel.valueKey().contains(StringConstants.DOT)
|
||||
? CharSequenceUtil.subAfter(dictModel.valueKey(), StringConstants.DOT, true)
|
||||
: dictModel.valueKey();
|
||||
List<String> extraFieldNames = Arrays.stream(dictModel.extraKeys())
|
||||
.map(extraKey -> extraKey.contains(StringConstants.DOT)
|
||||
? CharSequenceUtil.subAfter(extraKey, StringConstants.DOT, true)
|
||||
: extraKey)
|
||||
@@ -165,7 +166,7 @@ public abstract class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseIdD
|
||||
continue;
|
||||
}
|
||||
// 额外数据
|
||||
Map<String, Object> extraMap = MapUtil.newHashMap(dictField.extraKeys().length);
|
||||
Map<String, Object> extraMap = MapUtil.newHashMap(dictModel.extraKeys().length);
|
||||
for (String extraFieldName : extraFieldNames) {
|
||||
extraMap.put(extraFieldName, ReflectUtil.getFieldValue(entity, extraFieldName));
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import top.continew.starter.core.constant.PropertiesConstants;
|
||||
public class DataPermissionProperties {
|
||||
|
||||
/**
|
||||
* 是否启用多租户
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
|
||||
@@ -24,5 +24,11 @@
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-extension-datapermission-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- ContiNew Starter 数据访问模块 - 核心模块-->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-data-core</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -16,36 +16,45 @@
|
||||
|
||||
package top.continew.starter.extension.datapermission.handler;
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||
import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
import net.sf.jsqlparser.expression.Function;
|
||||
import net.sf.jsqlparser.expression.LongValue;
|
||||
import net.sf.jsqlparser.expression.StringValue;
|
||||
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
|
||||
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
|
||||
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
|
||||
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
|
||||
import net.sf.jsqlparser.expression.operators.relational.InExpression;
|
||||
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
|
||||
import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;
|
||||
import net.sf.jsqlparser.schema.Column;
|
||||
import net.sf.jsqlparser.schema.Table;
|
||||
import net.sf.jsqlparser.statement.select.ParenthesedSelect;
|
||||
import net.sf.jsqlparser.statement.select.PlainSelect;
|
||||
import net.sf.jsqlparser.statement.select.SelectItem;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
import top.continew.starter.data.core.enums.DatabaseType;
|
||||
import top.continew.starter.data.core.util.MetaUtils;
|
||||
import top.continew.starter.extension.datapermission.annotation.DataPermission;
|
||||
import top.continew.starter.extension.datapermission.enums.DataScope;
|
||||
import top.continew.starter.extension.datapermission.filter.DataPermissionUserContextProvider;
|
||||
import top.continew.starter.extension.datapermission.model.RoleContext;
|
||||
import top.continew.starter.extension.datapermission.model.UserContext;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 默认数据权限处理器
|
||||
*
|
||||
@@ -57,6 +66,7 @@ public class DefaultDataPermissionHandler implements DataPermissionHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(DefaultDataPermissionHandler.class);
|
||||
private final DataPermissionUserContextProvider dataPermissionUserContextProvider;
|
||||
private static final DataSource dataSource = SpringUtil.getBean(DataSource.class);
|
||||
|
||||
public DefaultDataPermissionHandler(DataPermissionUserContextProvider dataPermissionUserContextProvider) {
|
||||
this.dataPermissionUserContextProvider = dataPermissionUserContextProvider;
|
||||
@@ -133,13 +143,34 @@ public class DefaultDataPermissionHandler implements DataPermissionHandler {
|
||||
PlainSelect select = new PlainSelect();
|
||||
select.setSelectItems(Collections.singletonList(new SelectItem<>(new Column(dataPermission.id()))));
|
||||
select.setFromItem(new Table(dataPermission.deptTableAlias()));
|
||||
|
||||
EqualsTo equalsTo = new EqualsTo();
|
||||
equalsTo.setLeftExpression(new Column(dataPermission.id()));
|
||||
equalsTo.setRightExpression(new LongValue(userContext.getDeptId()));
|
||||
Function function = new Function();
|
||||
function.setName("find_in_set");
|
||||
function.setParameters(new ExpressionList<>(new LongValue(userContext.getDeptId()), new Column("ancestors")));
|
||||
select.setWhere(new OrExpression(equalsTo, function));
|
||||
|
||||
DatabaseType databaseType = MetaUtils.getDatabaseType(dataSource);
|
||||
Expression inSetExpression;
|
||||
if (DatabaseType.MYSQL.getDatabase().equalsIgnoreCase(databaseType.getDatabase())) {
|
||||
Function findInSetFunction = new Function();
|
||||
findInSetFunction.setName("find_in_set");
|
||||
findInSetFunction.setParameters(new ExpressionList<>(new LongValue(userContext
|
||||
.getDeptId()), new StringValue(new Column("ancestors") + ",")));
|
||||
inSetExpression = findInSetFunction;
|
||||
} else if (DatabaseType.POSTGRE_SQL.getDatabase().equalsIgnoreCase(databaseType.getDatabase())) {
|
||||
// 构建 concat 函数
|
||||
Function concatFunction = new Function("concat");
|
||||
concatFunction.setParameters(new ExpressionList<>(new Column("ancestors"), new StringValue(",")));
|
||||
|
||||
// 创建 LIKE 函数
|
||||
LikeExpression likeExpression = new LikeExpression();
|
||||
likeExpression.setLeftExpression(concatFunction);
|
||||
likeExpression.setRightExpression(new StringValue("%," + userContext.getDeptId() + ",%"));
|
||||
inSetExpression = likeExpression;
|
||||
} else {
|
||||
throw new IllegalArgumentException("暂不支持 [%s] 数据权限".formatted(""));
|
||||
}
|
||||
|
||||
select.setWhere(new OrExpression(equalsTo, inSetExpression));
|
||||
subSelect.setSelect(select);
|
||||
// 构建父查询
|
||||
InExpression inExpression = new InExpression();
|
||||
|
||||
@@ -32,7 +32,7 @@ import java.util.List;
|
||||
public class TenantProperties {
|
||||
|
||||
/**
|
||||
* 是否启用多租户
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
|
||||
@@ -13,11 +13,6 @@
|
||||
<description>ContiNew Starter 幂等模块</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 缓存模块 - Redisson -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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.json.jackson.util;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* json 构建工具
|
||||
*
|
||||
* @author echo
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public class JSONBuilder {
|
||||
|
||||
private static final ObjectMapper OBJECT_MAPPER = SpringUtil.getBean(ObjectMapper.class);
|
||||
private final ObjectNode rootNode;
|
||||
|
||||
private JSONBuilder() {
|
||||
this.rootNode = OBJECT_MAPPER.createObjectNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始构建
|
||||
*
|
||||
* @return {@link JSONBuilder }
|
||||
*/
|
||||
public static JSONBuilder builder() {
|
||||
return new JSONBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 字符串
|
||||
*
|
||||
* @param key key 值
|
||||
* @param value 值
|
||||
* @return {@link JSONBuilder }
|
||||
*/
|
||||
public JSONBuilder add(String key, String value) {
|
||||
Objects.requireNonNull(key, "键不能为 null");
|
||||
if (value != null) {
|
||||
rootNode.put(key, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 int
|
||||
*
|
||||
* @param key key 值
|
||||
* @param value 值
|
||||
* @return {@link JSONBuilder }
|
||||
*/
|
||||
public JSONBuilder add(String key, int value) {
|
||||
Objects.requireNonNull(key, "键不能为 null");
|
||||
rootNode.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 long
|
||||
*
|
||||
* @param key key 值
|
||||
* @param value 值
|
||||
* @return {@link JSONBuilder }
|
||||
*/
|
||||
public JSONBuilder add(String key, long value) {
|
||||
Objects.requireNonNull(key, "键不能为 null");
|
||||
rootNode.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 布尔
|
||||
*
|
||||
* @param key key 值
|
||||
* @param value 值
|
||||
* @return {@link JSONBuilder }
|
||||
*/
|
||||
public JSONBuilder add(String key, boolean value) {
|
||||
Objects.requireNonNull(key, "键不能为 null");
|
||||
rootNode.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 浮点
|
||||
*
|
||||
* @param key key 值
|
||||
* @param value 值
|
||||
* @return {@link JSONBuilder }
|
||||
*/
|
||||
public JSONBuilder add(String key, double value) {
|
||||
Objects.requireNonNull(key, "键不能为 null");
|
||||
rootNode.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 json
|
||||
*
|
||||
* @param key key 值
|
||||
* @param value 值
|
||||
* @return {@link JSONBuilder }
|
||||
*/
|
||||
public JSONBuilder add(String key, JsonNode value) {
|
||||
Objects.requireNonNull(key, "键不能为 null");
|
||||
if (value != null) {
|
||||
rootNode.set(key, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 Object
|
||||
*
|
||||
* @param key key 值
|
||||
* @param value 值
|
||||
* @return {@link JSONBuilder }
|
||||
*/
|
||||
public JSONBuilder add(String key, Object value) {
|
||||
Objects.requireNonNull(key, "键不能为 null");
|
||||
if (value != null) {
|
||||
rootNode.set(key, OBJECT_MAPPER.valueToTree(value));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 List 到 JSON
|
||||
*
|
||||
* @param key key 值
|
||||
* @param list list 参数
|
||||
* @return {@link JSONBuilder }
|
||||
*/
|
||||
public JSONBuilder add(String key, List<?> list) {
|
||||
Objects.requireNonNull(key, "键不能为 null");
|
||||
if (list != null) {
|
||||
ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode();
|
||||
for (Object item : list) {
|
||||
arrayNode.add(OBJECT_MAPPER.valueToTree(item));
|
||||
}
|
||||
rootNode.set(key, arrayNode);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 Map 到 JSON
|
||||
*
|
||||
* @param key key 值
|
||||
* @param map map 参数
|
||||
* @return {@link JSONBuilder }
|
||||
*/
|
||||
public JSONBuilder add(String key, Map<?, ?> map) {
|
||||
Objects.requireNonNull(key, "键不能为 null");
|
||||
if (map != null) {
|
||||
ObjectNode objectNode = OBJECT_MAPPER.valueToTree(map);
|
||||
rootNode.set(key, objectNode);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建
|
||||
*
|
||||
* @return {@link JsonNode }
|
||||
*/
|
||||
public JsonNode build() {
|
||||
return rootNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 json 字符串
|
||||
*
|
||||
* @return {@link String }
|
||||
*/
|
||||
public String buildString() {
|
||||
try {
|
||||
return rootNode.toString();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("构建 JSON 字符串失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* 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.json.jackson.util;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* json 工具
|
||||
*
|
||||
* @author echo
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public class JSONUtils {
|
||||
|
||||
private JSONUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Jackson 对象映射器,用于 JSON 解析与序列化。
|
||||
*/
|
||||
private static final ObjectMapper OBJECT_MAPPER = SpringUtil.getBean(ObjectMapper.class);
|
||||
|
||||
/**
|
||||
* 获取 Jackson 对象映射器。
|
||||
*
|
||||
* @return {@link ObjectMapper} Jackson 对象映射器
|
||||
*/
|
||||
public static ObjectMapper getObjectMapper() {
|
||||
return OBJECT_MAPPER;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对象转为 json 字符串
|
||||
*
|
||||
* @param object 对象
|
||||
* @return {@link String }
|
||||
*/
|
||||
public static String toJsonStr(Object object) {
|
||||
if (ObjectUtil.isNull(object)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return OBJECT_MAPPER.writeValueAsString(object);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象转换为 JsonNode。
|
||||
*
|
||||
* @param obj 需要转换的对象
|
||||
* @return 转换后的 {@link JsonNode},如果 obj 为空,则返回 null
|
||||
*/
|
||||
public static JsonNode toJson(Object obj) {
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return OBJECT_MAPPER.valueToTree(obj);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 List 转换为 JsonNode。
|
||||
*
|
||||
* @param list 输入的 List
|
||||
* @return 转换后的 {@link JsonNode}
|
||||
*/
|
||||
public static JsonNode listToJson(List<?> list) {
|
||||
return toJson(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Map 转换为 JsonNode。
|
||||
*
|
||||
* @param map 输入的 Map
|
||||
* @return 转换后的 {@link JsonNode}
|
||||
*/
|
||||
public static JsonNode mapToJson(Map<?, ?> map) {
|
||||
return toJson(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 JsonNode 转换为 List<String>,用于环境变量格式解析。
|
||||
*
|
||||
* @param jsonNode 需要转换的 JsonNode
|
||||
* @return 转换后的 List<String>
|
||||
*/
|
||||
public static List<String> jsonToEnvList(JsonNode jsonNode) {
|
||||
if (jsonNode == null || jsonNode.isNull()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<String> envList = new ArrayList<>();
|
||||
jsonNode.fields().forEachRemaining(field -> {
|
||||
String key = field.getKey();
|
||||
JsonNode valueNode = field.getValue();
|
||||
String value = valueNode.isValueNode() ? valueNode.asText() : valueNode.toString();
|
||||
envList.add(key + "=" + value);
|
||||
});
|
||||
return envList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 JsonNode 转换为 List<String>。
|
||||
*
|
||||
* @param jsonNode 需要转换的 JsonNode
|
||||
* @return 转换后的 List<String>
|
||||
*/
|
||||
public static List<String> jsonToStringList(JsonNode jsonNode) {
|
||||
if (jsonNode == null || jsonNode.isNull()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
try {
|
||||
return OBJECT_MAPPER.convertValue(jsonNode, new TypeReference<>() {
|
||||
});
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 JsonNode 转换为指定类型的 Java 对象。
|
||||
*
|
||||
* @param jsonNode JSON 数据
|
||||
* @param clazz 目标 Java 类
|
||||
* @return 解析后的 Java 对象
|
||||
*/
|
||||
public static <T> T fromJson(JsonNode jsonNode, Class<T> clazz) {
|
||||
try {
|
||||
return OBJECT_MAPPER.treeToValue(jsonNode, clazz);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 JSON 字符串为 Java 对象。
|
||||
*
|
||||
* @param str JSON 字符串
|
||||
* @param clazz 目标 Java 类
|
||||
* @return 解析后的 Java 对象
|
||||
*/
|
||||
public static <T> T parseObject(String str, Class<T> clazz) {
|
||||
if (StrUtil.isEmpty(str)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return OBJECT_MAPPER.readValue(str, clazz);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串 解析为 list<T>
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param clazz 目标 Java 类
|
||||
* @return 解析后的 List<T>
|
||||
*/
|
||||
public static <T> List<T> parseArray(String str, Class<T> clazz) {
|
||||
if (StrUtil.isEmpty(str)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
try {
|
||||
return OBJECT_MAPPER.readValue(str, OBJECT_MAPPER.getTypeFactory()
|
||||
.constructCollectionType(List.class, clazz));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字符串是否为 JSON 格式。
|
||||
*
|
||||
* @param str 字符串
|
||||
* @return 是否为 JSON 格式
|
||||
*/
|
||||
public static boolean isTypeJSON(String str) {
|
||||
if (StrUtil.isEmpty(str)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
OBJECT_MAPPER.readTree(str);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 JSON 字符串转换为指定类型的 Java 对象。
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param clazz 目标对象的 Class 类型
|
||||
* @return 解析后的 Java 对象
|
||||
*/
|
||||
public static <T> T toBean(String str, Class<T> clazz) {
|
||||
if (StrUtil.isEmpty(str)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return OBJECT_MAPPER.readValue(str, clazz);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package top.continew.starter.log.aspect;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
@@ -25,8 +23,6 @@ import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import top.continew.starter.log.handler.LogHandler;
|
||||
import top.continew.starter.log.http.servlet.RecordableServletHttpRequest;
|
||||
import top.continew.starter.log.http.servlet.RecordableServletHttpResponse;
|
||||
import top.continew.starter.log.model.AccessLogContext;
|
||||
import top.continew.starter.log.model.LogProperties;
|
||||
|
||||
@@ -107,22 +103,16 @@ public class AccessLogAspect {
|
||||
if (attributes == null) {
|
||||
return joinPoint.proceed();
|
||||
}
|
||||
HttpServletRequest request = attributes.getRequest();
|
||||
HttpServletResponse response = attributes.getResponse();
|
||||
try {
|
||||
// 开始访问日志记录
|
||||
logHandler.accessLogStart(AccessLogContext.builder()
|
||||
.startTime(startTime)
|
||||
.request(new RecordableServletHttpRequest(request))
|
||||
.properties(logProperties)
|
||||
.build());
|
||||
return joinPoint.proceed();
|
||||
} finally {
|
||||
Instant endTime = Instant.now();
|
||||
logHandler.accessLogFinish(AccessLogContext.builder()
|
||||
.endTime(endTime)
|
||||
.response(new RecordableServletHttpResponse(response, response.getStatus()))
|
||||
.build());
|
||||
logHandler.accessLogFinish(AccessLogContext.builder().endTime(endTime).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@ package top.continew.starter.log.aspect;
|
||||
|
||||
import cn.hutool.core.annotation.AnnotationUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
@@ -35,7 +33,6 @@ import top.continew.starter.log.dao.LogDao;
|
||||
import top.continew.starter.log.handler.LogHandler;
|
||||
import top.continew.starter.log.model.LogProperties;
|
||||
import top.continew.starter.log.model.LogRecord;
|
||||
import top.continew.starter.web.util.SpringWebUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.Instant;
|
||||
@@ -79,7 +76,6 @@ public class LogAspect {
|
||||
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
Instant startTime = Instant.now();
|
||||
// 指定规则不记录
|
||||
HttpServletRequest request = SpringWebUtils.getRequest();
|
||||
Method targetMethod = this.getMethod(joinPoint);
|
||||
Class<?> targetClass = joinPoint.getTarget().getClass();
|
||||
if (!isRequestRecord(targetMethod, targetClass)) {
|
||||
@@ -87,7 +83,7 @@ public class LogAspect {
|
||||
}
|
||||
String errorMsg = null;
|
||||
// 开始记录
|
||||
LogRecord.Started startedLogRecord = logHandler.start(startTime, request);
|
||||
LogRecord.Started startedLogRecord = logHandler.start(startTime);
|
||||
try {
|
||||
// 执行目标方法
|
||||
return joinPoint.proceed();
|
||||
@@ -97,8 +93,7 @@ public class LogAspect {
|
||||
} finally {
|
||||
try {
|
||||
Instant endTime = Instant.now();
|
||||
HttpServletResponse response = SpringWebUtils.getResponse();
|
||||
LogRecord logRecord = logHandler.finish(startedLogRecord, endTime, response, logProperties
|
||||
LogRecord logRecord = logHandler.finish(startedLogRecord, endTime, logProperties
|
||||
.getIncludes(), targetMethod, targetClass);
|
||||
// 记录异常信息
|
||||
if (errorMsg != null) {
|
||||
|
||||
@@ -22,20 +22,15 @@ import cn.hutool.core.util.ObjectUtil;
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import top.continew.starter.log.annotation.Log;
|
||||
import top.continew.starter.log.enums.Include;
|
||||
import top.continew.starter.log.http.RecordableHttpRequest;
|
||||
import top.continew.starter.log.http.RecordableHttpResponse;
|
||||
import top.continew.starter.log.http.servlet.RecordableServletHttpRequest;
|
||||
import top.continew.starter.log.http.servlet.RecordableServletHttpResponse;
|
||||
import top.continew.starter.log.model.AccessLogContext;
|
||||
import top.continew.starter.log.model.AccessLogProperties;
|
||||
import top.continew.starter.log.model.LogRecord;
|
||||
import top.continew.starter.log.util.AccessLogUtils;
|
||||
import top.continew.starter.web.util.ServletUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.Duration;
|
||||
@@ -55,19 +50,18 @@ public abstract class AbstractLogHandler implements LogHandler {
|
||||
private final TransmittableThreadLocal<AccessLogContext> logContextThread = new TransmittableThreadLocal<>();
|
||||
|
||||
@Override
|
||||
public LogRecord.Started start(Instant startTime, HttpServletRequest request) {
|
||||
return LogRecord.start(startTime, new RecordableServletHttpRequest(request));
|
||||
public LogRecord.Started start(Instant startTime) {
|
||||
return LogRecord.start(startTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LogRecord finish(LogRecord.Started started,
|
||||
Instant endTime,
|
||||
HttpServletResponse response,
|
||||
Set<Include> includes,
|
||||
Method targetMethod,
|
||||
Class<?> targetClass) {
|
||||
Set<Include> includeSet = this.getIncludes(includes, targetMethod, targetClass);
|
||||
LogRecord logRecord = this.finish(started, endTime, response, includeSet);
|
||||
LogRecord logRecord = this.finish(started, endTime, includeSet);
|
||||
// 记录日志描述
|
||||
if (includeSet.contains(Include.DESCRIPTION)) {
|
||||
this.logDescription(logRecord, targetMethod);
|
||||
@@ -80,11 +74,8 @@ public abstract class AbstractLogHandler implements LogHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public LogRecord finish(LogRecord.Started started,
|
||||
Instant endTime,
|
||||
HttpServletResponse response,
|
||||
Set<Include> includes) {
|
||||
return started.finish(endTime, new RecordableServletHttpResponse(response, response.getStatus()), includes);
|
||||
public LogRecord finish(LogRecord.Started started, Instant endTime, Set<Include> includes) {
|
||||
return started.finish(endTime, includes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -174,16 +165,15 @@ public abstract class AbstractLogHandler implements LogHandler {
|
||||
public void accessLogStart(AccessLogContext accessLogContext) {
|
||||
AccessLogProperties properties = accessLogContext.getProperties().getAccessLog();
|
||||
// 是否需要打印 规则: 是否打印开关 或 放行路径
|
||||
if (!properties.isEnabled() || accessLogContext.getProperties()
|
||||
.isMatch(accessLogContext.getRequest().getPath())) {
|
||||
if (!properties.isEnabled() || AccessLogUtils.exclusionPath(accessLogContext.getProperties(), ServletUtils
|
||||
.getRequestPath())) {
|
||||
return;
|
||||
}
|
||||
// 构建上下文
|
||||
logContextThread.set(accessLogContext);
|
||||
RecordableHttpRequest request = accessLogContext.getRequest();
|
||||
String path = request.getPath();
|
||||
String param = AccessLogUtils.getParam(request, properties);
|
||||
log.info(param != null ? "[Start] [{}] {} param: {}" : "[Start] [{}] {}", request.getMethod(), path, param);
|
||||
String param = AccessLogUtils.getParam(properties);
|
||||
log.info(param != null ? "[Start] [{}] {} param: {}" : "[Start] [{}] {}", ServletUtils
|
||||
.getRequestMethod(), ServletUtils.getRequestPath(), param);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -193,11 +183,9 @@ public abstract class AbstractLogHandler implements LogHandler {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
RecordableHttpRequest request = logContext.getRequest();
|
||||
RecordableHttpResponse response = accessLogContext.getResponse();
|
||||
Duration timeTaken = Duration.between(logContext.getStartTime(), accessLogContext.getEndTime());
|
||||
log.info("[End] [{}] {} {} {}ms", request.getMethod(), request.getPath(), response.getStatus(), timeTaken
|
||||
.toMillis());
|
||||
log.info("[End] [{}] {} {} {}ms", ServletUtils.getRequestMethod(), ServletUtils
|
||||
.getRequestPath(), ServletUtils.getResponseStatus(), timeTaken.toMillis());
|
||||
} finally {
|
||||
logContextThread.remove();
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package top.continew.starter.log.handler;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import top.continew.starter.log.enums.Include;
|
||||
import top.continew.starter.log.model.AccessLogContext;
|
||||
import top.continew.starter.log.model.LogRecord;
|
||||
@@ -39,28 +37,25 @@ public interface LogHandler {
|
||||
* 开始日志记录
|
||||
*
|
||||
* @param startTime 开始时间
|
||||
* @param request 请求对象
|
||||
* @return 日志记录器
|
||||
*/
|
||||
LogRecord.Started start(Instant startTime, HttpServletRequest request);
|
||||
LogRecord.Started start(Instant startTime);
|
||||
|
||||
/**
|
||||
* 结束日志记录
|
||||
*
|
||||
* @param started 开始日志记录器
|
||||
* @param endTime 结束时间
|
||||
* @param response 响应对象
|
||||
* @param includes 包含信息
|
||||
* @return 日志记录
|
||||
*/
|
||||
LogRecord finish(LogRecord.Started started, Instant endTime, HttpServletResponse response, Set<Include> includes);
|
||||
LogRecord finish(LogRecord.Started started, Instant endTime, Set<Include> includes);
|
||||
|
||||
/**
|
||||
* 结束日志记录
|
||||
*
|
||||
* @param started 开始日志记录器-
|
||||
* @param endTime 结束时间
|
||||
* @param response 响应对象
|
||||
* @param includes 包含信息
|
||||
* @param targetMethod 目标方法
|
||||
* @param targetClass 目标类
|
||||
@@ -68,7 +63,6 @@ public interface LogHandler {
|
||||
*/
|
||||
LogRecord finish(LogRecord.Started started,
|
||||
Instant endTime,
|
||||
HttpServletResponse response,
|
||||
Set<Include> includes,
|
||||
Method targetMethod,
|
||||
Class<?> targetClass);
|
||||
|
||||
@@ -1,84 +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.log.http;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 可记录的 HTTP 请求信息
|
||||
*
|
||||
* @author Andy Wilkinson(Spring Boot Actuator)
|
||||
* @author Phillip Webb(Spring Boot Actuator)
|
||||
* @author Charles7c
|
||||
* @author echo
|
||||
* @see RecordableHttpResponse
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public interface RecordableHttpRequest {
|
||||
|
||||
/**
|
||||
* 获取请求方式
|
||||
*
|
||||
* @return 请求方式
|
||||
*/
|
||||
String getMethod();
|
||||
|
||||
/**
|
||||
* 获取 URL
|
||||
*
|
||||
* @return URL
|
||||
*/
|
||||
URI getUrl();
|
||||
|
||||
/**
|
||||
* 获取路径
|
||||
* <p>/foo/bar</p>
|
||||
*
|
||||
* @return 路径
|
||||
* @since 2.10.0
|
||||
*/
|
||||
String getPath();
|
||||
|
||||
/**
|
||||
* 获取请求头
|
||||
*
|
||||
* @return 请求头
|
||||
*/
|
||||
Map<String, String> getHeaders();
|
||||
|
||||
/**
|
||||
* 获取请求体
|
||||
*
|
||||
* @return 请求体
|
||||
*/
|
||||
String getBody();
|
||||
|
||||
/**
|
||||
* 获取请求参数
|
||||
*
|
||||
* @return 请求参数
|
||||
*/
|
||||
Map<String, Object> getParam();
|
||||
|
||||
/**
|
||||
* 获取 IP
|
||||
*
|
||||
* @return IP
|
||||
*/
|
||||
String getIp();
|
||||
}
|
||||
@@ -1,108 +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.log.http.servlet;
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.extra.servlet.JakartaServletUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.web.util.UriUtils;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
import top.continew.starter.log.http.RecordableHttpRequest;
|
||||
import top.continew.starter.web.util.RepeatReadRequestWrapper;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 可记录的 HTTP 请求信息适配器
|
||||
*
|
||||
* @author Andy Wilkinson(Spring Boot Actuator)
|
||||
* @author Charles7c
|
||||
* @author echo
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public final class RecordableServletHttpRequest implements RecordableHttpRequest {
|
||||
|
||||
private final HttpServletRequest request;
|
||||
|
||||
public RecordableServletHttpRequest(HttpServletRequest request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethod() {
|
||||
return request.getMethod();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getUrl() {
|
||||
String queryString = request.getQueryString();
|
||||
if (CharSequenceUtil.isBlank(queryString)) {
|
||||
return URI.create(request.getRequestURL().toString());
|
||||
}
|
||||
try {
|
||||
StringBuilder urlBuilder = this.appendQueryString(queryString);
|
||||
return new URI(urlBuilder.toString());
|
||||
} catch (URISyntaxException e) {
|
||||
String encoded = UriUtils.encodeQuery(queryString, StandardCharsets.UTF_8);
|
||||
StringBuilder urlBuilder = this.appendQueryString(encoded);
|
||||
return URI.create(urlBuilder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return request.getRequestURI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getHeaders() {
|
||||
return JakartaServletUtil.getHeaderMap(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBody() {
|
||||
if (request instanceof RepeatReadRequestWrapper wrapper && !wrapper.isMultipartContent(request)) {
|
||||
String body = JakartaServletUtil.getBody(request);
|
||||
return JSONUtil.isTypeJSON(body) ? body : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getParam() {
|
||||
String body = this.getBody();
|
||||
return CharSequenceUtil.isNotBlank(body) && JSONUtil.isTypeJSON(body)
|
||||
? JSONUtil.toBean(body, Map.class)
|
||||
: Collections.unmodifiableMap(JakartaServletUtil.getParamMap(request));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIp() {
|
||||
return JakartaServletUtil.getClientIP(request);
|
||||
}
|
||||
|
||||
private StringBuilder appendQueryString(String queryString) {
|
||||
return new StringBuilder().append(request.getRequestURL())
|
||||
.append(StringConstants.QUESTION_MARK)
|
||||
.append(queryString);
|
||||
}
|
||||
}
|
||||
@@ -1,71 +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.log.http.servlet;
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import top.continew.starter.web.util.ServletUtils;
|
||||
import top.continew.starter.web.util.RepeatReadResponseWrapper;
|
||||
import top.continew.starter.log.http.RecordableHttpResponse;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 可记录的 HTTP 响应信息适配器
|
||||
*
|
||||
* @author Andy Wilkinson(Spring Boot Actuator)
|
||||
* @author Charles7c
|
||||
* @author echo
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public final class RecordableServletHttpResponse implements RecordableHttpResponse {
|
||||
|
||||
private final HttpServletResponse response;
|
||||
|
||||
private final int status;
|
||||
|
||||
public RecordableServletHttpResponse(HttpServletResponse response, int status) {
|
||||
this.response = response;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getHeaders() {
|
||||
return ServletUtils.getHeaderMap(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBody() {
|
||||
if (response instanceof RepeatReadResponseWrapper wrapper && !wrapper.isStreamingResponse()) {
|
||||
String body = wrapper.getResponseContent();
|
||||
return JSONUtil.isTypeJSON(body) ? body : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getParam() {
|
||||
String body = this.getBody();
|
||||
return CharSequenceUtil.isNotBlank(body) && JSONUtil.isTypeJSON(body) ? JSONUtil.toBean(body, Map.class) : null;
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
package top.continew.starter.log.model;
|
||||
|
||||
import top.continew.starter.log.http.RecordableHttpRequest;
|
||||
import top.continew.starter.log.http.RecordableHttpResponse;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
@@ -39,16 +36,6 @@ public class AccessLogContext {
|
||||
*/
|
||||
private Instant endTime;
|
||||
|
||||
/**
|
||||
* 请求信息
|
||||
*/
|
||||
private final RecordableHttpRequest request;
|
||||
|
||||
/**
|
||||
* 响应信息
|
||||
*/
|
||||
private final RecordableHttpResponse response;
|
||||
|
||||
/**
|
||||
* 配置信息
|
||||
*/
|
||||
@@ -57,8 +44,6 @@ public class AccessLogContext {
|
||||
private AccessLogContext(Builder builder) {
|
||||
this.startTime = builder.startTime;
|
||||
this.endTime = builder.endTime;
|
||||
this.request = builder.request;
|
||||
this.response = builder.response;
|
||||
this.properties = builder.properties;
|
||||
}
|
||||
|
||||
@@ -70,14 +55,6 @@ public class AccessLogContext {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
public RecordableHttpRequest getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
public RecordableHttpResponse getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public LogProperties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
@@ -97,8 +74,6 @@ public class AccessLogContext {
|
||||
|
||||
private Instant startTime;
|
||||
private Instant endTime;
|
||||
private RecordableHttpRequest request;
|
||||
private RecordableHttpResponse response;
|
||||
private LogProperties properties;
|
||||
|
||||
private Builder() {
|
||||
@@ -114,16 +89,6 @@ public class AccessLogContext {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder request(RecordableHttpRequest request) {
|
||||
this.request = request;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder response(RecordableHttpResponse response) {
|
||||
this.response = response;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder properties(LogProperties properties) {
|
||||
this.properties = properties;
|
||||
return this;
|
||||
|
||||
@@ -29,7 +29,7 @@ import java.util.List;
|
||||
public class AccessLogProperties {
|
||||
|
||||
/**
|
||||
* 是否打印访问日志(类似于 Nginx access log)
|
||||
* 是否启用
|
||||
* <p>
|
||||
* 不记录请求日志也支持开启打印访问日志
|
||||
* </p>
|
||||
|
||||
@@ -36,7 +36,7 @@ import java.util.Set;
|
||||
public class LogProperties {
|
||||
|
||||
/**
|
||||
* 是否启用日志
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
package top.continew.starter.log.model;
|
||||
|
||||
import top.continew.starter.log.enums.Include;
|
||||
import top.continew.starter.log.http.RecordableHttpRequest;
|
||||
import top.continew.starter.log.http.RecordableHttpResponse;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
@@ -80,22 +78,20 @@ public class LogRecord {
|
||||
/**
|
||||
* 开始记录日志
|
||||
*
|
||||
* @param request 请求信息
|
||||
* @return 日志记录器
|
||||
*/
|
||||
public static Started start(RecordableHttpRequest request) {
|
||||
return start(Instant.now(), request);
|
||||
public static Started start() {
|
||||
return start(Instant.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始记录日志
|
||||
*
|
||||
* @param timestamp 开始时间
|
||||
* @param request 请求信息
|
||||
* @return 日志记录器
|
||||
*/
|
||||
public static Started start(Instant timestamp, RecordableHttpRequest request) {
|
||||
return new Started(timestamp, request);
|
||||
public static Started start(Instant timestamp) {
|
||||
return new Started(timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,24 +101,20 @@ public class LogRecord {
|
||||
|
||||
private final Instant timestamp;
|
||||
|
||||
private final RecordableHttpRequest request;
|
||||
|
||||
private Started(Instant timestamp, RecordableHttpRequest request) {
|
||||
private Started(Instant timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束日志记录
|
||||
*
|
||||
* @param timestamp 结束时间
|
||||
* @param response 响应信息
|
||||
* @param includes 包含信息
|
||||
* @return 日志记录
|
||||
*/
|
||||
public LogRecord finish(Instant timestamp, RecordableHttpResponse response, Set<Include> includes) {
|
||||
LogRequest logRequest = new LogRequest(this.request, includes);
|
||||
LogResponse logResponse = new LogResponse(response, includes);
|
||||
public LogRecord finish(Instant timestamp, Set<Include> includes) {
|
||||
LogRequest logRequest = new LogRequest(includes);
|
||||
LogResponse logResponse = new LogResponse(includes);
|
||||
Duration duration = Duration.between(this.timestamp, timestamp);
|
||||
return new LogRecord(this.timestamp, logRequest, logResponse, duration);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import top.continew.starter.core.util.ExceptionUtils;
|
||||
import top.continew.starter.core.util.IpUtils;
|
||||
import top.continew.starter.web.util.ServletUtils;
|
||||
import top.continew.starter.log.enums.Include;
|
||||
import top.continew.starter.log.http.RecordableHttpRequest;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
@@ -81,15 +80,15 @@ public class LogRequest {
|
||||
*/
|
||||
private String os;
|
||||
|
||||
public LogRequest(RecordableHttpRequest request, Set<Include> includes) {
|
||||
this.method = request.getMethod();
|
||||
this.url = request.getUrl();
|
||||
this.ip = request.getIp();
|
||||
this.headers = (includes.contains(Include.REQUEST_HEADERS)) ? request.getHeaders() : null;
|
||||
public LogRequest(Set<Include> includes) {
|
||||
this.method = ServletUtils.getRequestMethod();
|
||||
this.url = ServletUtils.getRequestUrl();
|
||||
this.ip = ServletUtils.getRequestIp();
|
||||
this.headers = (includes.contains(Include.REQUEST_HEADERS)) ? ServletUtils.getRequestHeaders() : null;
|
||||
if (includes.contains(Include.REQUEST_BODY)) {
|
||||
this.body = request.getBody();
|
||||
this.body = ServletUtils.getRequestBody();
|
||||
} else if (includes.contains(Include.REQUEST_PARAM)) {
|
||||
this.param = request.getParam();
|
||||
this.param = ServletUtils.getRequestParams();
|
||||
}
|
||||
this.address = (includes.contains(Include.IP_ADDRESS))
|
||||
? ExceptionUtils.exToNull(() -> IpUtils.getIpv4Address(this.ip))
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package top.continew.starter.log.model;
|
||||
|
||||
import top.continew.starter.log.enums.Include;
|
||||
import top.continew.starter.log.http.RecordableHttpResponse;
|
||||
import top.continew.starter.web.util.ServletUtils;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -50,13 +50,13 @@ public class LogResponse {
|
||||
*/
|
||||
private Map<String, Object> param;
|
||||
|
||||
public LogResponse(RecordableHttpResponse response, Set<Include> includes) {
|
||||
this.status = response.getStatus();
|
||||
this.headers = (includes.contains(Include.RESPONSE_HEADERS)) ? response.getHeaders() : null;
|
||||
public LogResponse(Set<Include> includes) {
|
||||
this.status = ServletUtils.getResponseStatus();
|
||||
this.headers = (includes.contains(Include.RESPONSE_HEADERS)) ? ServletUtils.getResponseHeaders() : null;
|
||||
if (includes.contains(Include.RESPONSE_BODY)) {
|
||||
this.body = response.getBody();
|
||||
this.body = ServletUtils.getResponseBody();
|
||||
} else if (includes.contains(Include.RESPONSE_PARAM)) {
|
||||
this.param = response.getParam();
|
||||
this.param = ServletUtils.getResponseParams();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,14 +16,17 @@
|
||||
|
||||
package top.continew.starter.log.util;
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import top.continew.starter.log.http.RecordableHttpRequest;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import top.continew.starter.json.jackson.util.JSONUtils;
|
||||
import top.continew.starter.log.model.AccessLogProperties;
|
||||
import top.continew.starter.log.model.LogProperties;
|
||||
import top.continew.starter.web.util.ServletUtils;
|
||||
import top.continew.starter.web.util.SpringWebUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 访问日志工具类
|
||||
@@ -34,42 +37,81 @@ import java.util.Map;
|
||||
*/
|
||||
public class AccessLogUtils {
|
||||
|
||||
private AccessLogUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源路径 - doc 路径
|
||||
*/
|
||||
private static final List<String> RESOURCE_PATH = List
|
||||
.of("/doc/**", "/v2/api-docs/**", "/v3/api-docs/**", "/webjars/**", "/swagger-resources/**", "/swagger-ui.html");
|
||||
|
||||
/**
|
||||
* 获取参数信息
|
||||
*
|
||||
* @param request 请求
|
||||
* @param properties 属性
|
||||
* @return {@link String }
|
||||
*/
|
||||
public static String getParam(RecordableHttpRequest request, AccessLogProperties properties) {
|
||||
public static String getParam(AccessLogProperties properties) {
|
||||
// 是否需要打印请求参数
|
||||
if (!properties.isPrintRequestParam()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 参数为空返回空
|
||||
Map<String, Object> params;
|
||||
Object params;
|
||||
try {
|
||||
params = request.getParam();
|
||||
params = getAccessLogReqParam();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ObjectUtil.isEmpty(params) || params.isEmpty()) {
|
||||
if (ObjectUtil.isEmpty(params)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 是否需要对特定入参脱敏
|
||||
if (properties.isParamSensitive()) {
|
||||
params = filterSensitiveParams(params, properties.getSensitiveParams());
|
||||
params = processSensitiveParams(params, properties.getSensitiveParams());
|
||||
}
|
||||
|
||||
// 是否自动截断超长参数值
|
||||
if (properties.isLongParamTruncate()) {
|
||||
params = truncateLongParams(params, properties.getLongParamThreshold(), properties
|
||||
params = processTruncateLongParams(params, properties.getLongParamThreshold(), properties
|
||||
.getLongParamMaxLength(), properties.getLongParamSuffix());
|
||||
}
|
||||
return JSONUtil.toJsonStr(params);
|
||||
return JSONUtils.toJsonStr(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 排除路径
|
||||
*
|
||||
* @param properties 属性
|
||||
* @param path 路径
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean exclusionPath(LogProperties properties, String path) {
|
||||
// 放行路由配置的排除检查
|
||||
return properties.isMatch(path) || RESOURCE_PATH.stream()
|
||||
.anyMatch(resourcePath -> SpringWebUtils.isMatch(path, resourcePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理敏感参数,支持 Map 和 List<Map<String, Object>> 类型
|
||||
*
|
||||
* @param params 参数
|
||||
* @param sensitiveParams 敏感参数列表
|
||||
* @return 处理后的参数
|
||||
*/
|
||||
private static Object processSensitiveParams(Object params, List<String> sensitiveParams) {
|
||||
if (params instanceof Map) {
|
||||
return filterSensitiveParams((Map<String, Object>)params, sensitiveParams);
|
||||
} else if (params instanceof List) {
|
||||
return ((List<?>)params).stream()
|
||||
.filter(item -> item instanceof Map)
|
||||
.map(item -> filterSensitiveParams((Map<String, Object>)item, sensitiveParams))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,11 +128,34 @@ public class AccessLogUtils {
|
||||
|
||||
Map<String, Object> filteredParams = new HashMap<>(params);
|
||||
for (String sensitiveKey : sensitiveParams) {
|
||||
filteredParams.computeIfPresent(sensitiveKey, (key, value) -> "***");
|
||||
if (filteredParams.containsKey(sensitiveKey)) {
|
||||
filteredParams.put(sensitiveKey, "***");
|
||||
}
|
||||
}
|
||||
return filteredParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理超长参数,支持 Map 和 List<Map<String, Object>> 类型
|
||||
*
|
||||
* @param params 参数
|
||||
* @param threshold 截断阈值(值长度超过该值才截断)
|
||||
* @param maxLength 最大长度
|
||||
* @param suffix 后缀(如 "...")
|
||||
* @return 处理后的参数
|
||||
*/
|
||||
private static Object processTruncateLongParams(Object params, int threshold, int maxLength, String suffix) {
|
||||
if (params instanceof Map) {
|
||||
return truncateLongParams((Map<String, Object>)params, threshold, maxLength, suffix);
|
||||
} else if (params instanceof List) {
|
||||
return ((List<?>)params).stream()
|
||||
.filter(Map.class::isInstance)
|
||||
.map(item -> truncateLongParams((Map<String, Object>)item, threshold, maxLength, suffix))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 截断超长参数
|
||||
*
|
||||
@@ -120,6 +185,25 @@ public class AccessLogUtils {
|
||||
return truncatedParams;
|
||||
}
|
||||
|
||||
private AccessLogUtils() {
|
||||
/**
|
||||
* 获取访问日志请求参数
|
||||
*
|
||||
* @return {@link Object }
|
||||
*/
|
||||
private static Object getAccessLogReqParam() {
|
||||
String body = ServletUtils.getRequestBody();
|
||||
if (CharSequenceUtil.isNotBlank(body) && JSONUtils.isTypeJSON(body)) {
|
||||
try {
|
||||
JsonNode jsonNode = JSONUtils.getObjectMapper().readTree(body);
|
||||
if (jsonNode.isArray()) {
|
||||
return JSONUtils.toBean(body, List.class);
|
||||
} else {
|
||||
return JSONUtils.toBean(body, Map.class);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableMap(ServletUtils.getRequestParams());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
import top.continew.starter.log.annotation.ConditionalOnEnabledLog;
|
||||
import top.continew.starter.log.dao.LogDao;
|
||||
import top.continew.starter.log.dao.impl.DefaultLogDaoImpl;
|
||||
@@ -56,7 +57,9 @@ public class LogAutoConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(new LogInterceptor(logProperties, logHandler(), logDao()));
|
||||
registry.addInterceptor(new LogInterceptor(logProperties, logHandler(), logDao()))
|
||||
.addPathPatterns(StringConstants.PATH_PATTERN)
|
||||
.excludePathPatterns(logProperties.getExcludePatterns());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,12 +27,10 @@ import org.springframework.lang.NonNull;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import top.continew.starter.log.annotation.Log;
|
||||
import top.continew.starter.log.http.servlet.RecordableServletHttpRequest;
|
||||
import top.continew.starter.log.http.servlet.RecordableServletHttpResponse;
|
||||
import top.continew.starter.log.model.AccessLogContext;
|
||||
import top.continew.starter.log.model.LogProperties;
|
||||
import top.continew.starter.log.dao.LogDao;
|
||||
import top.continew.starter.log.handler.LogHandler;
|
||||
import top.continew.starter.log.model.AccessLogContext;
|
||||
import top.continew.starter.log.model.LogProperties;
|
||||
import top.continew.starter.log.model.LogRecord;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
@@ -64,14 +62,10 @@ public class LogInterceptor implements HandlerInterceptor {
|
||||
@NonNull HttpServletResponse response,
|
||||
@NonNull Object handler) {
|
||||
Instant startTime = Instant.now();
|
||||
logHandler.accessLogStart(AccessLogContext.builder()
|
||||
.startTime(startTime)
|
||||
.request(new RecordableServletHttpRequest(request))
|
||||
.properties(logProperties)
|
||||
.build());
|
||||
logHandler.accessLogStart(AccessLogContext.builder().startTime(startTime).properties(logProperties).build());
|
||||
// 开始日志记录
|
||||
if (this.isRequestRecord(handler, request)) {
|
||||
LogRecord.Started startedLogRecord = logHandler.start(startTime, request);
|
||||
LogRecord.Started startedLogRecord = logHandler.start(startTime);
|
||||
logTtl.set(startedLogRecord);
|
||||
}
|
||||
return true;
|
||||
@@ -84,10 +78,7 @@ public class LogInterceptor implements HandlerInterceptor {
|
||||
Exception e) {
|
||||
try {
|
||||
Instant endTime = Instant.now();
|
||||
logHandler.accessLogFinish(AccessLogContext.builder()
|
||||
.endTime(endTime)
|
||||
.response(new RecordableServletHttpResponse(response, response.getStatus()))
|
||||
.build());
|
||||
logHandler.accessLogFinish(AccessLogContext.builder().endTime(endTime).build());
|
||||
LogRecord.Started startedLogRecord = logTtl.get();
|
||||
if (null == startedLogRecord) {
|
||||
return;
|
||||
@@ -96,7 +87,7 @@ public class LogInterceptor implements HandlerInterceptor {
|
||||
HandlerMethod handlerMethod = (HandlerMethod)handler;
|
||||
Method targetMethod = handlerMethod.getMethod();
|
||||
Class<?> targetClass = handlerMethod.getBeanType();
|
||||
LogRecord logRecord = logHandler.finish(startedLogRecord, endTime, response, logProperties
|
||||
LogRecord logRecord = logHandler.finish(startedLogRecord, endTime, logProperties
|
||||
.getIncludes(), targetMethod, targetClass);
|
||||
logDao.add(logRecord);
|
||||
} catch (Exception ex) {
|
||||
@@ -118,10 +109,6 @@ public class LogInterceptor implements HandlerInterceptor {
|
||||
if (!(handler instanceof HandlerMethod handlerMethod)) {
|
||||
return false;
|
||||
}
|
||||
// 如果接口匹配排除列表,不记录日志
|
||||
if (logProperties.isMatch(request.getRequestURI())) {
|
||||
return false;
|
||||
}
|
||||
// 如果接口被隐藏,不记录日志
|
||||
Operation methodOperation = handlerMethod.getMethodAnnotation(Operation.class);
|
||||
if (null != methodOperation && methodOperation.hidden()) {
|
||||
|
||||
@@ -36,7 +36,7 @@ public class WebSocketProperties {
|
||||
private static final List<String> ALL = Collections.singletonList(StringConstants.ASTERISK);
|
||||
|
||||
/**
|
||||
* 是否启用 WebSocket
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ package top.continew.starter.ratelimiter.aop;
|
||||
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;
|
||||
@@ -36,7 +35,7 @@ import top.continew.starter.ratelimiter.autoconfigure.RateLimiterProperties;
|
||||
import top.continew.starter.ratelimiter.generator.RateLimiterNameGenerator;
|
||||
import top.continew.starter.ratelimiter.enums.LimitType;
|
||||
import top.continew.starter.ratelimiter.exception.RateLimiterException;
|
||||
import top.continew.starter.web.util.SpringWebUtils;
|
||||
import top.continew.starter.web.util.ServletUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.Duration;
|
||||
@@ -170,7 +169,7 @@ public class RateLimiterAspect {
|
||||
}
|
||||
// 获取后缀
|
||||
String suffix = switch (rateLimiter.type()) {
|
||||
case IP -> JakartaServletUtil.getClientIP(SpringWebUtils.getRequest());
|
||||
case IP -> ServletUtils.getRequestIp();
|
||||
case CLUSTER -> redissonClient.getId();
|
||||
default -> StringConstants.EMPTY;
|
||||
};
|
||||
|
||||
@@ -28,11 +28,24 @@ import top.continew.starter.core.constant.PropertiesConstants;
|
||||
@ConfigurationProperties(PropertiesConstants.RATE_LIMITER)
|
||||
public class RateLimiterProperties {
|
||||
|
||||
/**
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* Key 前缀
|
||||
*/
|
||||
private String keyPrefix = "RateLimiter";
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getKeyPrefix() {
|
||||
return keyPrefix;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import top.continew.starter.core.constant.PropertiesConstants;
|
||||
public class CryptoProperties {
|
||||
|
||||
/**
|
||||
* 是否启用加/解密配置
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ import top.continew.starter.core.constant.PropertiesConstants;
|
||||
public class PasswordEncoderProperties {
|
||||
|
||||
/**
|
||||
* 是否启用密码编解码配置
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ import java.util.List;
|
||||
public class XssProperties {
|
||||
|
||||
/**
|
||||
* 是否启用 XSS 过滤
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import top.continew.starter.core.constant.PropertiesConstants;
|
||||
public class TraceProperties {
|
||||
|
||||
/**
|
||||
* 是否启用链路追踪配置
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = false;
|
||||
|
||||
|
||||
@@ -32,12 +32,6 @@
|
||||
<artifactId>spring-boot-starter-undertow</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Hibernate Validator -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Graceful Response(一个Spring Boot技术栈下的优雅响应处理组件,可以帮助开发者完成响应数据封装、异常处理、错误码填充等过程,提高开发效率,提高代码质量) -->
|
||||
<dependency>
|
||||
<groupId>com.feiniaojin</groupId>
|
||||
|
||||
@@ -36,7 +36,7 @@ public class CorsProperties {
|
||||
private static final List<String> ALL = Collections.singletonList(StringConstants.ASTERISK);
|
||||
|
||||
/**
|
||||
* 是否启用跨域配置
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = false;
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.server;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 服务器配置属性
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2.11.0
|
||||
*/
|
||||
@ConfigurationProperties("server.extension")
|
||||
public class ServerExtensionProperties {
|
||||
|
||||
/**
|
||||
* 默认禁止三个不安全的 HTTP 方法(如 CONNECT、TRACE、TRACK)
|
||||
*/
|
||||
private static final List<String> DEFAULT_ALLOWED_METHODS = List.of("CONNECT", "TRACE", "TRACK");
|
||||
|
||||
/**
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* 不允许的请求方式
|
||||
*/
|
||||
private List<String> disallowedMethods = new ArrayList<>(DEFAULT_ALLOWED_METHODS);
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public List<String> getDisallowedMethods() {
|
||||
return disallowedMethods;
|
||||
}
|
||||
|
||||
public void setDisallowedMethods(List<String> disallowedMethods) {
|
||||
this.disallowedMethods = disallowedMethods;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.server;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import io.undertow.Undertow;
|
||||
import io.undertow.server.handlers.DisallowedMethodsHandler;
|
||||
import io.undertow.util.HttpString;
|
||||
import top.continew.starter.core.constant.PropertiesConstants;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Undertow 自动配置
|
||||
*
|
||||
* @author Jasmine
|
||||
* @author Charles7c
|
||||
* @since 2.11.0
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@ConditionalOnWebApplication
|
||||
@ConditionalOnClass(Undertow.class)
|
||||
@EnableConfigurationProperties(ServerExtensionProperties.class)
|
||||
@ConditionalOnProperty(prefix = "server.extension", name = PropertiesConstants.ENABLED, havingValue = "true")
|
||||
public class UndertowAutoConfiguration {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(UndertowAutoConfiguration.class);
|
||||
|
||||
/**
|
||||
* Undertow 自定义配置
|
||||
*/
|
||||
@Bean
|
||||
public WebServerFactoryCustomizer<UndertowServletWebServerFactory> customize(ServerExtensionProperties properties) {
|
||||
return factory -> {
|
||||
factory.addDeploymentInfoCustomizers(deploymentInfo -> deploymentInfo
|
||||
.addInitialHandlerChainWrapper(handler -> new DisallowedMethodsHandler(handler, properties
|
||||
.getDisallowedMethods()
|
||||
.stream()
|
||||
.map(HttpString::tryFromString)
|
||||
.collect(Collectors.toSet()))));
|
||||
log.debug("[ContiNew Starter] - Disallowed HTTP methods on Server Undertow: {}.", properties
|
||||
.getDisallowedMethods());
|
||||
log.debug("[ContiNew Starter] - Auto Configuration 'Web-Server Undertow' completed initialization.");
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -17,22 +17,33 @@
|
||||
package top.continew.starter.web.util;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.extra.servlet.JakartaServletUtil;
|
||||
import cn.hutool.http.useragent.UserAgent;
|
||||
import cn.hutool.http.useragent.UserAgentUtil;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.util.UriUtils;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
import top.continew.starter.json.jackson.util.JSONUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Servlet 工具类
|
||||
*
|
||||
* @author Charles7c
|
||||
* @author echo
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class ServletUtils {
|
||||
public class ServletUtils extends JakartaServletUtil {
|
||||
|
||||
private ServletUtils() {
|
||||
}
|
||||
@@ -85,13 +96,138 @@ public class ServletUtils {
|
||||
return userAgent.getOs().getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求方法
|
||||
*
|
||||
* @return {@link String }
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static String getRequestMethod() {
|
||||
HttpServletRequest request = getRequest();
|
||||
return request != null ? request.getMethod() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求参数
|
||||
*
|
||||
* @param name 参数名
|
||||
* @return {@link String }
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static String getRequestParameter(String name) {
|
||||
HttpServletRequest request = getRequest();
|
||||
return request != null ? request.getParameter(name) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求 Ip
|
||||
*
|
||||
* @return {@link String }
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static String getRequestIp() {
|
||||
HttpServletRequest request = getRequest();
|
||||
return request != null ? getClientIP(request) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求头信息
|
||||
*
|
||||
* @return {@link Map }<{@link String }, {@link String }>
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static Map<String, String> getRequestHeaders() {
|
||||
HttpServletRequest request = getRequest();
|
||||
return request != null ? getHeaderMap(request) : Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求 URL(包含 query 参数)
|
||||
* <p>{@code http://localhost:8000/system/user?page=1&size=10}</p>
|
||||
*
|
||||
* @return {@link URI }
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static URI getRequestUrl() {
|
||||
HttpServletRequest request = getRequest();
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
String queryString = request.getQueryString();
|
||||
if (CharSequenceUtil.isBlank(queryString)) {
|
||||
return URI.create(request.getRequestURL().toString());
|
||||
}
|
||||
try {
|
||||
StringBuilder urlBuilder = appendQueryString(queryString);
|
||||
return new URI(urlBuilder.toString());
|
||||
} catch (URISyntaxException e) {
|
||||
String encoded = UriUtils.encodeQuery(queryString, StandardCharsets.UTF_8);
|
||||
StringBuilder urlBuilder = appendQueryString(encoded);
|
||||
return URI.create(urlBuilder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求路径
|
||||
*
|
||||
* @return {@link URI }
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static String getRequestPath() {
|
||||
HttpServletRequest request = getRequest();
|
||||
return request != null ? request.getRequestURI() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求 body 参数
|
||||
*
|
||||
* @return {@link String }
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static String getRequestBody() {
|
||||
HttpServletRequest request = getRequest();
|
||||
if (request instanceof RepeatReadRequestWrapper wrapper && !wrapper.isMultipartContent(request)) {
|
||||
String body = JakartaServletUtil.getBody(request);
|
||||
return JSONUtils.isTypeJSON(body) ? body : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求参数
|
||||
*
|
||||
* @return {@link Map }<{@link String }, {@link Object }>
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static Map<String, Object> getRequestParams() {
|
||||
String body = getRequestBody();
|
||||
return CharSequenceUtil.isNotBlank(body) && JSONUtils.isTypeJSON(body)
|
||||
? JSONUtils.toBean(body, Map.class)
|
||||
: Collections.unmodifiableMap(JakartaServletUtil.getParamMap(Objects.requireNonNull(getRequest())));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取响应状态
|
||||
*
|
||||
* @return int
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static int getResponseStatus() {
|
||||
HttpServletResponse response = getResponse();
|
||||
return response != null ? response.getStatus() : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取响应所有的头(header)信息
|
||||
*
|
||||
* @param response 响应对象{@link HttpServletResponse}
|
||||
* @return header值
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static Map<String, String> getHeaderMap(HttpServletResponse response) {
|
||||
public static Map<String, String> getResponseHeaders() {
|
||||
HttpServletResponse response = getResponse();
|
||||
if (response == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
final Collection<String> headerNames = response.getHeaderNames();
|
||||
final Map<String, String> headerMap = MapUtil.newHashMap(headerNames.size(), true);
|
||||
for (String name : headerNames) {
|
||||
@@ -99,4 +235,102 @@ public class ServletUtils {
|
||||
}
|
||||
return headerMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取响应 body 参数
|
||||
*
|
||||
* @return {@link String }
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static String getResponseBody() {
|
||||
HttpServletResponse response = getResponse();
|
||||
if (response instanceof RepeatReadResponseWrapper wrapper && !wrapper.isStreamingResponse()) {
|
||||
String body = wrapper.getResponseContent();
|
||||
return JSONUtils.isTypeJSON(body) ? body : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取响应参数
|
||||
*
|
||||
* @return {@link Map }<{@link String }, {@link Object }>
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static Map<String, Object> getResponseParams() {
|
||||
String body = getResponseBody();
|
||||
return CharSequenceUtil.isNotBlank(body) && JSONUtils.isTypeJSON(body)
|
||||
? JSONUtils.toBean(body, Map.class)
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 HTTP Session
|
||||
*
|
||||
* @return HttpSession
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static HttpSession getSession() {
|
||||
HttpServletRequest request = getRequest();
|
||||
return request != null ? request.getSession() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 HTTP Request
|
||||
*
|
||||
* @return HttpServletRequest
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static HttpServletRequest getRequest() {
|
||||
ServletRequestAttributes attributes = getRequestAttributes();
|
||||
if (attributes == null) {
|
||||
return null;
|
||||
}
|
||||
return attributes.getRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 HTTP Response
|
||||
*
|
||||
* @return HttpServletResponse
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static HttpServletResponse getResponse() {
|
||||
ServletRequestAttributes attributes = getRequestAttributes();
|
||||
if (attributes == null) {
|
||||
return null;
|
||||
}
|
||||
return attributes.getResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求属性
|
||||
*
|
||||
* @return {@link ServletRequestAttributes }
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static ServletRequestAttributes getRequestAttributes() {
|
||||
try {
|
||||
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
|
||||
return (ServletRequestAttributes)attributes;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加查询字符串
|
||||
*
|
||||
* @param queryString 查询字符串
|
||||
* @return {@link StringBuilder }
|
||||
*/
|
||||
private static StringBuilder appendQueryString(String queryString) {
|
||||
HttpServletRequest request = getRequest();
|
||||
if (request == null) {
|
||||
return new StringBuilder();
|
||||
}
|
||||
return new StringBuilder().append(request.getRequestURL())
|
||||
.append(StringConstants.QUESTION_MARK)
|
||||
.append(queryString);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,13 +20,9 @@ import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import jakarta.servlet.ServletContext;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.server.PathContainer;
|
||||
import org.springframework.web.accept.ContentNegotiationManager;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
@@ -38,7 +34,6 @@ import top.continew.starter.core.constant.StringConstants;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Spring Web 工具类
|
||||
@@ -51,24 +46,6 @@ public class SpringWebUtils {
|
||||
private SpringWebUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求对象
|
||||
*
|
||||
* @return 请求对象
|
||||
*/
|
||||
public static HttpServletRequest getRequest() {
|
||||
return getServletRequestAttributes().getRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取响应对象
|
||||
*
|
||||
* @return 响应对象
|
||||
*/
|
||||
public static HttpServletResponse getResponse() {
|
||||
return getServletRequestAttributes().getResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* 路径是否匹配
|
||||
*
|
||||
@@ -157,8 +134,4 @@ public class SpringWebUtils {
|
||||
.getUrlMap();
|
||||
ReflectUtil.<Void>invoke(resourceHandlerMapping, "registerHandlers", additionalUrlMap);
|
||||
}
|
||||
|
||||
private static ServletRequestAttributes getServletRequestAttributes() {
|
||||
return (ServletRequestAttributes)Objects.requireNonNull(RequestContextHolder.getRequestAttributes());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
top.continew.starter.web.autoconfigure.mvc.WebMvcAutoConfiguration
|
||||
top.continew.starter.web.autoconfigure.cors.CorsAutoConfiguration
|
||||
top.continew.starter.web.autoconfigure.cors.CorsAutoConfiguration
|
||||
top.continew.starter.web.autoconfigure.server.UndertowAutoConfiguration
|
||||
@@ -22,4 +22,21 @@ continew-starter.web.response:
|
||||
exclude-packages:
|
||||
- io.swagger.**
|
||||
- org.springdoc.**
|
||||
- org.springframework.boot.actuate.*
|
||||
- org.springframework.boot.actuate.*
|
||||
|
||||
--- ### 服务器配置
|
||||
server:
|
||||
## Undertow 服务器配置
|
||||
undertow:
|
||||
# HTTP POST 请求内容的大小上限(默认 -1,不限制)
|
||||
max-http-post-size: -1
|
||||
# 以下的配置会影响 buffer,这些 buffer 会用于服务器连接的 IO 操作,有点类似 Netty 的池化内存管理
|
||||
# 每块 buffer的空间大小(越小的空间被利用越充分,不要设置太大,以免影响其他应用,合适即可)
|
||||
buffer-size: 512
|
||||
# 是否分配的直接内存(NIO 直接分配的堆外内存)
|
||||
direct-buffers: true
|
||||
threads:
|
||||
# 设置 IO 线程数,它主要执行非阻塞的任务,它们会负责多个连接(默认每个 CPU 核心一个线程)
|
||||
io: 8
|
||||
# 阻塞任务线程池,当执行类似 Servlet 请求阻塞操作,Undertow 会从这个线程池中取得线程(它的值设置取决于系统的负载)
|
||||
worker: 256
|
||||
52
pom.xml
52
pom.xml
@@ -19,31 +19,6 @@
|
||||
ContiNew Starter 包含了一系列经过企业实践优化的依赖包(如 MyBatis-Plus、SaToken),
|
||||
可轻松集成到应用中,为开发人员减少手动引入依赖及配置的麻烦,为 Spring Boot Web 项目的灵活快速构建提供支持。
|
||||
</description>
|
||||
<url>https://github.com/continew-org/continew-starter</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>GNU LESSER GENERAL PUBLIC LICENSE</name>
|
||||
<url>http://www.gnu.org/licenses/lgpl.html</url>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>charles7c</id>
|
||||
<name>Charles7c</name>
|
||||
<email>charles7c@126.com</email>
|
||||
<roles>
|
||||
<role>Creator</role>
|
||||
<role>Java Development Engineer</role>
|
||||
</roles>
|
||||
<timezone>+8</timezone>
|
||||
<url>https://github.com/Charles7c</url>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<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>
|
||||
<!-- Maven Environment Versions -->
|
||||
@@ -58,6 +33,7 @@
|
||||
|
||||
<modules>
|
||||
<module>continew-starter-dependencies</module>
|
||||
<module>continew-starter-bom</module>
|
||||
<module>continew-starter-core</module>
|
||||
<module>continew-starter-json</module>
|
||||
<module>continew-starter-api-doc</module>
|
||||
@@ -142,4 +118,30 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<url>https://github.com/continew-org/continew-starter</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>GNU LESSER GENERAL PUBLIC LICENSE</name>
|
||||
<url>http://www.gnu.org/licenses/lgpl.html</url>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>charles7c</id>
|
||||
<name>Charles7c</name>
|
||||
<email>charles7c@126.com</email>
|
||||
<roles>
|
||||
<role>Creator</role>
|
||||
<role>Java Development Engineer</role>
|
||||
</roles>
|
||||
<timezone>+8</timezone>
|
||||
<url>https://github.com/Charles7c</url>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<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>
|
||||
</project>
|
||||
Reference in New Issue
Block a user