components添加前端组件searchSelect
This commit is contained in:
parent
782cca521a
commit
463a18f209
|
@ -3,6 +3,9 @@ package com.ruoyi.common.utils.sql;
|
|||
import com.ruoyi.common.exception.UtilException;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* sql操作工具类
|
||||
*
|
||||
|
@ -25,6 +28,25 @@ public class SqlUtil
|
|||
*/
|
||||
private static final int ORDER_BY_MAX_LENGTH = 500;
|
||||
|
||||
private final static String XSS_STR = "and |extractvalue|updatexml|geohash|gtid_subset|gtid_subtract|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|or |+|user()";
|
||||
|
||||
/**
|
||||
* 正则 user() 匹配更严谨
|
||||
*/
|
||||
private final static String REGULAR_EXPRE_USER = "user[\\s]*\\([\\s]*\\)";
|
||||
/**正则 show tables*/
|
||||
private final static String SHOW_TABLES = "show\\s+tables";
|
||||
|
||||
/**
|
||||
* sleep函数
|
||||
*/
|
||||
private final static Pattern FUN_SLEEP = Pattern.compile("sleep\\(.*\\)", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
/**
|
||||
* sql注释的正则
|
||||
*/
|
||||
private final static Pattern SQL_ANNOTATION = Pattern.compile("/\\*[\\s\\S]*\\*/");
|
||||
|
||||
/**
|
||||
* 检查字符,防止注入绕过
|
||||
*/
|
||||
|
@ -67,4 +89,90 @@ public class SqlUtil
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回查询表名
|
||||
* <p>
|
||||
* sql注入过滤处理,遇到注入关键字抛异常
|
||||
*
|
||||
* @param table
|
||||
*/
|
||||
private static Pattern tableNamePattern = Pattern.compile("^[a-zA-Z][a-zA-Z0-9_]{0,63}$");
|
||||
public static String getSqlInjectTableName(String table) {
|
||||
table = table.trim();
|
||||
/**
|
||||
* 检验表名是否合法
|
||||
*
|
||||
* 表名只能由字母、数字和下划线组成。
|
||||
* 表名必须以字母开头。
|
||||
* 表名长度通常有限制,例如最多为 64 个字符。
|
||||
*/
|
||||
boolean isValidTableName = tableNamePattern.matcher(table).matches();
|
||||
if (!isValidTableName) {
|
||||
String errorMsg = "表名不合法,存在SQL注入风险!--->" + table;
|
||||
throw new UtilException(errorMsg);
|
||||
}
|
||||
|
||||
//进一步验证是否存在SQL注入风险
|
||||
filterContent(table, null);
|
||||
return table;
|
||||
}
|
||||
|
||||
/**
|
||||
* sql注入过滤处理,遇到注入关键字抛异常
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public static void filterContent(String value, String customXssString) {
|
||||
if (value == null || "".equals(value)) {
|
||||
return;
|
||||
}
|
||||
// 校验sql注释 不允许有sql注释
|
||||
checkSqlAnnotation(value);
|
||||
// 统一转为小写
|
||||
value = value.toLowerCase();
|
||||
//SQL注入检测存在绕过风险 https://gitee.com/jeecg/jeecg-boot/issues/I4NZGE
|
||||
//value = value.replaceAll("/\\*.*\\*/","");
|
||||
|
||||
String[] xssArr = XSS_STR.split("\\|");
|
||||
for (int i = 0; i < xssArr.length; i++) {
|
||||
if (value.indexOf(xssArr[i]) > -1) {
|
||||
throw new UtilException("请注意,值可能存在SQL注入风险!--->" + value);
|
||||
}
|
||||
}
|
||||
//update-begin-author:taoyan date:2022-7-13 for: 除了XSS_STR这些提前设置好的,还需要额外的校验比如 单引号
|
||||
if (customXssString != null) {
|
||||
String[] xssArr2 = customXssString.split("\\|");
|
||||
for (int i = 0; i < xssArr2.length; i++) {
|
||||
if (value.indexOf(xssArr2[i]) > -1) {
|
||||
throw new UtilException("请注意,值可能存在SQL注入风险!--->" + value);
|
||||
}
|
||||
}
|
||||
}
|
||||
//update-end-author:taoyan date:2022-7-13 for: 除了XSS_STR这些提前设置好的,还需要额外的校验比如 单引号
|
||||
if(Pattern.matches(SHOW_TABLES, value) || Pattern.matches(REGULAR_EXPRE_USER, value)){
|
||||
throw new UtilException("请注意,值可能存在SQL注入风险!--->" + value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验是否有sql注释
|
||||
* @return
|
||||
*/
|
||||
public static void checkSqlAnnotation(String str){
|
||||
Matcher matcher = SQL_ANNOTATION.matcher(str);
|
||||
if(matcher.find()){
|
||||
String error = "请注意,值可能存在SQL注入风险---> \\*.*\\";
|
||||
throw new UtilException(error);
|
||||
}
|
||||
|
||||
// issues/4737 sys/duplicate/check SQL注入 #4737
|
||||
Matcher sleepMatcher = FUN_SLEEP.matcher(str);
|
||||
if(sleepMatcher.find()){
|
||||
String error = "请注意,值可能存在SQL注入风险---> sleep";
|
||||
throw new UtilException(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -260,4 +260,23 @@ public class GenController extends BaseController
|
|||
response.setContentType("application/octet-stream; charset=UTF-8");
|
||||
IOUtils.write(data, response.getOutputStream());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询业务列表
|
||||
*/
|
||||
@GetMapping("/getSearchSelectData")
|
||||
public AjaxResult getSearchSelectData( String tableName, String value, String label, String where, String groupby, String orderby)
|
||||
{
|
||||
orderby = SqlUtil.escapeOrderBySql(orderby);
|
||||
tableName = SqlUtil.getSqlInjectTableName(tableName);
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("tableName", tableName);
|
||||
map.put("value", SqlUtil.getSqlInjectTableName(value));
|
||||
map.put("label", SqlUtil.getSqlInjectTableName(label));
|
||||
map.put("where", where);
|
||||
map.put("groupby", groupby);
|
||||
map.put("orderby", orderby);
|
||||
List<Map> list = genTableService.getSearchSelectData(map);
|
||||
return AjaxResult.success(list);
|
||||
}
|
||||
}
|
|
@ -98,6 +98,10 @@ public class GenTable extends BaseEntity
|
|||
/** 上级菜单名称字段 */
|
||||
private String parentMenuName;
|
||||
|
||||
private String remark;
|
||||
|
||||
private Integer columnCount;
|
||||
|
||||
public Long getTableId()
|
||||
{
|
||||
return tableId;
|
||||
|
@ -343,6 +347,14 @@ public class GenTable extends BaseEntity
|
|||
return isSub(this.tplCategory);
|
||||
}
|
||||
|
||||
public Integer getColumnCount() {
|
||||
return columnCount;
|
||||
}
|
||||
|
||||
public void setColumnCount(Integer columnCount) {
|
||||
this.columnCount = columnCount;
|
||||
}
|
||||
|
||||
public static boolean isSub(String tplCategory)
|
||||
{
|
||||
return tplCategory != null && StringUtils.equals(GenConstants.TPL_SUB, tplCategory);
|
||||
|
|
|
@ -19,6 +19,8 @@ public class GenTableColumn extends BaseEntity
|
|||
/** 归属表编号 */
|
||||
private Long tableId;
|
||||
|
||||
private String tableName;
|
||||
|
||||
/** 列名称 */
|
||||
private String columnName;
|
||||
|
||||
|
@ -78,6 +80,14 @@ public class GenTableColumn extends BaseEntity
|
|||
return columnId;
|
||||
}
|
||||
|
||||
public String getTableName() {
|
||||
return tableName;
|
||||
}
|
||||
|
||||
public void setTableName(String tableName) {
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
public void setTableId(Long tableId)
|
||||
{
|
||||
this.tableId = tableId;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package com.ruoyi.generator.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.ruoyi.generator.domain.GenTable;
|
||||
|
||||
/**
|
||||
|
@ -88,4 +90,6 @@ public interface GenTableMapper
|
|||
* @return 结果
|
||||
*/
|
||||
public int createTable(String sql);
|
||||
|
||||
List<Map> getSearchSelectData(Map<String, Object> map);
|
||||
}
|
||||
|
|
|
@ -528,4 +528,10 @@ public class GenTableServiceImpl implements IGenTableService
|
|||
}
|
||||
return genPath + File.separator + VelocityUtils.getFileName(template, table);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map> getSearchSelectData(Map<String, Object> map ) {
|
||||
|
||||
return genTableMapper.getSearchSelectData(map);
|
||||
}
|
||||
}
|
|
@ -127,4 +127,6 @@ public interface IGenTableService
|
|||
* @param genTable 业务信息
|
||||
*/
|
||||
public void validateEdit(GenTable genTable);
|
||||
|
||||
List<Map> getSearchSelectData(Map<String, Object> map );
|
||||
}
|
||||
|
|
|
@ -174,6 +174,23 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
<update id="createTable">
|
||||
${sql}
|
||||
</update>
|
||||
|
||||
<select id="getSearchSelectData" resultType="java.util.Map">
|
||||
select
|
||||
${value} as value,
|
||||
${label} as label
|
||||
from
|
||||
${tableName}
|
||||
<if test="where != null and where != ''">
|
||||
where ${where}
|
||||
</if>
|
||||
<if test="groupby != null and groupby != ''">
|
||||
group by ${groupby}
|
||||
</if>
|
||||
<if test="orderby != null and orderby != ''">
|
||||
order by ${orderby}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<update id="updateGenTable" parameterType="GenTable">
|
||||
update gen_table
|
||||
|
|
|
@ -83,3 +83,11 @@ export function synchDb(tableName) {
|
|||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function getSearchSelectData(param){
|
||||
return request({
|
||||
url: '/tool/gen/getSearchSelectData',
|
||||
method: 'get',
|
||||
params: param
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-select
|
||||
filterable
|
||||
slot="reference"
|
||||
ref="select"
|
||||
:size="size"
|
||||
v-model="selectedData"
|
||||
:multiple="multiple"
|
||||
:clearable="clearable"
|
||||
:disabled="disabled"
|
||||
@change="changeSelected"
|
||||
>
|
||||
<el-option v-for="item in options" :key="item.label+'-'+item.value" :label="item.label" :value="item.value" >
|
||||
<span style="float: left">{{ item.label }}</span>
|
||||
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.value }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import {getSearchSelectData } from '@/api/tool/gen';
|
||||
export default {
|
||||
name: 'search-select',
|
||||
props: {
|
||||
value: [Number, String],
|
||||
// 参数:表名,value,label,where条件,orderby排序:"t_bd_factory,id,name"
|
||||
params: {
|
||||
type: String,
|
||||
default() {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
where: {
|
||||
type: String,
|
||||
default() {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
orderby: {
|
||||
type: String,
|
||||
default() {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
groupby: {
|
||||
type: String,
|
||||
default() {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
defaultProps: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
// 配置是否可多选
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
// 配置是否可清空选择
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default() {
|
||||
return 'small';
|
||||
}
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
default() {
|
||||
return 250;
|
||||
}
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default() {
|
||||
return 300;
|
||||
}
|
||||
},
|
||||
scope: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
options: [],
|
||||
selectedData: [], // 选中的节点
|
||||
style: 'width:' + this.width + 'px;' + 'height:' + this.height + 'px;',
|
||||
selectStyle: 'width:' + (this.width + 24) + 'px;',
|
||||
checkedIds: [],
|
||||
checkedData: []
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.initData();
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
deep: true,
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
this.selectedData = val;
|
||||
}
|
||||
},
|
||||
params: {
|
||||
deep: true,
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
this.initData();
|
||||
}
|
||||
},
|
||||
where: {
|
||||
deep: true,
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
this.initData();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initData(){
|
||||
if (!this.params) return;
|
||||
let tableName = this.params.split(",")[0];
|
||||
let value = this.params.split(",")[1];
|
||||
let label = this.params.split(",")[2];
|
||||
getSearchSelectData({
|
||||
tableName: tableName,
|
||||
value: value,
|
||||
label: label,
|
||||
where: this.where,
|
||||
groupby: this.groupby,
|
||||
orderby: this.orderby
|
||||
}).then(res=>{
|
||||
console.log(res)
|
||||
if(res.code == 200) {
|
||||
this.options = res.data;
|
||||
//
|
||||
console.log(this.value)
|
||||
if(this.value) {
|
||||
this.selectedData = this.value;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
// 选中的select选项改变的回调 {label: "名称", value: "值"}
|
||||
changeSelected(selectedData) {
|
||||
this.$emit('input', selectedData)
|
||||
if (this.multiple) {
|
||||
this.$emit('change', this.options.filter(a=> selectedData.includes(a.value)));
|
||||
} else {
|
||||
let result = this.options.find(a=>a.value == selectedData);
|
||||
result['scope'] = this.scope;
|
||||
this.$emit('change', result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
Loading…
Reference in New Issue