🎉 2.6.0.RELEASE 升级Hoxton.SR1 适配最新架构

This commit is contained in:
smallchill 2019-12-22 23:43:17 +08:00
parent 457ab4088f
commit 181063c231
39 changed files with 419 additions and 182 deletions

View File

@ -1,7 +1,7 @@
<p align="center">
<img src="https://img.shields.io/badge/license-LGPL%20v3-blue.svg" alt="Build Status">
<img src="https://img.shields.io/badge/Spring%20Cloud-Greenwich.SR3-blue.svg" alt="Coverage Status">
<img src="https://img.shields.io/badge/Spring%20Boot-2.1.9.RELEASE-blue.svg" alt="Downloads">
<img src="https://img.shields.io/badge/Spring%20Cloud-Hoxton.SR1-blue.svg" alt="Coverage Status">
<img src="https://img.shields.io/badge/Spring%20Boot-2.2.2.RELEASE-blue.svg" alt="Downloads">
</p>
## SpringBlade微服务开发平台

View File

@ -5,7 +5,7 @@
<parent>
<groupId>org.springblade</groupId>
<artifactId>blade-tool</artifactId>
<version>2.5.4</version>
<version>2.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -62,28 +62,6 @@
<artifactId>blade-core-swagger</artifactId>
<version>${blade.tool.version}</version>
</dependency>
<!--Swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
<exclusions>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>${swagger.models.version}</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>${swagger.bootstrapui.version}</version>
</dependency>
<!--Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@ -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();
}
}

View File

@ -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));
}

View File

@ -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

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>2.5.4</version>
<version>2.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>2.5.4</version>
<version>2.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -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;

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>2.5.4</version>
<version>2.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -25,7 +25,7 @@ public interface AppConstant {
/**
* 应用版本
*/
String APPLICATION_VERSION = "2.5.4";
String APPLICATION_VERSION = "2.6.0";
/**
* 基础包

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>2.5.4</version>
<version>2.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>2.5.4</version>
<version>2.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -32,7 +32,7 @@ public interface BaseService<T> extends IService<T> {
* 逻辑删除
*
* @param ids id集合(逗号分隔)
* @return
* @return boolean
*/
boolean deleteLogic(@NotEmpty List<Integer> ids);

View File

@ -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<M extends BaseMapper<T>, T extends BaseEntity> extends ServiceImpl<M, T> implements BaseService<T> {
private Class<T> modelClass;
@SuppressWarnings("unchecked")
public BaseServiceImpl() {
Type type = this.getClass().getGenericSuperclass();
this.modelClass = (Class<T>) ((ParameterizedType) type).getActualTypeArguments()[1];
}
@Override
public boolean save(T entity) {
BladeUser user = SecureUtil.getUser();

View File

@ -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 {
/**

View File

@ -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<Integer> 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<Integer> list = new ArrayList<>(set);
list.sort(Comparator.naturalOrder());
return list.get(0);
}
}

View File

@ -30,15 +30,17 @@ public abstract class BaseEntityWrapper<E, V> {
/**
* 单个实体类包装
* @param entity
* @return
*
* @param entity 实体类
* @return V
*/
public abstract V entityVO(E entity);
/**
* 实体类集合包装
* @param list
* @return
*
* @param list 猎豹
* @return List V
*/
public List<V> listVO(List<E> list) {
return list.stream().map(this::entityVO).collect(Collectors.toList());
@ -46,8 +48,9 @@ public abstract class BaseEntityWrapper<E, V> {
/**
* 分页实体类集合包装
* @param pages
* @return
*
* @param pages 分页
* @return Page V
*/
public IPage<V> pageVO(IPage<E> pages) {
List<V> records = listVO(pages.getRecords());

View File

@ -33,8 +33,8 @@ public class Condition {
/**
* 转化成mybatis plus中的Page
*
* @param query
* @return
* @param query 查询包装类
* @return Page T
*/
public static <T> IPage<T> getPage(Query query) {
Page<T> 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 <T>
* @return
* @param entity 实体类
* @param <T> 泛型
* @return QueryWrapper
*/
public static <T> QueryWrapper<T> getQueryWrapper(T entity) {
return new QueryWrapper<>(entity);
@ -57,10 +57,10 @@ public class Condition {
/**
* 获取mybatis plus中的QueryWrapper
*
* @param query
* @param clazz
* @param <T>
* @return
* @param query 查询包装类
* @param clazz 实体类
* @param <T> 泛型
* @return QueryWrapper
*/
public static <T> QueryWrapper<T> getQueryWrapper(Map<String, Object> query, Class<T> clazz) {
query.remove("current");

View File

@ -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));

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>2.5.4</version>
<version>2.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>2.5.4</version>
<version>2.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>2.5.4</version>
<version>2.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -23,26 +23,10 @@
<version>${blade.tool.version}</version>
</dependency>
<!--Swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
<exclusions>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>${swagger.models.version}</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>${swagger.bootstrapui.version}</version>
<artifactId>knife4j-micro-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
</dependencies>

View File

@ -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";

View File

@ -55,7 +55,7 @@ public class SwaggerProperties {
/**
* 版本
**/
private String version = "2.5.4";
private String version = "2.6.0";
/**
* 许可证
**/

View File

@ -5,7 +5,7 @@
<parent>
<groupId>org.springblade</groupId>
<artifactId>blade-tool</artifactId>
<version>2.5.4</version>
<version>2.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.springblade</groupId>
<artifactId>blade-tool</artifactId>
<version>2.5.4</version>
<version>2.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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 {
/**

View File

@ -0,0 +1,58 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <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 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<XssFilter> xssFilterRegistration() {
FilterRegistrationBean<XssFilter> 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;
}
}

View File

@ -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> T 泛型标记
* @return Bean
*/
public static <T> T parse(String content, TypeReference<?> typeReference) {
public static <T> T parse(String content, TypeReference<T> typeReference) {
try {
return getInstance().readValue(content, typeReference);
} catch (IOException e) {
@ -130,7 +131,7 @@ public class JsonUtil {
* @param <T> T 泛型标记
* @return Bean
*/
public static <T> T parse(byte[] bytes, TypeReference<?> typeReference) {
public static <T> T parse(byte[] bytes, TypeReference<T> typeReference) {
try {
return getInstance().readValue(bytes, typeReference);
} catch (IOException e) {
@ -162,7 +163,7 @@ public class JsonUtil {
* @param <T> T 泛型标记
* @return Bean
*/
public static <T> T parse(InputStream in, TypeReference<?> typeReference) {
public static <T> T parse(InputStream in, TypeReference<T> 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<Map<String, Object>> list = getInstance().readValue(content, new TypeReference<List<T>>() {
List<Map<String, Object>> list = getInstance().readValue(content, new TypeReference<List<Map<String, Object>>>() {
});
List<T> result = new ArrayList<>();
for (Map<String, Object> map : list) {
@ -208,7 +209,7 @@ public class JsonUtil {
public static <T> Map<String, T> toMap(String content, Class<T> valueTypeRef) {
try {
Map<String, Map<String, Object>> map = getInstance().readValue(content, new TypeReference<Map<String, T>>() {
Map<String, Map<String, Object>> map = getInstance().readValue(content, new TypeReference<Map<String, Map<String, Object>>>() {
});
Map<String, T> result = new HashMap<>(16);
for (Map.Entry<String, Map<String, Object>> 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);

View File

@ -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() {

View File

@ -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;
}

View File

@ -27,9 +27,17 @@ import java.util.List;
* @author Chill
*/
@Data
@ConfigurationProperties("blade.xss.url")
@ConfigurationProperties("blade.xss")
public class XssProperties {
private final List<String> excludePatterns = new ArrayList<>();
/**
* 开启xss
*/
private Boolean enable = true;
/**
* 放行url
*/
private List<String> skipUrl = new ArrayList<>();
}

View File

@ -0,0 +1,35 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <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 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<String> excludePatterns = new ArrayList<>();
}

View File

@ -1124,7 +1124,7 @@ public class Func {
* @param <T> T 泛型标记
* @return Bean
*/
public static <T> T parse(byte[] bytes, TypeReference<?> typeReference) {
public static <T> T parse(byte[] bytes, TypeReference<T> typeReference) {
return JsonUtil.parse(bytes, typeReference);
}
@ -1136,7 +1136,7 @@ public class Func {
* @param <T> T 泛型标记
* @return Bean
*/
public static <T> T parse(String jsonString, TypeReference<?> typeReference) {
public static <T> T parse(String jsonString, TypeReference<T> typeReference) {
return JsonUtil.parse(jsonString, typeReference);
}
@ -1148,7 +1148,7 @@ public class Func {
* @param <T> T 泛型标记
* @return Bean
*/
public static <T> T parse(InputStream in, TypeReference<?> typeReference) {
public static <T> T parse(InputStream in, TypeReference<T> typeReference) {
return JsonUtil.parse(in, typeReference);
}

View File

@ -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对象
*/

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>2.5.4</version>
<version>2.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

18
pom.xml
View File

@ -5,7 +5,7 @@
<groupId>org.springblade</groupId>
<artifactId>blade-tool</artifactId>
<version>2.5.4</version>
<version>2.6.0</version>
<packaging>pom</packaging>
<name>blade-tool</name>
<description>
@ -36,24 +36,24 @@
</scm>
<properties>
<blade.tool.version>2.5.4</blade.tool.version>
<blade.tool.version>2.6.0</blade.tool.version>
<java.version>1.8</java.version>
<maven.plugin.version>3.8.0</maven.plugin.version>
<swagger.version>2.9.2</swagger.version>
<swagger.models.version>1.5.21</swagger.models.version>
<swagger.bootstrapui.version>1.9.6</swagger.bootstrapui.version>
<mybatis.plus.version>3.1.2</mybatis.plus.version>
<knife4j.version>2.0.1</knife4j.version>
<mybatis.plus.version>3.2.0</mybatis.plus.version>
<curator.framework.version>4.0.1</curator.framework.version>
<protostuff.version>1.6.0</protostuff.version>
<disruptor.version>3.4.2</disruptor.version>
<spring.boot.admin.version>2.1.5</spring.boot.admin.version>
<spring.boot.admin.version>2.2.0</spring.boot.admin.version>
<mica.auto.version>1.1.0</mica.auto.version>
<alibaba.cloud.version>2.1.0.RELEASE</alibaba.cloud.version>
<alibaba.seata.version>0.9.0</alibaba.seata.version>
<alibaba.cloud.version>2.1.1.RELEASE</alibaba.cloud.version>
<alibaba.seata.version>1.0.0</alibaba.seata.version>
<spring.boot.version>2.1.9.RELEASE</spring.boot.version>
<spring.cloud.version>Greenwich.SR3</spring.cloud.version>
<spring.boot.version>2.2.2.RELEASE</spring.boot.version>
<spring.cloud.version>Hoxton.SR1</spring.cloud.version>
<spring.platform.version>Cairo-SR8</spring.platform.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>