feat: 添加baseMapper方法,将key补齐,修复排序安全性问题,增加多字段排序
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
package com.taixingyiji.base.common;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @className SortItem
|
||||
* @author lhc
|
||||
* @date 2025年12月24日 11:16
|
||||
* @description 描述
|
||||
* @version 1.0
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder(toBuilder = true)
|
||||
@Data
|
||||
@ApiModel
|
||||
public class SortItem implements Serializable {
|
||||
private static final long serialVersionUID = 5462627800342658554L;
|
||||
private String field;
|
||||
private String order;
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import lombok.NoArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* (WebPageInfo)实体类
|
||||
@@ -51,16 +52,38 @@ public class WebPageInfo implements Serializable {
|
||||
example = "asc")
|
||||
private String order = ASC;
|
||||
|
||||
|
||||
@ApiModelProperty(
|
||||
value = "符合排序字段",example = "[{\"field\":\"CREATE_TIME\",\"order\":\"ASC\"}]")
|
||||
private String sortList;
|
||||
|
||||
@ApiModelProperty(
|
||||
value = "开启缓存"
|
||||
)
|
||||
private boolean enableCache = false;
|
||||
|
||||
public static boolean isSafeOrderBy(String orderBy) {
|
||||
if (StringUtils.isBlank(orderBy)) {
|
||||
return true;
|
||||
}
|
||||
// 忽略大小写
|
||||
return orderBy.matches(
|
||||
"(?i)^([a-zA-Z0-9_]+\\s+(asc|desc))(\\s*,\\s*[a-zA-Z0-9_]+\\s+(asc|desc))*$"
|
||||
);
|
||||
}
|
||||
|
||||
public static boolean hasSortList(WebPageInfo webPageInfo) {
|
||||
return !StringUtils.isBlank(webPageInfo.getSortList());
|
||||
}
|
||||
|
||||
public static boolean hasSort(WebPageInfo webPageInfo) {
|
||||
return !StringUtils.isBlank(webPageInfo.getSortField());
|
||||
}
|
||||
|
||||
public String getSortSql() {
|
||||
if(!isSafeOrderBy(this.order)){
|
||||
throw new ServiceException("排序字段不合法");
|
||||
}
|
||||
return this.sortField + " " + this.order;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,21 +3,23 @@ package com.taixingyiji.base.common.utils;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.taixingyiji.base.common.ServiceException;
|
||||
import com.taixingyiji.base.common.SortItem;
|
||||
import com.taixingyiji.base.common.WebPageInfo;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.taixingyiji.base.common.config.FrameConfig;
|
||||
import com.taixingyiji.base.module.cache.CacheService;
|
||||
import com.taixingyiji.base.module.cache.base.BaseCache;
|
||||
import com.taixingyiji.base.module.cache.emum.CacheType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static com.taixingyiji.base.common.WebPageInfo.ASC;
|
||||
import static com.taixingyiji.base.common.WebPageInfo.DESC;
|
||||
|
||||
@Component
|
||||
public class MyPageHelper {
|
||||
|
||||
@@ -33,8 +35,50 @@ public class MyPageHelper {
|
||||
myPageHelper = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验单个字段和排序方向是否合法
|
||||
*/
|
||||
private static boolean isSafeField(String field, String order, Set<String> allowedFields) {
|
||||
if (StringUtils.isBlank(field)) return false;
|
||||
// 字段白名单校验,如果不传 allowedFields,可传 null,则只做格式校验
|
||||
if (allowedFields != null && !allowedFields.contains(field)) return false;
|
||||
// 字段名格式安全
|
||||
if (!field.matches("^[a-zA-Z0-9_]+$")) return false;
|
||||
// 排序方向安全
|
||||
if (StringUtils.isBlank(order)) return true; // 默认 asc
|
||||
return order.equalsIgnoreCase(ASC) || order.equalsIgnoreCase(DESC);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 List<SortItem> 转换为 PageHelper 可用 ORDER BY 字符串
|
||||
*
|
||||
* @param sortList List<SortItem> 前端传入的排序字段
|
||||
* @param allowedFields 可选业务白名单字段(可为 null)
|
||||
* @return 安全的 ORDER BY 字符串,如果没有合法字段返回 null
|
||||
*/
|
||||
public static String buildOrderBy(List<SortItem> sortList, Set<String> allowedFields) {
|
||||
if (sortList == null || sortList.isEmpty()) return null;
|
||||
|
||||
List<String> orderParts = new ArrayList<>();
|
||||
for (SortItem item : sortList) {
|
||||
if (item == null) continue;
|
||||
String field = item.getField();
|
||||
String order = item.getOrder();
|
||||
|
||||
if (!isSafeField(field, order, allowedFields)) continue;
|
||||
|
||||
String dir = DESC.equalsIgnoreCase(order) ? DESC : ASC;
|
||||
orderParts.add(field + " " + dir);
|
||||
}
|
||||
|
||||
return orderParts.isEmpty() ? null : String.join(", ", orderParts);
|
||||
}
|
||||
|
||||
public static void start(WebPageInfo webPageInfo) {
|
||||
if (WebPageInfo.hasSort(webPageInfo)) {
|
||||
if (WebPageInfo.hasSortList(webPageInfo)) {
|
||||
List<SortItem> sortList = JSONUtil.toList(JSONUtil.parseArray(webPageInfo.getSortList()), SortItem.class);
|
||||
PageHelper.startPage(webPageInfo.getPageNum(), webPageInfo.getPageSize(), buildOrderBy(sortList, new HashSet<>())).setAsyncCount(true);
|
||||
} else if (WebPageInfo.hasSort(webPageInfo)) {
|
||||
PageHelper.startPage(webPageInfo.getPageNum(), webPageInfo.getPageSize(), webPageInfo.getSortSql()).setAsyncCount(true);
|
||||
} else {
|
||||
PageHelper.startPage(webPageInfo.getPageNum(), webPageInfo.getPageSize()).setAsyncCount(true);
|
||||
@@ -42,7 +86,10 @@ public class MyPageHelper {
|
||||
}
|
||||
|
||||
public static void noCount(WebPageInfo webPageInfo) {
|
||||
if (WebPageInfo.hasSort(webPageInfo)) {
|
||||
if (WebPageInfo.hasSortList(webPageInfo)) {
|
||||
List<SortItem> sortList = JSONUtil.toList(JSONUtil.parseArray(webPageInfo.getSortList()), SortItem.class);
|
||||
PageHelper.startPage(webPageInfo.getPageNum(), webPageInfo.getPageSize(), false).setOrderBy(buildOrderBy(sortList, new HashSet<>()));
|
||||
} else if (WebPageInfo.hasSort(webPageInfo)) {
|
||||
PageHelper.startPage(webPageInfo.getPageNum(), webPageInfo.getPageSize(), false).setOrderBy(webPageInfo.getSortSql());
|
||||
} else {
|
||||
PageHelper.startPage(webPageInfo.getPageNum(), webPageInfo.getPageSize(), false);
|
||||
@@ -50,7 +97,10 @@ public class MyPageHelper {
|
||||
}
|
||||
|
||||
public static PageInfo<Map<String, Object>> noCount(WebPageInfo webPageInfo, Supplier<List<Map<String, Object>>> querySupplier) {
|
||||
if (WebPageInfo.hasSort(webPageInfo)) {
|
||||
if (WebPageInfo.hasSortList(webPageInfo)) {
|
||||
List<SortItem> sortList = JSONUtil.toList(JSONUtil.parseArray(webPageInfo.getSortList()), SortItem.class);
|
||||
return PageHelper.startPage(webPageInfo.getPageNum(), webPageInfo.getPageSize(), buildOrderBy(sortList, new HashSet<>())).count(false).doSelectPageInfo(querySupplier::get);
|
||||
} else if (WebPageInfo.hasSort(webPageInfo)) {
|
||||
return PageHelper.startPage(webPageInfo.getPageNum(), webPageInfo.getPageSize(), webPageInfo.getSortSql()).count(false).doSelectPageInfo(querySupplier::get);
|
||||
} else {
|
||||
return PageHelper.startPage(webPageInfo.getPageNum(), webPageInfo.getPageSize()).count(false).doSelectPageInfo(querySupplier::get);
|
||||
@@ -58,7 +108,10 @@ public class MyPageHelper {
|
||||
}
|
||||
|
||||
public static PageInfo<Map<String, Object>> myStart(WebPageInfo webPageInfo, Supplier<List<Map<String, Object>>> querySupplier) {
|
||||
if (WebPageInfo.hasSort(webPageInfo)) {
|
||||
if (WebPageInfo.hasSortList(webPageInfo)) {
|
||||
List<SortItem> sortList = JSONUtil.toList(JSONUtil.parseArray(webPageInfo.getSortList()), SortItem.class);
|
||||
return PageHelper.startPage(webPageInfo.getPageNum(), webPageInfo.getPageSize(), buildOrderBy(sortList, new HashSet<>())).doSelectPageInfo(querySupplier::get);
|
||||
} else if (WebPageInfo.hasSort(webPageInfo)) {
|
||||
return PageHelper.startPage(webPageInfo.getPageNum(), webPageInfo.getPageSize(), webPageInfo.getSortSql()).doSelectPageInfo(querySupplier::get);
|
||||
} else {
|
||||
return PageHelper.startPage(webPageInfo.getPageNum(), webPageInfo.getPageSize()).doSelectPageInfo(querySupplier::get);
|
||||
@@ -73,7 +126,7 @@ public class MyPageHelper {
|
||||
PageInfo<Map<String, Object>> result = myStart(webPageInfo, querySupplier);
|
||||
jsonObject.set("time", currentTime);
|
||||
jsonObject.set("count", result.getTotal());
|
||||
if(result.getTotal() > myPageHelper.frameConfig.getPageMaxCache()){
|
||||
if (result.getTotal() > myPageHelper.frameConfig.getPageMaxCache()) {
|
||||
myPageHelper.baseCache.add(CacheType.pageCache.toString(), sql, jsonObject.toString(), String.class);
|
||||
}
|
||||
return result;
|
||||
@@ -102,6 +155,9 @@ public class MyPageHelper {
|
||||
}
|
||||
|
||||
public static void orderBy(String sortField, String order) {
|
||||
if (!WebPageInfo.isSafeOrderBy(order)) {
|
||||
throw new ServiceException("order 不合法");
|
||||
}
|
||||
PageHelper.orderBy(sortField + " " + order);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package com.taixingyiji.base.common.utils;
|
||||
|
||||
|
||||
import static org.thymeleaf.util.StringUtils.split;
|
||||
|
||||
public class XssClass {
|
||||
|
||||
public static boolean sqlInj(String str){
|
||||
if(StringUtils.isBlank(str)){
|
||||
return false;
|
||||
}
|
||||
String injStr = "'| and | exec | insert | select | delete | update |"+
|
||||
" count |*|%| chr | mid | master | truncate | char | declare |;| or |+|,|<script>";
|
||||
String[] injStra = split(injStr,"|");
|
||||
@@ -17,6 +21,9 @@ public class XssClass {
|
||||
}
|
||||
|
||||
public static boolean sqlInjLike(String str){
|
||||
if(StringUtils.isBlank(str)){
|
||||
return false;
|
||||
}
|
||||
String injStr = "'| and | exec | insert | select | delete | update |"+
|
||||
" count |*| chr | mid | master | truncate | char | declare |;| or |+|,|<script>";
|
||||
String[] injStra = split(injStr,"|");
|
||||
|
||||
@@ -83,7 +83,8 @@ public interface BaseMapper {
|
||||
List<Map<String, Object>> selectByCondition(String tableName, String fieldList, Condition condition);
|
||||
|
||||
PageInfo<Map<String, Object>> selectByCondition(Condition condition, WebPageInfo webPageInfo);
|
||||
|
||||
List<Map<String, Object>> selectByConditionAllKey(String tableName, Condition condition);
|
||||
PageInfo<Map<String, Object>> selectByConditionAllKey(String tableName, Condition condition, WebPageInfo webPageInfo);
|
||||
<E> PageInfo<Map<String, Object>> selectByCondition(DataMap<E> dataMap, Condition condition, WebPageInfo webPageInfo);
|
||||
|
||||
PageInfo<Map<String, Object>> selectByCondition(String tableName, Condition condition, WebPageInfo webPageInfo);
|
||||
|
||||
@@ -395,6 +395,33 @@ public class BaseMapperImpl implements BaseMapper {
|
||||
return tableMapper.useSql(SelectCondition.builder().tableName(tableName).build().getSql());
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> selectListAllKey(Condition condition, String tableName) {
|
||||
Map<String, Object> params = condition.getParamMap();
|
||||
params.put("sql", condition.getSql());
|
||||
List<Map<String, Object>> list =
|
||||
sqlSessionTemplate.selectList(TABLE_MAPPER_PACKAGE + "useSql", params);
|
||||
|
||||
if (list == null || list.isEmpty()) {
|
||||
return list;
|
||||
}
|
||||
|
||||
// 收集所有可能出现过的 key
|
||||
Set<String> allKeys = new HashSet<>();
|
||||
for (Map<String, Object> row : list) {
|
||||
allKeys.addAll(row.keySet());
|
||||
}
|
||||
|
||||
// 补全缺失字段
|
||||
for (Map<String, Object> row : list) {
|
||||
for (String key : allKeys) {
|
||||
row.putIfAbsent(key, null);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private List<Map<String, Object>> selectList(Condition condition, String tableName) {
|
||||
Map<String, Object> params = condition.getParamMap();
|
||||
String dataTypeConfig = getDataConfig();
|
||||
@@ -534,6 +561,26 @@ public class BaseMapperImpl implements BaseMapper {
|
||||
return selectList(condition,tableName);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> selectByConditionAllKey(String tableName, Condition condition) {
|
||||
JudgesNull(tableName, "tableName can not be null!");
|
||||
condition = condition.toCreatCriteria(DataMap.builder().tableName(tableName).build()).build();
|
||||
return selectListAllKey(condition,tableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageInfo<Map<String, Object>> selectByConditionAllKey(String tableName, Condition condition, WebPageInfo webPageInfo) {
|
||||
JudgesNull(tableName, "tableName can not be null!");
|
||||
condition = condition.toCreatCriteria(DataMap.builder().tableName(tableName).build()).build();
|
||||
if (webPageInfo.isEnableCache()) {
|
||||
Condition finalCondition = condition;
|
||||
return MyPageHelper.start(webPageInfo, condition.getSql(), () -> selectList(finalCondition,tableName));
|
||||
}
|
||||
MyPageHelper.start(webPageInfo);
|
||||
return new PageInfo<>(selectListAllKey(condition,tableName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageInfo<Map<String, Object>> selectByCondition(Condition condition, WebPageInfo webPageInfo) {
|
||||
MyPageHelper.start(webPageInfo);
|
||||
|
||||
Reference in New Issue
Block a user