🎉 3.7.0.RELEASE

This commit is contained in:
smallchill 2023-09-11 13:37:06 +08:00
parent 0885b48ad9
commit 911ebedaa9
42 changed files with 230 additions and 85 deletions

View File

@ -5,7 +5,7 @@
<parent>
<groupId>org.springblade</groupId>
<artifactId>blade-tool</artifactId>
<version>3.6.0</version>
<version>3.7.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -88,7 +88,7 @@
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.16</version>
<version>1.2.19</version>
</dependency>
<!-- MySQL -->
<dependency>

View File

@ -101,7 +101,7 @@ mybatis-plus:
swagger:
title: SpringBlade 接口文档系统
description: SpringBlade 接口文档系统
version: 3.6.0
version: 3.7.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>3.6.0</version>
<version>3.7.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -25,6 +25,7 @@ public interface TokenConstant {
String AVATAR = "avatar";
String HEADER = "blade-auth";
String BEARER = "bearer";
String CRYPTO = "crypto";
String ACCESS_TOKEN = "access_token";
String REFRESH_TOKEN = "refresh_token";
String TOKEN_TYPE = "token_type";

View File

@ -18,10 +18,14 @@ package org.springblade.core.launch.props;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.lang.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* 配置文件
@ -29,7 +33,9 @@ import java.util.Map;
* @author Chill
*/
@ConfigurationProperties("blade")
public class BladeProperties {
public class BladeProperties implements EnvironmentAware, EnvironmentCapable {
@Nullable
private Environment environment;
/**
* 开发环境
@ -204,4 +210,14 @@ public class BladeProperties {
return prop.containsKey(key);
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@Override
public Environment getEnvironment() {
Objects.requireNonNull(environment, "Spring boot 环境下 Environment 不可能为null");
return this.environment;
}
}

View File

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

View File

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

View File

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

View File

@ -16,6 +16,7 @@
package org.springblade.core.mp.support;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.core.tool.utils.StringUtil;
@ -28,17 +29,23 @@ import java.util.Map;
* @author Chill
*/
public class SqlKeyword {
private final static String SQL_REGEX = "'|%|--|insert|delete|select|count|group|union|drop|truncate|alter|grant|execute|exec|xp_cmdshell|call|declare|sql";
private final static String SQL_REGEX = "'|%|--|insert|delete|select|sleep|count|group|union|drop|truncate|alter|grant|execute|exec|xp_cmdshell|call|declare|sql";
private static final String EQUAL = "_equal";
private static final String NOT_EQUAL = "_notequal";
private static final String LIKE = "_like";
private static final String LIKE_LEFT = "_likeleft";
private static final String LIKE_RIGHT = "_likeright";
private static final String NOT_LIKE = "_notlike";
private static final String GE = "_ge";
private static final String LE = "_le";
private static final String GT = "_gt";
private static final String LT = "_lt";
private static final String DATE_GE = "_datege";
private static final String DATE_GT = "_dategt";
private static final String DATE_EQUAL = "_dateequal";
private static final String DATE_LT = "_datelt";
private static final String DATE_LE = "_datele";
private static final String IS_NULL = "_null";
private static final String NOT_NULL = "_notnull";
private static final String IGNORE = "_ignore";
@ -57,22 +64,36 @@ public class SqlKeyword {
if (Func.hasEmpty(k, v) || k.endsWith(IGNORE)) {
return;
}
// 过滤sql注入关键词
k = filter(k);
if (k.endsWith(EQUAL)) {
qw.eq(getColumn(k, EQUAL), v);
} else if (k.endsWith(NOT_EQUAL)) {
qw.ne(getColumn(k, NOT_EQUAL), v);
} else if (k.endsWith(LIKE_LEFT)) {
qw.likeLeft(getColumn(k, LIKE_LEFT), v);
} else if (k.endsWith(LIKE_RIGHT)) {
qw.likeRight(getColumn(k, LIKE_RIGHT), v);
} else if (k.endsWith(NOT_LIKE)) {
qw.notLike(getColumn(k, NOT_LIKE), v);
} else if (k.endsWith(GE)) {
qw.ge(getColumn(k, GE), v);
} else if (k.endsWith(LE)) {
qw.le(getColumn(k, LE), v);
} else if (k.endsWith(GT)) {
qw.gt(getColumn(k, GT), v);
} else if (k.endsWith(LT)) {
qw.lt(getColumn(k, LT), v);
} else if (k.endsWith(DATE_GE)) {
qw.ge(getColumn(k, DATE_GE), DateUtil.parse(String.valueOf(v), DateUtil.PATTERN_DATETIME));
} else if (k.endsWith(DATE_GT)) {
qw.gt(getColumn(k, DATE_GT), v);
qw.gt(getColumn(k, DATE_GT), DateUtil.parse(String.valueOf(v), DateUtil.PATTERN_DATETIME));
} else if (k.endsWith(DATE_EQUAL)) {
qw.eq(getColumn(k, DATE_EQUAL), v);
qw.eq(getColumn(k, DATE_EQUAL), DateUtil.parse(String.valueOf(v), DateUtil.PATTERN_DATETIME));
} else if (k.endsWith(DATE_LE)) {
qw.le(getColumn(k, DATE_LE), DateUtil.parse(String.valueOf(v), DateUtil.PATTERN_DATETIME));
} else if (k.endsWith(DATE_LT)) {
qw.lt(getColumn(k, DATE_LT), v);
qw.lt(getColumn(k, DATE_LT), DateUtil.parse(String.valueOf(v), DateUtil.PATTERN_DATETIME));
} else if (k.endsWith(IS_NULL)) {
qw.isNull(getColumn(k, IS_NULL));
} else if (k.endsWith(NOT_NULL)) {
@ -106,5 +127,4 @@ public class SqlKeyword {
}
return param.replaceAll("(?i)" + SQL_REGEX, StringPool.EMPTY);
}
}

View File

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

View File

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

View File

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

View File

@ -15,11 +15,16 @@
*/
package org.springblade.core.secure.auth;
import org.springblade.core.launch.constant.TokenConstant;
import org.springblade.core.secure.utils.SecureUtil;
import org.springblade.core.tool.constant.RoleConstant;
import org.springblade.core.tool.utils.CollectionUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.core.tool.utils.WebUtil;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
/**
* 权限判断
@ -76,4 +81,17 @@ public class AuthFun {
return false;
}
/**
* 判断请求是否为加密token
*
* @return {boolean}
*/
public boolean hasCrypto() {
HttpServletRequest request = WebUtil.getRequest();
String auth = Objects.requireNonNull(request).getHeader(TokenConstant.HEADER);
return SecureUtil.isCrypto(
StringUtil.isNotBlank(auth) ? auth : request.getParameter(TokenConstant.HEADER)
);
}
}

View File

@ -37,6 +37,11 @@ public class BladeTokenProperties {
*/
private String signKey = StringPool.EMPTY;
/**
* token签名
*/
private String aesKey = StringPool.EMPTY;
/**
* 获取签名规则
*/

View File

@ -46,6 +46,7 @@ public class SecureUtil {
private final static String HEADER = TokenConstant.HEADER;
private final static String BEARER = TokenConstant.BEARER;
private final static String CRYPTO = TokenConstant.CRYPTO;
private final static String ACCOUNT = TokenConstant.ACCOUNT;
private final static String USER_ID = TokenConstant.USER_ID;
private final static String ROLE_ID = TokenConstant.ROLE_ID;
@ -292,21 +293,56 @@ public class SecureUtil {
*/
public static Claims getClaims(HttpServletRequest request) {
String auth = request.getHeader(SecureUtil.HEADER);
if (StringUtil.isNotBlank(auth) && auth.length() > AUTH_LENGTH) {
String headStr = auth.substring(0, 6).toLowerCase();
if (headStr.compareTo(SecureUtil.BEARER) == 0) {
auth = auth.substring(7);
return SecureUtil.parseJWT(auth);
String token = getToken(
StringUtil.isNotBlank(auth) ? auth : request.getParameter(SecureUtil.HEADER)
);
return SecureUtil.parseJWT(token);
}
} else {
String parameter = request.getParameter(SecureUtil.HEADER);
if (StringUtil.isNotBlank(parameter)) {
return SecureUtil.parseJWT(parameter);
/**
* 获取请求传递的token串
*
* @param auth token
* @return String
*/
public static String getToken(String auth) {
if (isBearer(auth)) {
return auth.substring(AUTH_LENGTH);
}
if (isCrypto(auth)) {
return AesUtil.decryptFormBase64ToString(auth.substring(AUTH_LENGTH), getTokenProperties().getAesKey());
}
return null;
}
/**
* 判断token类型为bearer
*
* @param auth token
* @return String
*/
public static Boolean isBearer(String auth) {
if ((auth != null) && (auth.length() > AUTH_LENGTH)) {
String headStr = auth.substring(0, 6).toLowerCase();
return headStr.compareTo(BEARER) == 0;
}
return false;
}
/**
* 判断token类型为crypto
*
* @param auth token
* @return String
*/
public static Boolean isCrypto(String auth) {
if ((auth != null) && (auth.length() > AUTH_LENGTH)) {
String headStr = auth.substring(0, 6).toLowerCase();
return headStr.compareTo(CRYPTO) == 0;
}
return false;
}
/**
* 获取请求头
*

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -215,6 +215,7 @@ public class R<T> implements Serializable {
* 返回R
*
* @param flag 成功状态
* @param <T> 泛型
* @return R
*/
public static <T> R<T> status(boolean flag) {

View File

@ -42,6 +42,8 @@ public class RequestConfiguration {
/**
* 全局过滤器
*
* @return 自定义过滤器
*/
@Bean
public FilterRegistrationBean<BladeRequestFilter> bladeFilterRegistration() {

View File

@ -69,6 +69,10 @@ public interface BladeConstant {
* 顶级父节点id
*/
Long TOP_PARENT_ID = 0L;
/**
* 顶级父节点名称
*/
String TOP_PARENT_NAME = "顶级";
/**
* 管理员对应的租户ID

View File

@ -34,4 +34,6 @@ public class RoleConstant {
public static final String HAS_ROLE_TEST = "hasRole('" + TEST + "')";
public static final String HAS_CRYPTO = "hasCrypto()";
}

View File

@ -45,6 +45,7 @@ public class MappingApiJackson2HttpMessageConverter extends AbstractReadWriteJac
* You can use {@link Jackson2ObjectMapperBuilder} to build it easily.
*
* @param objectMapper ObjectMapper
* @param properties properties
* @see Jackson2ObjectMapperBuilder#json()
*/
public MappingApiJackson2HttpMessageConverter(ObjectMapper objectMapper, BladeJacksonProperties properties) {

View File

@ -26,10 +26,10 @@ import java.util.List;
/**
* 节点基类
*
* @author Chill
* @author smallchill
*/
@Data
public class BaseNode implements INode {
public class BaseNode<T> implements INode<T> {
private static final long serialVersionUID = 1L;
@ -49,7 +49,7 @@ public class BaseNode implements INode {
* 子孙节点
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
protected List<INode> children = new ArrayList<>();
protected List<T> children = new ArrayList<T>();
/**
* 是否有子孙节点
@ -59,6 +59,8 @@ public class BaseNode implements INode {
/**
* 是否有子孙节点
*
* @return Boolean
*/
@Override
public Boolean getHasChildren() {

View File

@ -22,11 +22,11 @@ import lombok.EqualsAndHashCode;
/**
* 森林节点类
*
* @author Chill
* @author smallchill
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class ForestNode extends BaseNode {
public class ForestNode extends BaseNode<ForestNode> {
private static final long serialVersionUID = 1L;

View File

@ -15,28 +15,33 @@
*/
package org.springblade.core.tool.node;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import org.springblade.core.tool.utils.StringPool;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 森林管理类
*
* @author Chill
* @author smallchill
*/
public class ForestNodeManager<T extends INode> {
public class ForestNodeManager<T extends INode<T>> {
/**
* 森林的所有节点
*/
private List<T> list;
private final ImmutableMap<Long, T> nodeMap;
/**
* 森林的父节点ID
*/
private List<Long> parentIds = new ArrayList<>();
private final Map<Long, Object> parentIdMap = Maps.newHashMap();
public ForestNodeManager(List<T> items) {
list = items;
public ForestNodeManager(List<T> nodes) {
nodeMap = Maps.uniqueIndex(nodes, INode::getId);
}
/**
@ -45,11 +50,9 @@ public class ForestNodeManager<T extends INode> {
* @param id 节点ID
* @return 对应的节点对象
*/
public INode getTreeNodeAT(Long id) {
for (INode forestNode : list) {
if (forestNode.getId().longValue() == id) {
return forestNode;
}
public INode<T> getTreeNodeAt(Long id) {
if (nodeMap.containsKey(id)) {
return nodeMap.get(id);
}
return null;
}
@ -60,7 +63,7 @@ public class ForestNodeManager<T extends INode> {
* @param parentId 父节点ID
*/
public void addParentId(Long parentId) {
parentIds.add(parentId);
parentIdMap.put(parentId, StringPool.EMPTY);
}
/**
@ -70,11 +73,11 @@ public class ForestNodeManager<T extends INode> {
*/
public List<T> getRoot() {
List<T> roots = new ArrayList<>();
for (T forestNode : list) {
if (forestNode.getParentId() == 0 || parentIds.contains(forestNode.getId())) {
roots.add(forestNode);
}
nodeMap.forEach((key, node) -> {
if (node.getParentId() == 0 || parentIdMap.containsKey(node.getId())) {
roots.add(node);
}
});
return roots;
}

View File

@ -20,7 +20,7 @@ import java.util.List;
/**
* 森林节点归并类
*
* @author Chill
* @author smallchill
*/
public class ForestNodeMerger {
@ -29,14 +29,14 @@ public class ForestNodeMerger {
* 时间复杂度为O(n^2)
*
* @param items 节点域
* @param <T> T 泛型标记
* @param <T> 泛型
* @return 多棵树的根节点集合
*/
public static <T extends INode> List<T> merge(List<T> items) {
public static <T extends INode<T>> List<T> merge(List<T> items) {
ForestNodeManager<T> forestNodeManager = new ForestNodeManager<>(items);
items.forEach(forestNode -> {
if (forestNode.getParentId() != 0) {
INode node = forestNodeManager.getTreeNodeAT(forestNode.getParentId());
INode<T> node = forestNodeManager.getTreeNodeAt(forestNode.getParentId());
if (node != null) {
node.getChildren().add(forestNode);
} else {

View File

@ -21,21 +21,21 @@ import java.util.List;
/**
* Created by Blade.
*
* @author Chill
* @author smallchill
*/
public interface INode extends Serializable {
public interface INode<T> extends Serializable {
/**
* 主键
*
* @return Integer
* @return Long
*/
Long getId();
/**
* 父主键
*
* @return Integer
* @return Long
*/
Long getParentId();
@ -44,7 +44,7 @@ public interface INode extends Serializable {
*
* @return List
*/
List<INode> getChildren();
List<T> getChildren();
/**
* 是否有子孙节点

View File

@ -8,7 +8,7 @@ import java.util.List;
/**
* Created by Blade.
*
* @author Chill
* @author smallchill
*/
public class NodeTest {

View File

@ -18,16 +18,17 @@ package org.springblade.core.tool.node;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springblade.core.tool.utils.Func;
import java.util.Objects;
/**
* 树型节点类
*
* @author Chill
* @author smallchill
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class TreeNode extends BaseNode {
public class TreeNode extends BaseNode<TreeNode> {
private static final long serialVersionUID = 1L;
@ -39,4 +40,21 @@ public class TreeNode extends BaseNode {
@JsonSerialize(using = ToStringSerializer.class)
private Long value;
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
TreeNode other = (TreeNode) obj;
return Func.equals(this.getId(), other.getId());
}
@Override
public int hashCode() {
return Objects.hash(id, parentId);
}
}

View File

@ -406,6 +406,7 @@ public class DateUtil {
* @param dateStr 时间字符串
* @param pattern 表达式
* @param query 移动查询
* @param <T> 泛型
* @return 时间
*/
public static <T> T parse(String dateStr, String pattern, TemporalQuery<T> query) {

View File

@ -398,6 +398,20 @@ public class Func {
return String.valueOf(str);
}
/**
* 强转string(包含空字符串),并去掉多余空格
*
* @param str 字符串
* @param defaultValue 默认值
* @return {String}
*/
public static String toStrWithEmpty(Object str, String defaultValue) {
if (null == str || str.equals(StringPool.NULL) || str.equals(StringPool.EMPTY)) {
return defaultValue;
}
return String.valueOf(str);
}
/**
* 判断一个字符串是否是数字
*

View File

@ -477,9 +477,10 @@ public final class ImageUtil {
/**
* 默认字符串
*
* @param str 字符串
* @param defaultStr 默认值
* @return
* @return 字符串
*/
public static String defaultString(String str, String defaultStr) {
return ((str == null) ? defaultStr : str);

View File

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

14
pom.xml
View File

@ -5,7 +5,7 @@
<groupId>org.springblade</groupId>
<artifactId>blade-tool</artifactId>
<version>3.6.0</version>
<version>3.7.0</version>
<packaging>pom</packaging>
<name>blade-tool</name>
<description>
@ -36,14 +36,14 @@
</scm>
<properties>
<blade.tool.version>3.6.0</blade.tool.version>
<blade.tool.version>3.7.0</blade.tool.version>
<java.version>1.8</java.version>
<maven.plugin.version>3.8.1</maven.plugin.version>
<knife4j.version>4.1.0</knife4j.version>
<mybatis.plus.version>3.5.3.1</mybatis.plus.version>
<mybatis.plus.generator.version>3.5.3.1</mybatis.plus.generator.version>
<mybatis.plus.version>3.5.3.2</mybatis.plus.version>
<mybatis.plus.generator.version>3.5.3.2</mybatis.plus.generator.version>
<protostuff.version>1.6.0</protostuff.version>
<disruptor.version>3.4.2</disruptor.version>
<guava.version>31.1-jre</guava.version>
@ -52,10 +52,10 @@
<alibaba.cloud.version>2021.0.5.0</alibaba.cloud.version>
<alibaba.nacos.version>2.1.2</alibaba.nacos.version>
<spring.version>5.3.27</spring.version>
<spring.boot.version>2.7.10</spring.boot.version>
<spring.version>5.3.29</spring.version>
<spring.boot.version>2.7.15</spring.boot.version>
<spring.boot.admin.version>2.7.10</spring.boot.admin.version>
<spring.cloud.version>2021.0.6</spring.cloud.version>
<spring.cloud.version>2021.0.8</spring.cloud.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>