diff --git a/README.md b/README.md index cdc7779..d5c8289 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@

Build Status - Coverage Status - Downloads + Coverage Status + Downloads

## SpringBlade微服务开发平台 diff --git a/blade-core-boot/pom.xml b/blade-core-boot/pom.xml index c15a6cf..7278c75 100644 --- a/blade-core-boot/pom.xml +++ b/blade-core-boot/pom.xml @@ -5,7 +5,7 @@ org.springblade blade-tool - 2.5.4 + 2.6.0 4.0.0 @@ -62,28 +62,6 @@ blade-core-swagger ${blade.tool.version} - - - io.springfox - springfox-swagger2 - ${swagger.version} - - - io.swagger - swagger-models - - - - - io.swagger - swagger-models - ${swagger.models.version} - - - com.github.xiaoymin - swagger-bootstrap-ui - ${swagger.bootstrapui.version} - org.springframework.boot diff --git a/blade-core-boot/src/main/java/org/springblade/core/boot/config/MybatisPlusConfiguration.java b/blade-core-boot/src/main/java/org/springblade/core/boot/config/MybatisPlusConfiguration.java index c1f70c5..cae413c 100644 --- a/blade-core-boot/src/main/java/org/springblade/core/boot/config/MybatisPlusConfiguration.java +++ b/blade-core-boot/src/main/java/org/springblade/core/boot/config/MybatisPlusConfiguration.java @@ -16,13 +16,12 @@ package org.springblade.core.boot.config; import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; -import com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor; import org.mybatis.spring.annotation.MapperScan; -import org.springblade.core.launch.constant.AppConstant; +import org.springblade.core.mp.plugins.SqlLogInterceptor; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; /** * mybatisplus 配置 @@ -40,14 +39,12 @@ public class MybatisPlusConfiguration { } /** - * SQL执行效率插件 - * - * @return PerformanceInterceptor + * sql 日志 */ @Bean - @Profile({AppConstant.DEV_CODE, AppConstant.TEST_CODE}) - public PerformanceInterceptor performanceInterceptor() { - return new PerformanceInterceptor(); + @ConditionalOnProperty(value = "blade.mybatis-plus.sql-log.enable", matchIfMissing = true) + public SqlLogInterceptor sqlLogInterceptor() { + return new SqlLogInterceptor(); } } diff --git a/blade-core-boot/src/main/java/org/springblade/core/boot/tenant/BladeTenantHandler.java b/blade-core-boot/src/main/java/org/springblade/core/boot/tenant/BladeTenantHandler.java index dea58f0..13e22e6 100644 --- a/blade-core-boot/src/main/java/org/springblade/core/boot/tenant/BladeTenantHandler.java +++ b/blade-core-boot/src/main/java/org/springblade/core/boot/tenant/BladeTenantHandler.java @@ -41,7 +41,7 @@ public class BladeTenantHandler implements TenantHandler { * @return 租户ID */ @Override - public Expression getTenantId() { + public Expression getTenantId(boolean where) { return new StringValue(Func.toStr(SecureUtil.getTenantId(), TenantConstant.DEFAULT_TENANT_ID)); } diff --git a/blade-core-boot/src/main/resources/bootstrap.yml b/blade-core-boot/src/main/resources/bootstrap.yml index 071731a..ee1c4d6 100644 --- a/blade-core-boot/src/main/resources/bootstrap.yml +++ b/blade-core-boot/src/main/resources/bootstrap.yml @@ -100,7 +100,7 @@ mybatis-plus: swagger: title: SpringBlade 接口文档系统 description: SpringBlade 接口文档系统 - version: 2.5.4 + version: 2.6.0 license: Powered By SpringBlade licenseUrl: https://bladex.vip terms-of-service-url: https://bladex.vip diff --git a/blade-core-cloud/pom.xml b/blade-core-cloud/pom.xml index fc31522..ac4a50f 100644 --- a/blade-core-cloud/pom.xml +++ b/blade-core-cloud/pom.xml @@ -5,7 +5,7 @@ blade-tool org.springblade - 2.5.4 + 2.6.0 4.0.0 diff --git a/blade-core-develop/pom.xml b/blade-core-develop/pom.xml index b35e73a..2eea7ce 100644 --- a/blade-core-develop/pom.xml +++ b/blade-core-develop/pom.xml @@ -5,7 +5,7 @@ blade-tool org.springblade - 2.5.4 + 2.6.0 4.0.0 diff --git a/blade-core-develop/src/main/resources/templates/controller.java.vm b/blade-core-develop/src/main/resources/templates/controller.java.vm index e145040..3f0d5e9 100644 --- a/blade-core-develop/src/main/resources/templates/controller.java.vm +++ b/blade-core-develop/src/main/resources/templates/controller.java.vm @@ -16,9 +16,9 @@ package $!{package.Controller}; import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperationSupport; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import lombok.AllArgsConstructor; import javax.validation.Valid; diff --git a/blade-core-launch/pom.xml b/blade-core-launch/pom.xml index c4c81e9..0d9fb8f 100644 --- a/blade-core-launch/pom.xml +++ b/blade-core-launch/pom.xml @@ -5,7 +5,7 @@ blade-tool org.springblade - 2.5.4 + 2.6.0 4.0.0 diff --git a/blade-core-launch/src/main/java/org/springblade/core/launch/constant/AppConstant.java b/blade-core-launch/src/main/java/org/springblade/core/launch/constant/AppConstant.java index c7e124e..6ed79f4 100644 --- a/blade-core-launch/src/main/java/org/springblade/core/launch/constant/AppConstant.java +++ b/blade-core-launch/src/main/java/org/springblade/core/launch/constant/AppConstant.java @@ -25,7 +25,7 @@ public interface AppConstant { /** * 应用版本 */ - String APPLICATION_VERSION = "2.5.4"; + String APPLICATION_VERSION = "2.6.0"; /** * 基础包 diff --git a/blade-core-log/pom.xml b/blade-core-log/pom.xml index 439c964..4297c89 100644 --- a/blade-core-log/pom.xml +++ b/blade-core-log/pom.xml @@ -5,7 +5,7 @@ blade-tool org.springblade - 2.5.4 + 2.6.0 4.0.0 diff --git a/blade-core-mybatis/pom.xml b/blade-core-mybatis/pom.xml index a72b0c9..8a92fc9 100644 --- a/blade-core-mybatis/pom.xml +++ b/blade-core-mybatis/pom.xml @@ -5,7 +5,7 @@ blade-tool org.springblade - 2.5.4 + 2.6.0 4.0.0 diff --git a/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/BaseService.java b/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/BaseService.java index d179c76..cf9dcfb 100644 --- a/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/BaseService.java +++ b/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/BaseService.java @@ -32,7 +32,7 @@ public interface BaseService extends IService { * 逻辑删除 * * @param ids id集合(逗号分隔) - * @return + * @return boolean */ boolean deleteLogic(@NotEmpty List ids); diff --git a/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/BaseServiceImpl.java b/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/BaseServiceImpl.java index 0b18187..b7b592a 100644 --- a/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/BaseServiceImpl.java +++ b/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/BaseServiceImpl.java @@ -24,8 +24,6 @@ import org.springblade.core.tool.utils.DateUtil; import org.springframework.validation.annotation.Validated; import javax.validation.constraints.NotEmpty; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; import java.util.Date; import java.util.List; @@ -39,14 +37,6 @@ import java.util.List; @Validated public class BaseServiceImpl, T extends BaseEntity> extends ServiceImpl implements BaseService { - private Class modelClass; - - @SuppressWarnings("unchecked") - public BaseServiceImpl() { - Type type = this.getClass().getGenericSuperclass(); - this.modelClass = (Class) ((ParameterizedType) type).getActualTypeArguments()[1]; - } - @Override public boolean save(T entity) { BladeUser user = SecureUtil.getUser(); diff --git a/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/TenantEntity.java b/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/TenantEntity.java index 04f5946..a53bf61 100644 --- a/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/TenantEntity.java +++ b/blade-core-mybatis/src/main/java/org/springblade/core/mp/base/TenantEntity.java @@ -18,6 +18,7 @@ package org.springblade.core.mp.base; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import lombok.EqualsAndHashCode; /** * 租户基础实体类 @@ -25,6 +26,7 @@ import lombok.Data; * @author Chill */ @Data +@EqualsAndHashCode(callSuper = true) public class TenantEntity extends BaseEntity { /** diff --git a/blade-core-mybatis/src/main/java/org/springblade/core/mp/plugins/SqlLogInterceptor.java b/blade-core-mybatis/src/main/java/org/springblade/core/mp/plugins/SqlLogInterceptor.java new file mode 100644 index 0000000..9fe9f85 --- /dev/null +++ b/blade-core-mybatis/src/main/java/org/springblade/core/mp/plugins/SqlLogInterceptor.java @@ -0,0 +1,185 @@ +package org.springblade.core.mp.plugins; + +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.baomidou.mybatisplus.core.toolkit.PluginUtils; +import com.baomidou.mybatisplus.core.toolkit.StringPool; +import com.baomidou.mybatisplus.core.toolkit.SystemClock; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.executor.statement.StatementHandler; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.plugin.*; +import org.apache.ibatis.reflection.MetaObject; +import org.apache.ibatis.reflection.SystemMetaObject; +import org.apache.ibatis.session.ResultHandler; +import org.springblade.core.tool.utils.StringUtil; + +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.Statement; +import java.util.*; + +/** + * 用于输出每条 SQL 语句及其执行时间 + * + * @author hubin nieqiurong TaoYu + * @since 2016-07-07 + */ +@Slf4j +@Intercepts({ + @Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}), + @Signature(type = StatementHandler.class, method = "update", args = Statement.class), + @Signature(type = StatementHandler.class, method = "batch", args = Statement.class) +}) +public class SqlLogInterceptor implements Interceptor { + private static final String DRUID_POOLED_PREPARED_STATEMENT = "com.alibaba.druid.pool.DruidPooledPreparedStatement"; + private static final String T4C_PREPARED_STATEMENT = "oracle.jdbc.driver.T4CPreparedStatement"; + private static final String ORACLE_PREPARED_STATEMENT_WRAPPER = "oracle.jdbc.driver.OraclePreparedStatementWrapper"; + + private Method oracleGetOriginalSqlMethod; + private Method druidGetSqlMethod; + + @Override + public Object intercept(Invocation invocation) throws Throwable { + Statement statement; + Object firstArg = invocation.getArgs()[0]; + if (Proxy.isProxyClass(firstArg.getClass())) { + statement = (Statement) SystemMetaObject.forObject(firstArg).getValue("h.statement"); + } else { + statement = (Statement) firstArg; + } + MetaObject stmtMetaObj = SystemMetaObject.forObject(statement); + try { + statement = (Statement) stmtMetaObj.getValue("stmt.statement"); + } catch (Exception e) { + // do nothing + } + if (stmtMetaObj.hasGetter("delegate")) { + //Hikari + try { + statement = (Statement) stmtMetaObj.getValue("delegate"); + } catch (Exception ignored) { + + } + } + + String originalSql = null; + String stmtClassName = statement.getClass().getName(); + if (DRUID_POOLED_PREPARED_STATEMENT.equals(stmtClassName)) { + try { + if (druidGetSqlMethod == null) { + Class clazz = Class.forName(DRUID_POOLED_PREPARED_STATEMENT); + druidGetSqlMethod = clazz.getMethod("getSql"); + } + Object stmtSql = druidGetSqlMethod.invoke(statement); + if (stmtSql instanceof String) { + originalSql = (String) stmtSql; + } + } catch (Exception e) { + e.printStackTrace(); + } + } else if (T4C_PREPARED_STATEMENT.equals(stmtClassName) + || ORACLE_PREPARED_STATEMENT_WRAPPER.equals(stmtClassName)) { + try { + if (oracleGetOriginalSqlMethod != null) { + Object stmtSql = oracleGetOriginalSqlMethod.invoke(statement); + if (stmtSql instanceof String) { + originalSql = (String) stmtSql; + } + } else { + Class clazz = Class.forName(stmtClassName); + oracleGetOriginalSqlMethod = getMethodRegular(clazz, "getOriginalSql"); + if (oracleGetOriginalSqlMethod != null) { + //OraclePreparedStatementWrapper is not a public class, need set this. + oracleGetOriginalSqlMethod.setAccessible(true); + if (null != oracleGetOriginalSqlMethod) { + Object stmtSql = oracleGetOriginalSqlMethod.invoke(statement); + if (stmtSql instanceof String) { + originalSql = (String) stmtSql; + } + } + } + } + } catch (Exception e) { + //ignore + } + } + if (originalSql == null) { + originalSql = statement.toString(); + } + originalSql = originalSql.replaceAll("[\\s]+", StringPool.SPACE); + int index = indexOfSqlStart(originalSql); + if (index > 0) { + originalSql = originalSql.substring(index); + } + + // 计算执行 SQL 耗时 + long start = SystemClock.now(); + Object result = invocation.proceed(); + long timing = SystemClock.now() - start; + + // SQL 打印执行结果 + Object target = PluginUtils.realTarget(invocation.getTarget()); + MetaObject metaObject = SystemMetaObject.forObject(target); + MappedStatement ms = (MappedStatement) metaObject.getValue("delegate.mappedStatement"); + // 打印 sql + System.err.println( + StringUtil.format( + "\n============== Sql Start ==============" + + "\nExecute ID :{}" + + "\nExecute SQL :{}" + + "\nExecute Time:{} ms" + + "\n============== Sql End ==============\n", + ms.getId(), originalSql, timing)); + return result; + } + + @Override + public Object plugin(Object target) { + if (target instanceof StatementHandler) { + return Plugin.wrap(target, this); + } + return target; + } + + /** + * 获取此方法名的具体 Method + * + * @param clazz class 对象 + * @param methodName 方法名 + * @return 方法 + */ + private Method getMethodRegular(Class clazz, String methodName) { + if (Object.class.equals(clazz)) { + return null; + } + for (Method method : clazz.getDeclaredMethods()) { + if (method.getName().equals(methodName)) { + return method; + } + } + return getMethodRegular(clazz.getSuperclass(), methodName); + } + + /** + * 获取sql语句开头部分 + * + * @param sql ignore + * @return ignore + */ + private int indexOfSqlStart(String sql) { + String upperCaseSql = sql.toUpperCase(); + Set set = new HashSet<>(); + set.add(upperCaseSql.indexOf("SELECT ")); + set.add(upperCaseSql.indexOf("UPDATE ")); + set.add(upperCaseSql.indexOf("INSERT ")); + set.add(upperCaseSql.indexOf("DELETE ")); + set.remove(-1); + if (CollectionUtils.isEmpty(set)) { + return -1; + } + List list = new ArrayList<>(set); + list.sort(Comparator.naturalOrder()); + return list.get(0); + } + +} diff --git a/blade-core-mybatis/src/main/java/org/springblade/core/mp/support/BaseEntityWrapper.java b/blade-core-mybatis/src/main/java/org/springblade/core/mp/support/BaseEntityWrapper.java index c6ad5bf..1a62cb4 100644 --- a/blade-core-mybatis/src/main/java/org/springblade/core/mp/support/BaseEntityWrapper.java +++ b/blade-core-mybatis/src/main/java/org/springblade/core/mp/support/BaseEntityWrapper.java @@ -30,15 +30,17 @@ public abstract class BaseEntityWrapper { /** * 单个实体类包装 - * @param entity - * @return + * + * @param entity 实体类 + * @return V */ public abstract V entityVO(E entity); /** * 实体类集合包装 - * @param list - * @return + * + * @param list 猎豹 + * @return List V */ public List listVO(List list) { return list.stream().map(this::entityVO).collect(Collectors.toList()); @@ -46,8 +48,9 @@ public abstract class BaseEntityWrapper { /** * 分页实体类集合包装 - * @param pages - * @return + * + * @param pages 分页 + * @return Page V */ public IPage pageVO(IPage pages) { List records = listVO(pages.getRecords()); diff --git a/blade-core-mybatis/src/main/java/org/springblade/core/mp/support/Condition.java b/blade-core-mybatis/src/main/java/org/springblade/core/mp/support/Condition.java index ce27a3f..d684311 100644 --- a/blade-core-mybatis/src/main/java/org/springblade/core/mp/support/Condition.java +++ b/blade-core-mybatis/src/main/java/org/springblade/core/mp/support/Condition.java @@ -33,8 +33,8 @@ public class Condition { /** * 转化成mybatis plus中的Page * - * @param query - * @return + * @param query 查询包装类 + * @return Page T */ public static IPage getPage(Query query) { Page page = new Page<>(Func.toInt(query.getCurrent(), 1), Func.toInt(query.getSize(), 10)); @@ -46,9 +46,9 @@ public class Condition { /** * 获取mybatis plus中的QueryWrapper * - * @param entity - * @param - * @return + * @param entity 实体类 + * @param 泛型 + * @return QueryWrapper */ public static QueryWrapper getQueryWrapper(T entity) { return new QueryWrapper<>(entity); @@ -57,10 +57,10 @@ public class Condition { /** * 获取mybatis plus中的QueryWrapper * - * @param query - * @param clazz - * @param - * @return + * @param query 查询包装类 + * @param clazz 实体类 + * @param 泛型 + * @return QueryWrapper */ public static QueryWrapper getQueryWrapper(Map query, Class clazz) { query.remove("current"); diff --git a/blade-core-mybatis/src/main/java/org/springblade/core/mp/support/SqlKeyword.java b/blade-core-mybatis/src/main/java/org/springblade/core/mp/support/SqlKeyword.java index bd4b269..2096dca 100644 --- a/blade-core-mybatis/src/main/java/org/springblade/core/mp/support/SqlKeyword.java +++ b/blade-core-mybatis/src/main/java/org/springblade/core/mp/support/SqlKeyword.java @@ -88,7 +88,7 @@ public class SqlKeyword { * * @param column 字段名 * @param keyword 关键字 - * @return + * @return String */ private static String getColumn(String column, String keyword) { return StringUtil.humpToUnderline(StringUtil.removeSuffix(column, keyword)); diff --git a/blade-core-oss/pom.xml b/blade-core-oss/pom.xml index 2aa1868..bbbc13b 100644 --- a/blade-core-oss/pom.xml +++ b/blade-core-oss/pom.xml @@ -5,7 +5,7 @@ blade-tool org.springblade - 2.5.4 + 2.6.0 4.0.0 diff --git a/blade-core-secure/pom.xml b/blade-core-secure/pom.xml index 7299c12..7c3d376 100644 --- a/blade-core-secure/pom.xml +++ b/blade-core-secure/pom.xml @@ -5,7 +5,7 @@ blade-tool org.springblade - 2.5.4 + 2.6.0 4.0.0 diff --git a/blade-core-swagger/pom.xml b/blade-core-swagger/pom.xml index 13bf861..0f88f69 100644 --- a/blade-core-swagger/pom.xml +++ b/blade-core-swagger/pom.xml @@ -5,7 +5,7 @@ blade-tool org.springblade - 2.5.4 + 2.6.0 4.0.0 @@ -23,26 +23,10 @@ ${blade.tool.version} - - io.springfox - springfox-swagger2 - ${swagger.version} - - - io.swagger - swagger-models - - - - - io.swagger - swagger-models - ${swagger.models.version} - com.github.xiaoymin - swagger-bootstrap-ui - ${swagger.bootstrapui.version} + knife4j-micro-spring-boot-starter + ${knife4j.version} diff --git a/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerAutoConfiguration.java b/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerAutoConfiguration.java index e283e06..56fc7ce 100644 --- a/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerAutoConfiguration.java +++ b/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerAutoConfiguration.java @@ -16,14 +16,16 @@ package org.springblade.core.swagger; -import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI; +import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Profile; +import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.service.*; @@ -42,10 +44,11 @@ import java.util.List; * @author Chill */ @Configuration +@EnableKnife4j @EnableSwagger2 -@EnableSwaggerBootstrapUI @Profile({"dev", "test"}) @EnableConfigurationProperties(SwaggerProperties.class) +@Import(BeanValidatorPluginsConfiguration.class) public class SwaggerAutoConfiguration { private static final String DEFAULT_EXCLUDE_PATH = "/error"; diff --git a/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerProperties.java b/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerProperties.java index 09822f1..3299cbd 100644 --- a/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerProperties.java +++ b/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerProperties.java @@ -55,7 +55,7 @@ public class SwaggerProperties { /** * 版本 **/ - private String version = "2.5.4"; + private String version = "2.6.0"; /** * 许可证 **/ diff --git a/blade-core-test/pom.xml b/blade-core-test/pom.xml index 2b9c195..976f54c 100644 --- a/blade-core-test/pom.xml +++ b/blade-core-test/pom.xml @@ -5,7 +5,7 @@ org.springblade blade-tool - 2.5.4 + 2.6.0 4.0.0 diff --git a/blade-core-tool/pom.xml b/blade-core-tool/pom.xml index 2f8bb48..0016aee 100644 --- a/blade-core-tool/pom.xml +++ b/blade-core-tool/pom.xml @@ -6,7 +6,7 @@ org.springblade blade-tool - 2.5.4 + 2.6.0 4.0.0 diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/config/JacksonConfiguration.java b/blade-core-tool/src/main/java/org/springblade/core/tool/config/JacksonConfiguration.java index ae14f38..f1216eb 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/config/JacksonConfiguration.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/config/JacksonConfiguration.java @@ -16,9 +16,11 @@ package org.springblade.core.tool.config; import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.json.JsonReadFeature; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import lombok.AllArgsConstructor; import org.springblade.core.tool.jackson.BladeJavaTimeModule; import org.springblade.core.tool.utils.DateUtil; import org.springframework.boot.autoconfigure.AutoConfigureBefore; @@ -40,6 +42,7 @@ import java.util.TimeZone; * @author Chill */ @Configuration +@AllArgsConstructor @ConditionalOnClass(ObjectMapper.class) @AutoConfigureBefore(JacksonAutoConfiguration.class) public class JacksonConfiguration { @@ -59,8 +62,8 @@ public class JacksonConfiguration { //序列化时,日期的统一格式 objectMapper.setDateFormat(new SimpleDateFormat(DateUtil.PATTERN_DATETIME, Locale.CHINA)); //序列化处理 - objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true); - objectMapper.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true); + objectMapper.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true); + objectMapper.configure(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER.mappedFeature(), true); objectMapper.findAndRegisterModules(); //失败处理 objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/config/MessageConfiguration.java b/blade-core-tool/src/main/java/org/springblade/core/tool/config/MessageConfiguration.java index acb4fdb..c3f3344 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/config/MessageConfiguration.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/config/MessageConfiguration.java @@ -19,11 +19,7 @@ package org.springblade.core.tool.config; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.AllArgsConstructor; import org.springblade.core.tool.jackson.MappingApiJackson2HttpMessageConverter; -import org.springblade.core.tool.support.xss.XssFilter; -import org.springblade.core.tool.support.xss.XssProperties; import org.springblade.core.tool.utils.Charsets; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; @@ -31,7 +27,6 @@ import org.springframework.http.converter.*; import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import javax.servlet.DispatcherType; import java.util.List; /** @@ -46,8 +41,6 @@ public class MessageConfiguration implements WebMvcConfigurer { private final ObjectMapper objectMapper; - private final XssProperties xssProperties; - /** * 使用 JACKSON 作为JSON MessageConverter */ @@ -61,20 +54,4 @@ public class MessageConfiguration implements WebMvcConfigurer { converters.add(new MappingApiJackson2HttpMessageConverter(objectMapper)); } - /** - * 防XSS注入 - * - * @return FilterRegistrationBean - */ - @Bean - public FilterRegistrationBean xssFilterRegistration() { - FilterRegistrationBean registration = new FilterRegistrationBean(); - registration.setDispatcherTypes(DispatcherType.REQUEST); - registration.setFilter(new XssFilter(xssProperties)); - registration.addUrlPatterns("/*"); - registration.setName("xssFilter"); - registration.setOrder(Ordered.LOWEST_PRECEDENCE); - return registration; - } - } diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/config/ToolConfiguration.java b/blade-core-tool/src/main/java/org/springblade/core/tool/config/ToolConfiguration.java index 7994b4f..0f5b62b 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/config/ToolConfiguration.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/config/ToolConfiguration.java @@ -16,9 +16,7 @@ package org.springblade.core.tool.config; -import org.springblade.core.tool.support.xss.XssProperties; import org.springblade.core.tool.utils.SpringUtil; -import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; @@ -32,7 +30,6 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; */ @Configuration @Order(Ordered.HIGHEST_PRECEDENCE) -@EnableConfigurationProperties(XssProperties.class) public class ToolConfiguration implements WebMvcConfigurer { /** diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/config/XssConfiguration.java b/blade-core-tool/src/main/java/org/springblade/core/tool/config/XssConfiguration.java new file mode 100644 index 0000000..d14adac --- /dev/null +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/config/XssConfiguration.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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 org.springblade.core.tool.config; + +import lombok.AllArgsConstructor; +import org.springblade.core.tool.support.xss.XssFilter; +import org.springblade.core.tool.support.xss.XssProperties; +import org.springblade.core.tool.support.xss.XssUrlProperties; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; + +import javax.servlet.DispatcherType; + +/** + * Xss配置类 + * + * @author Chill + */ +@Configuration +@AllArgsConstructor +@ConditionalOnProperty(value = "blade.xss.enable", havingValue = "true") +@EnableConfigurationProperties({XssProperties.class, XssUrlProperties.class}) +public class XssConfiguration { + + private final XssProperties xssProperties; + private final XssUrlProperties xssUrlProperties; + + /** + * 防XSS注入 + */ + @Bean + public FilterRegistrationBean xssFilterRegistration() { + FilterRegistrationBean registration = new FilterRegistrationBean<>(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new XssFilter(xssProperties, xssUrlProperties)); + registration.addUrlPatterns("/*"); + registration.setName("xssFilter"); + registration.setOrder(Ordered.LOWEST_PRECEDENCE); + return registration; + } +} diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/jackson/JsonUtil.java b/blade-core-tool/src/main/java/org/springblade/core/tool/jackson/JsonUtil.java index 72c036e..8bdac8d 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/jackson/JsonUtil.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/jackson/JsonUtil.java @@ -17,6 +17,7 @@ package org.springblade.core.tool.jackson; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.json.JsonReadFeature; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; @@ -97,7 +98,7 @@ public class JsonUtil { * @param T 泛型标记 * @return Bean */ - public static T parse(String content, TypeReference typeReference) { + public static T parse(String content, TypeReference typeReference) { try { return getInstance().readValue(content, typeReference); } catch (IOException e) { @@ -130,7 +131,7 @@ public class JsonUtil { * @param T 泛型标记 * @return Bean */ - public static T parse(byte[] bytes, TypeReference typeReference) { + public static T parse(byte[] bytes, TypeReference typeReference) { try { return getInstance().readValue(bytes, typeReference); } catch (IOException e) { @@ -162,7 +163,7 @@ public class JsonUtil { * @param T 泛型标记 * @return Bean */ - public static T parse(InputStream in, TypeReference typeReference) { + public static T parse(InputStream in, TypeReference typeReference) { try { return getInstance().readValue(in, typeReference); } catch (IOException e) { @@ -184,7 +185,7 @@ public class JsonUtil { content = StringPool.LEFT_SQ_BRACKET + content + StringPool.RIGHT_SQ_BRACKET; } - List> list = getInstance().readValue(content, new TypeReference>() { + List> list = getInstance().readValue(content, new TypeReference>>() { }); List result = new ArrayList<>(); for (Map map : list) { @@ -208,7 +209,7 @@ public class JsonUtil { public static Map toMap(String content, Class valueTypeRef) { try { - Map> map = getInstance().readValue(content, new TypeReference>() { + Map> map = getInstance().readValue(content, new TypeReference>>() { }); Map result = new HashMap<>(16); for (Map.Entry> entry : map.entrySet()) { @@ -305,8 +306,8 @@ public class JsonUtil { //序列化时,日期的统一格式 super.setDateFormat(new SimpleDateFormat(DateUtil.PATTERN_DATETIME, Locale.CHINA)); //序列化处理 - super.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true); - super.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true); + super.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true); + super.configure(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER.mappedFeature(), true); super.findAndRegisterModules(); //失败处理 super.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/support/xss/XssFilter.java b/blade-core-tool/src/main/java/org/springblade/core/tool/support/xss/XssFilter.java index e94fa3e..cab3e79 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/support/xss/XssFilter.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/support/xss/XssFilter.java @@ -16,6 +16,7 @@ package org.springblade.core.tool.support.xss; import lombok.AllArgsConstructor; +import org.springblade.core.tool.utils.StringPool; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; @@ -30,6 +31,7 @@ import java.io.IOException; public class XssFilter implements Filter { private XssProperties xssProperties; + private XssUrlProperties xssUrlProperties; @Override public void init(FilterConfig config) { @@ -39,7 +41,7 @@ public class XssFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String path = ((HttpServletRequest) request).getServletPath(); - if (xssProperties.getExcludePatterns().stream().anyMatch(path::contains)) { + if (isSkip(path)) { chain.doFilter(request, response); } else { XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); @@ -47,6 +49,11 @@ public class XssFilter implements Filter { } } + private boolean isSkip(String path) { + return (xssUrlProperties.getExcludePatterns().stream().anyMatch(path::startsWith)) + || (xssProperties.getSkipUrl().stream().map(url -> url.replace("/**", StringPool.EMPTY)).anyMatch(path::startsWith)); + } + @Override public void destroy() { diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/support/xss/XssHttpServletRequestWrapper.java b/blade-core-tool/src/main/java/org/springblade/core/tool/support/xss/XssHttpServletRequestWrapper.java index 6c0fdd1..cfc8620 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/support/xss/XssHttpServletRequestWrapper.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/support/xss/XssHttpServletRequestWrapper.java @@ -15,9 +15,7 @@ */ package org.springblade.core.tool.support.xss; -import org.springblade.core.tool.utils.Charsets; import org.springblade.core.tool.utils.StringUtil; -import org.springblade.core.tool.utils.WebUtil; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -29,6 +27,7 @@ import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.util.LinkedHashMap; import java.util.Map; @@ -49,15 +48,9 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { */ private final static HtmlFilter HTML_FILTER = new HtmlFilter(); - /** - * 缓存报文,支持多次读取流 - */ - private final byte[] body; - - public XssHttpServletRequestWrapper(HttpServletRequest request) throws IOException { + public XssHttpServletRequestWrapper(HttpServletRequest request) { super(request); orgRequest = request; - body = WebUtil.getRequestBytes(request); } @Override @@ -67,51 +60,67 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { @Override public ServletInputStream getInputStream() throws IOException { - - //为空,直接返回 if (null == super.getHeader(HttpHeaders.CONTENT_TYPE)) { return super.getInputStream(); } - //非json类型,直接返回 - if (!super.getHeader(HttpHeaders.CONTENT_TYPE).equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE) - && !super.getHeader(HttpHeaders.CONTENT_TYPE).equalsIgnoreCase(MediaType.APPLICATION_JSON_UTF8_VALUE)) { + if (super.getHeader(HttpHeaders.CONTENT_TYPE).startsWith(MediaType.MULTIPART_FORM_DATA_VALUE)) { return super.getInputStream(); } - //为空,直接返回 - String requestStr = WebUtil.getRequestStr(orgRequest, body); - if (StringUtil.isBlank(requestStr)) { - return super.getInputStream(); - } - - requestStr = xssEncode(requestStr); - - final ByteArrayInputStream bis = new ByteArrayInputStream(requestStr.getBytes(Charsets.UTF_8)); + final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(inputHandlers(super.getInputStream()).getBytes()); return new ServletInputStream() { + @Override + public int read() { + return byteArrayInputStream.read(); + } + @Override public boolean isFinished() { - return true; + return false; } @Override public boolean isReady() { - return true; + return false; } @Override public void setReadListener(ReadListener readListener) { - - } - - @Override - public int read() throws IOException { - return bis.read(); } }; + } + private String inputHandlers(ServletInputStream servletInputStream) { + StringBuilder sb = new StringBuilder(); + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader(servletInputStream, StandardCharsets.UTF_8)); + String line; + while ((line = reader.readLine()) != null) { + sb.append(line); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (servletInputStream != null) { + try { + servletInputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return xssEncode(sb.toString()); } @Override @@ -129,7 +138,6 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { if (parameters == null || parameters.length == 0) { return null; } - for (int i = 0; i < parameters.length; i++) { parameters[i] = xssEncode(parameters[i]); } @@ -165,6 +173,7 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { /** * 获取最原始的request + * * @return HttpServletRequest */ public HttpServletRequest getOrgRequest() { @@ -173,6 +182,7 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { /** * 获取最原始的request + * * @param request request * @return HttpServletRequest */ @@ -180,7 +190,6 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { if (request instanceof XssHttpServletRequestWrapper) { return ((XssHttpServletRequestWrapper) request).getOrgRequest(); } - return request; } diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/support/xss/XssProperties.java b/blade-core-tool/src/main/java/org/springblade/core/tool/support/xss/XssProperties.java index 3535e8e..2b4e3d5 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/support/xss/XssProperties.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/support/xss/XssProperties.java @@ -27,9 +27,17 @@ import java.util.List; * @author Chill */ @Data -@ConfigurationProperties("blade.xss.url") +@ConfigurationProperties("blade.xss") public class XssProperties { - private final List excludePatterns = new ArrayList<>(); + /** + * 开启xss + */ + private Boolean enable = true; + + /** + * 放行url + */ + private List skipUrl = new ArrayList<>(); } diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/support/xss/XssUrlProperties.java b/blade-core-tool/src/main/java/org/springblade/core/tool/support/xss/XssUrlProperties.java new file mode 100644 index 0000000..89f45f9 --- /dev/null +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/support/xss/XssUrlProperties.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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 org.springblade.core.tool.support.xss; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.ArrayList; +import java.util.List; + +/** + * Xss配置类 + * + * @author Chill + */ +@Data +@ConfigurationProperties("blade.xss.url") +public class XssUrlProperties { + + private final List excludePatterns = new ArrayList<>(); + +} diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/Func.java b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/Func.java index bf310f1..1533129 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/Func.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/Func.java @@ -1124,7 +1124,7 @@ public class Func { * @param T 泛型标记 * @return Bean */ - public static T parse(byte[] bytes, TypeReference typeReference) { + public static T parse(byte[] bytes, TypeReference typeReference) { return JsonUtil.parse(bytes, typeReference); } @@ -1136,7 +1136,7 @@ public class Func { * @param T 泛型标记 * @return Bean */ - public static T parse(String jsonString, TypeReference typeReference) { + public static T parse(String jsonString, TypeReference typeReference) { return JsonUtil.parse(jsonString, typeReference); } @@ -1148,7 +1148,7 @@ public class Func { * @param T 泛型标记 * @return Bean */ - public static T parse(InputStream in, TypeReference typeReference) { + public static T parse(InputStream in, TypeReference typeReference) { return JsonUtil.parse(in, typeReference); } diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/StringUtil.java b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/StringUtil.java index 7d0c02d..2cdaf7b 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/StringUtil.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/StringUtil.java @@ -186,7 +186,7 @@ public class StringUtil extends org.springframework.util.StringUtils { * @return {String} */ public static String escapeHtml(String html) { - return HtmlUtils.htmlEscape(html); + return StringUtil.isBlank(html) ? StringPool.EMPTY : HtmlUtils.htmlEscape(html); } /** @@ -1250,7 +1250,7 @@ public class StringUtil extends org.springframework.util.StringUtils { /** * 创建StringBuilder对象 * - * @param sb 初始StringBuilder + * @param sb 初始StringBuilder * @param strs 初始字符串列表 * @return StringBuilder对象 */ diff --git a/blade-core-transaction/pom.xml b/blade-core-transaction/pom.xml index a3da188..e7fc256 100644 --- a/blade-core-transaction/pom.xml +++ b/blade-core-transaction/pom.xml @@ -5,7 +5,7 @@ blade-tool org.springblade - 2.5.4 + 2.6.0 4.0.0 diff --git a/pom.xml b/pom.xml index 5256045..5560df8 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springblade blade-tool - 2.5.4 + 2.6.0 pom blade-tool @@ -36,24 +36,24 @@ - 2.5.4 + 2.6.0 1.8 3.8.0 2.9.2 1.5.21 - 1.9.6 - 3.1.2 + 2.0.1 + 3.2.0 4.0.1 1.6.0 3.4.2 - 2.1.5 + 2.2.0 1.1.0 - 2.1.0.RELEASE - 0.9.0 + 2.1.1.RELEASE + 1.0.0 - 2.1.9.RELEASE - Greenwich.SR3 + 2.2.2.RELEASE + Hoxton.SR1 Cairo-SR8 UTF-8