SpringBoot 使用 AOP校验入参字段(DictVerify)和翻译响应字段(Dict)

在这里插入图片描述

组织架构

在这里插入图片描述
参考项目:Jeecg-Boot

校验入参数据中的字典字段和关联表字段

创建 DictVerify 注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 字典校验注解
 * (注解加在方法或类属性上)
 *
 * @author Windows
 */
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DictVerify {
    /**
     * 字典数据的code或关联表的关联字段名(加注解必须要的,设置默认值是方便加在方法上)
     */
    String dicCode() default "";

    /**
     * 关联表需要展示字段(比如)
     */
    String dicText() default "";

    /**
     * 代码注释(用于msg提示)
     */
    String dicMsg() default "";

    /**
     * 关联表名(数据库表名)
     */
    String dicTable() default "";
    
}

创建AOP切面

import cn.hutool.core.util.StrUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.mohrss.leaf.baseinformation.api.dict.dto.QueryBa10FeignDTO;
import org.mohrss.leaf.baseinformation.api.dict.entity.Ba10Feign;
import org.mohrss.leaf.common.aspect.annotation.DictVerify;
import org.mohrss.leaf.common.constant.CommonConstants;
import org.mohrss.leaf.common.utils.RedisUtils;
import org.mohrss.leaf.common.utils.oConvertUtils;
import org.mohrss.leaf.common.vo.DictModel;
import org.mohrss.leaf.entity.dto.JsonResponse;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 字典校验AOP类
 *
 * @author Windows
 */
@Aspect
@Component
@Slf4j
@Order(10)
public class DictVerifyAspect {

    @Resource
    private CommonMapper commonMapper;

    @Resource
    private RedisUtils redisUtils;


    /**
     * 定义切点Pointcut   加了DictVerify注解的方法才需要校验
     * 这里根据自己控制器的包名来修改
     */
    @Pointcut("execution(public *控制器的包路径.*Controller.*(..)) && @annotation(DictVerify注解的包路径)")
    public void verifyService() {
    }

    @SneakyThrows
    @Around("verifyService()")
    public Object doBefore(ProceedingJoinPoint pjp) {
        // 获取接口返回的数据对象
//        Object result = pjp.proceed();
//        System.out.println("result:" + JSONObject.toJSONString(result));
        // 获取接口方法的入参
        Object[] args = pjp.getArgs();
//        System.out.println("pipArray:" + JSONObject.toJSONString(args));
        Object obj = args[0];
        for (Field field : oConvertUtils.getAllFields(obj)) {
            if (field.getAnnotation(DictVerify.class) != null) {
            	// 获取注解中的值
                String table = field.getAnnotation(DictVerify.class).dicTable();
                String code = field.getAnnotation(DictVerify.class).dicCode();
                String text = field.getAnnotation(DictVerify.class).dicText();
                String msg = field.getAnnotation(DictVerify.class).dicMsg();
                // 设置允许通过反射访问私有变量
                field.setAccessible(true);
                // 获取属性值
                String value = field.get(obj).toString();
                if (StrUtil.isNotEmpty(value)) {
                    // 校验关联表
                    if (StrUtil.isNotEmpty(table)) {
                    	// 先查缓存
                        String dictCode = String.format("%s,%s,%s", table, text, code);
                        String redisKey = String.format("sys:cache:dicTable:SimpleKey [%s,%s]", dictCode, value);
                        if (!redisUtils.exists(redisKey)) {
                            // 公共的查询不同表不同字段的方法
                            DictModel dictModel = commonMapper.queryTableDictCountByKey(table, text, code, value);
                            if (dictModel == null) {
                                return JsonResponse.Fail(1, "请输入正确的" + msg);
                            }
                            // 存入redis缓存,保留5分钟
                            redisUtils.set(redisKey, dictModel.getText(), 300, TimeUnit.SECONDS);
                        }
                    } else { // 校验字典表
                    	// 先查缓存
                        String keyString = String.format("sys:cache:dict:%s:%s", code, value);
                        if (!redisUtils.exists(keyString)) {
                            // 通过查数据库或缓存校验当前属性的值在指定字典类型中存在
                            QueryBa10FeignDTO queryBa10FeignDTO = new QueryBa10FeignDTO();
                            queryBa10FeignDTO.setAaa100(code);
                            queryBa10FeignDTO.setAaa102(value);
                           // 查询字典表,这里需要根据自己系统的字典表来修改  start 
                           Dict dict= commomMapper.queryDictTextByKey(code,value);
                           // 查询字典表,这里需要根据自己系统的字典表来修改  end 
                            if (dict== null) {
                                return JsonResponse.Fail(1, "请输入正确的" + msg);
                            }
                            String redisKey = String.format("sys:cache:dict:%s:%s", code, value);
                            redisUtils.set(redisKey, ba10 .getAaa103());
                        }
                    }
                }
            }
        }
        return pjp.proceed();
    }
}

在方法上添加注解

在需要校验字段或者关联表数据是否存在的方法上添加注解,一般是新增和修改需要。


    @ApiOperation(value = "新增数据")
    @PostMapping("insertInfo")
    @DictVerify 
    public JsonResponse insertInfo(@RequestBody @Valid InsertDTO insertDTO) {
        return this.testService.insertInfo(insertBb80DTO);
    }

在需要校验的字段上添加注解

校验关联表是否存在数据

dicTable=表名, dicCode = “关联字段名”, dicText = “需要展示的字段名”,dicMsg = “用于前端的提示消息”

    @ApiModelProperty(value = "学校编号", required = true)
    @DictVerify(dicTable = "scholl", dicCode = "school_no", dicText = "school_name", dicMsg = "学校编号")
    private String schoolNo;

校验字典表是否存在数据

这里的dicCode就是字典表中字典类型

    @ApiModelProperty(value = "是否毕业", required = true)
    @DictVerify(dicCode = "graduation", dicMsg = "是否毕业")
    private String graduation;

翻译响应字段

创建 Dict 注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 字典翻译注解
 *
 * @author Windows
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Dict {
    /**
     * 数据code
     */
    String dicCode();

    /**
     * 数据Text
     */
    String dicText() default "";

    /**
     * 数据字典表
     */
    String dictTable() default "";
}

创建 Dict AOP切面

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.mohrss.leaf.baseinformation.api.dict.entity.Ba10Feign;
import org.mohrss.leaf.baseinformation.api.dict.vo.DictModel;
import org.mohrss.leaf.baseinformation.api.dict.vo.DictModelMany;
import org.mohrss.leaf.common.aspect.annotation.Dict;
import org.mohrss.leaf.common.constant.CommonConstants;
import org.mohrss.leaf.common.utils.ConvertUtils;
import org.mohrss.leaf.common.utils.RedisUtils;
import org.mohrss.leaf.common.utils.SqlInjectionUtil;
import org.mohrss.leaf.entity.dto.JsonResponse;
import org.mohrss.leaf.supervision.enforcement.dao.CommonMapper;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 字典数据翻译AOP类
 *
 * @author Windows
 */
@Aspect
@Component
@Slf4j
@Order(1)
public class DictAspect {

    @Resource
    private RedisUtils redisUtils;

    @Resource
    private ObjectMapper objectMapper;

    @Resource
    private CommonMapper commonMapper;


    /**
     * 定义切点Pointcut 
     * 根据自己控制器包路径配置
     */
    @Pointcut("execution(public * 自己的包路径.*Controller.*(..))")
    public void excludeService() {
    }

    @Around("excludeService()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        long time1 = System.currentTimeMillis();
        Object result = pjp.proceed();
        long time2 = System.currentTimeMillis();
        log.debug("获取JSON数据 耗时:" + (time2 - time1) + "ms");
        long start = System.currentTimeMillis();
        this.parseDictText(result);
        long end = System.currentTimeMillis();
        log.debug("注入字典到JSON数据  耗时" + (end - start) + "ms");
        return result;
    }

    /**
     * 本方法针对返回对象为Result 的IPage的分页列表数据进行动态字典注入
     * 字典注入实现 通过对实体类添加注解@dict 来标识需要的字典内容,字典分为单字典code即可 ,table字典 code table text配合使用与原来jeecg的用法相同
     * 示例为SysUser   字段为sex 添加了注解@Dict(dicCode = "sex") 会在字典服务立马查出来对应的text 然后在请求list的时候将这个字典text,已字段名称加_dictText形式返回到前端
     *
     * @param result 响应数据
     */
    private Object parseDictText(Object result) {
        if (result instanceof JsonResponse) {
            // 响应数据是分页列表数据
            if (((JsonResponse) result).getData() instanceof IPage) {
                // 判断是否含有字典注解,没有注解返回 start-----
                Boolean hasDict = checkHasDict(((IPage) ((JsonResponse) result).getData()).getRecords());
                if (!hasDict) {
                    return result;
                }
                //判断是否含有字典注解,没有注解返回  end -----
                this.resultIsList(result);
            } else if (((JsonResponse) result).getData() != null) {  // 响应数据是单个对象数据时
                this.resultIsDetail(result);
            }
        }
        return result;
    }

    /**
     * 响应数据是分页列表数据
     */
    private void resultIsList(Object result) {
        // 列表数据集合 ,把数据转成json
        List<JSONObject> items = new ArrayList<>();
        //step.1 筛选出加了 Dict 注解的字段列表
        List<Field> dictFieldList = new ArrayList<>();
        // 字典数据列表, key = 字典code,value=数据列表
        Map<String, List<String>> dataListMap = new HashMap<>();
        //取出结果集
        List<Object> records = ((IPage) ((JsonResponse) result).getData()).getRecords();

        log.debug(" __ 进入字典翻译切面 DictAspect —— ");
        // 遍历数据列表,
        for (Object record : records) {
            String json = "{}";
            try {
                //解决@JsonFormat注解解析不了的问题详见SysAnnouncement类的@JsonFormat
                json = objectMapper.writeValueAsString(record);
            } catch (JsonProcessingException e) {
                log.error("json解析失败" + e.getMessage(), e);
            }
            //防止restcontroller返回json数据后key顺序错乱 -----
            JSONObject item = JSONObject.parseObject(json, Feature.OrderedField);

            //update-begin--Author:scott -- Date:20190603 ----for:解决继承实体字段无法翻译问题------
            // 遍历所有字段,把字典Code取出来,放到 map 里
            for (Field field : ConvertUtils.getAllFields(record)) {
                String value = item.getString(field.getName());
                if (ConvertUtils.isEmpty(value)) {
                    continue;
                }
                //update-end--Author:scott  -- Date:20190603 ----for:解决继承实体字段无法翻译问题------

                if (field.getAnnotation(Dict.class) != null) {
                    if (!dictFieldList.contains(field)) {
                        dictFieldList.add(field);
                    }
                    String code = field.getAnnotation(Dict.class).dicCode();
                    String text = field.getAnnotation(Dict.class).dicText();
                    String table = field.getAnnotation(Dict.class).dictTable();

                    List<String> dataList;
                    String dictCode = code;
                    if (StrUtil.isNotEmpty(table)) {
                        dictCode = String.format("%s,%s,%s", table, text, code);
                    }
//                          判断一个map中是否存在这个key,如果存在则处理value的数据,如果不存在,则创建一个满足value要求的数据结构放到value中
                    dataList = dataListMap.computeIfAbsent(dictCode, k -> new ArrayList<>());
//                          list 去重添加
                    this.listAddAllDeduplicate(dataList, Arrays.asList(value.split(",")));
                }
            }
            items.add(item);
        }

        //step.2 调用翻译方法,一次性翻译
        Map<String, List<DictModel>> translText = this.translateAllDict(dataListMap);

        //step.3 将翻译结果填充到返回结果里
        for (JSONObject record : items) {
            for (Field field : dictFieldList) {
                String code = field.getAnnotation(Dict.class).dicCode();
                String text = field.getAnnotation(Dict.class).dicText();
                String table = field.getAnnotation(Dict.class).dictTable();
                String fieldDictCode = code;
                // 如果指定了表名则需要拼接字符串
                if (StrUtil.isNotEmpty(table)) {
                    fieldDictCode = String.format("%s,%s,%s", table, text, code);
                }

                String value = record.getString(field.getName());
                if (ConvertUtils.isNotEmpty(value)) {
                    List<DictModel> dictModels = translText.get(fieldDictCode);
                    if (dictModels == null || dictModels.size() == 0) {
                        continue;
                    }

                    String textValue = this.translDictText(dictModels, value);
                    record.put(field.getName() + CommonConstants.DICT_TEXT_SUFFIX, textValue);
                }
            }
        }
        ((IPage) ((JsonResponse) result).getData()).setRecords(items);
    }

    /**
     * 响应数据是单个对象数据时
     */
    private void resultIsDetail(Object result) {
        // 获取响应里面的data数据
        Object data = ((JsonResponse) result).getData();

        String json = "{}";
        try {
            //解决@JsonFormat注解解析不了的问题详见SysAnnouncement类的@JsonFormat
            json = objectMapper.writeValueAsString(data);
        } catch (JsonProcessingException e) {
            log.error("json解析失败" + e.getMessage(), e);
        }
//              防止restcontroller返回json数据后key顺序错乱
        JSONObject item = JSONObject.parseObject(json, Feature.OrderedField);
        //step.1 筛选出加了 Dict 注解的字段列表
        List<Field> dictFieldList = new ArrayList<>();
        // 字典数据列表, key = 字典code,value=数据列表
        Map<String, List<String>> dataListMap = new HashMap<>();
        // 遍历所有字段,把字典Code取出来,放到 map 里
        for (Field field : ConvertUtils.getAllFields(data)) {
            // 根据对象的字段名获取字段的值
            String value = item.getString(field.getName());
            if (ConvertUtils.isEmpty(value)) {
                continue;
            }
            // 查看字段是否添加了 Dict 注解
            if (field.getAnnotation(Dict.class) != null) {
                // 不存在则添加到列表
                if (!dictFieldList.contains(field)) {
                    dictFieldList.add(field);
                }
                String code = field.getAnnotation(Dict.class).dicCode();    // 数据库字段名
                String text = field.getAnnotation(Dict.class).dicText();    // 数据库需要展示的字段
                String table = field.getAnnotation(Dict.class).dictTable(); // 数据库表名

                List<String> dataList;
                String dictCode = code;
                if (StrUtil.isNotEmpty(table)) {
                    dictCode = String.format("%s,%s,%s", table, text, code);
                }
                dataList = dataListMap.computeIfAbsent(dictCode, k -> new ArrayList<>());
                this.listAddAllDeduplicate(dataList, Arrays.asList(value.split(",")));
            }
        }

        //step.2 调用翻译方法,一次性翻译(把添加过注解属性翻译出来)
        Map<String, List<DictModel>> translText = this.translateAllDict(dataListMap);

        //step.3 将翻译结果填充到返回结果里
        for (Field field : dictFieldList) {
            String code = field.getAnnotation(Dict.class).dicCode();
            String text = field.getAnnotation(Dict.class).dicText();
            String table = field.getAnnotation(Dict.class).dictTable();
            String fieldDictCode = code;
            // 如果指定了表名则需要拼接字符串
            if (StrUtil.isNotEmpty(table)) {
                fieldDictCode = String.format("%s,%s,%s", table, text, code);
            }
            String value = item.getString(field.getName());
            // 判断是否为空、""、"null"
            if (ConvertUtils.isNotEmpty(value)) {
                List<DictModel> dictModels = translText.get(fieldDictCode);
                if (dictModels == null || dictModels.size() == 0) {
                    continue;
                }
                String textValue = this.translDictText(dictModels, value);
                // 把翻译过的数据添加到对象里
                item.put(field.getName() + CommonConstants.DICT_TEXT_SUFFIX, textValue);
            }
        }
        ((JsonResponse) result).setData(item);
    }

    /**
     * list 去重添加
     */
    private void listAddAllDeduplicate(List<String> dataList, List<String> addList) {
        // 筛选出dataList中没有的数据
        List<String> filterList = addList.stream().filter(i -> !dataList.contains(i)).collect(Collectors.toList());
        dataList.addAll(filterList);
    }

    /**
     * 一次性把所有的字典都翻译了
     * 1.  所有的普通数据字典的所有数据只执行一次SQL
     * 2.  表字典相同的所有数据只执行一次SQL
     *
     * @param dataListMap 字典数据列表, key = 字典code,value=数据列表
     * @return 翻译后的字典文本,key=dicCode
     */
    private Map<String, List<DictModel>> translateAllDict(Map<String, List<String>> dataListMap) {
        // 翻译后的字典文本,key=dicCode
        Map<String, List<DictModel>> translText = new HashMap<>();
        // 需要翻译的数据(有些可以从redis缓存中获取,就不走数据库查询)
        List<String> needTranslData = new ArrayList<>();
        //step.1 先通过redis中获取缓存字典数据
        for (String dictCode : dataListMap.keySet()) {
            List<String> dataList = dataListMap.get(dictCode);
            if (dataList.size() == 0) {
                continue;
            }
            // 表字典需要翻译的数据
            List<String> needTranslDataTable = new ArrayList<>();
            for (String s : dataList) {
                String data = s.trim();
                if (data.length() == 0) {
                    continue; //跳过循环
                }
                if (dictCode.contains(",")) {
                    String keyString = String.format(CommonConstants.DICT_TABLE_CACHE_PREFIX, dictCode, data);
                    if (redisUtils.exists(keyString)) {
                        try {
                            String text = ConvertUtils.getString(redisUtils.get(keyString));
                            List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
                            list.add(new DictModel(data, text));
                        } catch (Exception e) {
                            log.warn(e.getMessage());
                        }
                    } else if (!needTranslDataTable.contains(data)) {
                        // 去重添加
                        needTranslDataTable.add(data);
                    }
                } else {
                    String keyString = String.format(CommonConstants.DICT_TEXT_CACHE_PREFIX, dictCode, data);
                    if (redisUtils.exists(keyString)) {
                        try {
                            String text = ConvertUtils.getString(redisUtils.get(keyString));
                            List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
                            list.add(new DictModel(data, text));
                        } catch (Exception e) {
                            log.warn(e.getMessage());
                        }
                    } else if (!needTranslData.contains(data)) {
                        // 去重添加
                        needTranslData.add(data);
                    }
                }

            }
            //step.2 调用数据库翻译表字典
            if (needTranslDataTable.size() > 0) {
                String[] arr = dictCode.split(",");
                String table = arr[0], text = arr[1], code = arr[2];
                String values = String.join(",", needTranslDataTable);
                // 数据库查看字段的描述
//               @dict注解支持 dicTable 设置where条件
                String filterSql = null;
                if (table.toLowerCase().indexOf("where") > 0) {
                    String[] arr1 = table.split(" (?i)where ");
                    table = arr1[0];
                    filterSql = arr1[1];
                }
                String[] tableAndFields = new String[]{table, text, code};
                SqlInjectionUtil.filterContent(tableAndFields);
                SqlInjectionUtil.specialFilterContentForDictSql(filterSql);
                // 这里是公共的方法DAO 根据自己的业务定义
                List<DictModel> texts = commonMapper.queryTableDictByKeysAndFilterSql(table, text, code, filterSql, Arrays.asList(values.split(",")));
//                log.info("translateDictFromTableByKeys.result:" + texts);
                List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
                list.addAll(texts);
                // 做 redis 缓存
                for (DictModel dict : texts) {
                    String redisKey = String.format(CommonConstants.DICT_TABLE_CACHE_PREFIX, dictCode, dict.getValue());
                    try {
                        // 保留5分钟
                        redisUtils.set(redisKey, dict.getText(), 300, TimeUnit.SECONDS);061
                    } catch (Exception e) {
                        log.warn(e.getMessage(), e);
                    }
                }
            }
        }

        //step.3 调用数据库进行翻译普通字典
        if (needTranslData.size() > 0) {
            List<String> dictCodeList = Arrays.asList(dataListMap.keySet().toArray(new String[]{}));
            // 将不包含逗号的字典code筛选出来,因为带逗号的是表字典,而不是普通的数据字典
            List<String> filterDictCodes = dictCodeList.stream().filter(key -> !key.contains(",")).collect(Collectors.toList());
            String dictCodes = String.join(",", filterDictCodes);
            String values = String.join(",", needTranslData);
            // 公共DAO 查询字典表 start
            List<DictModelMany> dictModelManies = commonMapper.queryManyDictByKeys(Arrays.asList(dictCodes.split(",")), Arrays.asList(values.split(",")));
            // 公共DAO 查询字典表 end
            Map<String, List<DictModel>> dictMap = new HashMap(5);
            for (DictModelMany dict : dictModelManies) {
                List<DictModel> dictItemList = dictMap.computeIfAbsent(dict.getDictCode(), i -> new ArrayList<>());
                dictItemList.add(new DictModel(dict.getValue(), dict.getText()));
            }
            //系统字典数据应该包括自定义的java类-枚举 start
//            Map<String, List<DictModel>> enumRes = ResourceUtil.queryManyDictByKeys(dictCodeList, keys);
//            dictMap.putAll(enumRes);
            //系统字典数据应该包括自定义的java类-枚举 end
            Map<String, List<DictModel>> manyDict = dictMap;

//            log.info("translateManyDict.result:" + manyDict);
            for (String dictCode : manyDict.keySet()) {
                List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
                List<DictModel> newList = manyDict.get(dictCode);
                list.addAll(newList);
                // 做 redis 缓存
                for (DictModel dict : newList) {
                    String redisKey = String.format(CommonConstants.DICT_TEXT_CACHE_PREFIX, dictCode, dict.getValue());
                    try {
                        redisUtils.set(redisKey, dict.getText());
                    } catch (Exception e) {
                        log.warn(e.getMessage(), e);
                    }
                }
            }
        }
        return translText;
    }

    /**
     * 字典值替换文本
     *
     * @param dictModels 字典集合
     * @param values     文本内容
     * @return 替换后的文本
     */
    private String translDictText(List<DictModel> dictModels, String values) {
        List<String> result = new ArrayList<>();

        // 允许多个逗号分隔,允许传数组对象
        String[] splitVal = values.split(",");
        for (String val : splitVal) {
            String dictText = val;
            for (DictModel dict : dictModels) {
                if (val.equals(dict.getValue())) {
                    dictText = dict.getText();
                    break;
                }
            }
            result.add(dictText);
        }
        return String.join(",", result);
    }

    /**
     * 翻译字典文本
     *
     * @param code
     * @param text
     * @param table
     * @param key
     * @return
     */
    @Deprecated
    private String translateDictValue(String code, String text, String table, String key) {
        if (ConvertUtils.isEmpty(key)) {
            return null;
        }
        StringBuffer textValue = new StringBuffer();
        String[] keys = key.split(",");
        for (String k : keys) {
            String tmpValue = null;
            log.debug(" 字典 key : " + k);
            if (k.trim().length() == 0) {
                continue; //跳过循环
            }
            //update-begin--Author:scott -- Date:20210531 ----for: !56 优化微服务应用下存在表字段需要字典翻译时加载缓慢问题-----
            if (StrUtil.isNotEmpty(table)) {
                log.info("--DictAspect------dicTable=" + table + " ,dicText= " + text + " ,dicCode=" + code);
                String keyString = String.format("sys:cache:dicTable::SimpleKey [%s,%s,%s,%s]", table, text, code, k.trim());
                if (redisUtils.exists(keyString)) {
                    try {
                        tmpValue = ConvertUtils.getString(redisUtils.get(keyString));
                    } catch (Exception e) {
                        log.warn(e.getMessage());
                    }
                } else {
                    tmpValue = commonMapper.queryTableDictTextByKey(table, text, code, k.trim());
                }
            } else {
                String keyString = String.format(CommonConstants.DICT_TEXT_CACHE_PREFIX, code, k.trim());
                if (redisUtils.exists(keyString)) {
                    try {
                        tmpValue = ConvertUtils.getString(redisUtils.get(keyString));
                    } catch (Exception e) {
                        log.warn(e.getMessage());
                    }
                } else {
                // 通过字典的code和入参的值查字典表是否存在记录
                    tmpValue = commonMapper.queryDictTextByKey(code, k.trim());
                }
            }
            //update-end--Author:scott -- Date:20210531 ----for: !56 优化微服务应用下存在表字段需要字典翻译时加载缓慢问题-----

            if (tmpValue != null) {
                if (!"".equals(textValue.toString())) {
                    textValue.append(",");
                }
                textValue.append(tmpValue);
            }

        }
        return textValue.toString();
    }


    /**
     * 检测返回结果集中是否包含Dict注解
     *
     * @param records 结果集
     * @return 是否包含注解
     */
    private Boolean checkHasDict(List<Object> records) {
        if (ConvertUtils.isNotEmpty(records) && records.size() > 0) {
            for (Field field : ConvertUtils.getAllFields(records.get(0))) {
                if (ConvertUtils.isNotEmpty(field.getAnnotation(Dict.class))) {
                    return true;
                }
            }
        }
        return false;
    }
}

在需要翻译的字段上添加注解

需要关联表翻译的

dicTable=表名, dicCode = “关联字段名”, dicText = “需要展示的字段名”

    @ApiModelProperty(value = "学校编号")
    @Dict(dicTable = "scholl", dicCode = "school_no", dicText = "school_name")
    private String schoolNo;

关联字典表翻译的

这里的dicCode就是字典表中字典类型

    @ApiModelProperty(value = "是否毕业")
    @Dict(dicCode = "graduation")
    private String graduation;

查询结果

加了Dict 注解的就会帮我翻译出来,并不会覆盖原来的值

{
	"schoolNo":"1001",
	"schoolNo_dictText":"学校名称",
	"graduation":"1",
	"graduation_dictText":"是",		
}

CommonMapper、CommonMapper.xml、DictModel、DictModelMany、CommonConstants

CommonMapper

import org.apache.ibatis.annotations.Param;
import org.mohrss.leaf.baseinformation.api.dict.vo.DictModel;
import org.mohrss.leaf.baseinformation.api.dict.vo.DictModelMany;

import java.util.List;

/**
 * 代码表(Ba10)表数据库访问层
 *
 * @author ddz
 * @since 2022-12-19 10:05:17
 */
public interface CommonMapper {

    /**
     * 查询字典表的数据
     *
     * @param table      表名
     * @param text       显示字段名
     * @param code       存储字段名
     * @param filterSql  条件sql
     * @param codeValues 存储字段值 作为查询条件in
     * @return 集合
     */
    List<DictModel> queryTableDictByKeysAndFilterSql(@Param("table") String table, @Param("text") String text, @Param("code") String code, @Param("filterSql") String filterSql, @Param("codeValues") List<String> codeValues);


    /**
     * 通过查询指定table的 text code key 获取字典值
     *
     * @param table 表名
     * @param text  展示字段名
     * @param code  字段名
     * @param key   字段值
     * @return 展示字段值
     */
    String queryTableDictTextByKey(@Param("table") String table, @Param("text") String text, @Param("code") String code, @Param("key") String key);

    /**
     * 通过指定table的 code key 查询是否存在记录
     *
     * @param table 表名
     * @param text  展示字段名
     * @param code  字段名
     * @param key   字段值
     * @return 行数
     */
    DictModel queryTableDictCountByKey(@Param("table") String table, @Param("text") String text, @Param("code") String code, @Param("key") String key);


    /**
     * 可通过多个字典code查询翻译文本
     *
     * @param dictCodeList 多个字典code
     * @param keys         数据列表
     * @return 响应集合
     */
    List<DictModelMany> queryManyDictByKeys(@Param("dictCodeList") List<String> dictCodeList, @Param("keys") List<String> keys);

}

CommonMapper.xml

根据自己需求来编写SQL

<select id="queryTableDictByKeysAndFilterSql" parameterType="String"
            resultType="org.mohrss.leaf.baseinformation.api.dict.vo.DictModel">
        select ${text} as "text", ${code} as "value" from ${table} where ${code} IN (
        <foreach item="key" collection="codeValues" separator=",">
            #{key}
        </foreach>
        )
        <if test="filterSql != null and filterSql != ''">
            and ${filterSql}
        </if>
    </select>

    <!--通过查询指定table的 text code key 获取字典值-->
    <select id="queryTableDictTextByKey" parameterType="String" resultType="String">
		   select ${text} as "text" from ${table} where ${code}= #{key}
	</select>

    <!--通过指定table的 code key 查询是否存在记录-->
    <select id="queryTableDictCountByKey" parameterType="String"
            resultType="org.mohrss.leaf.baseinformation.api.dict.vo.DictModel">
		   select ${text} as "text", ${code} as "value" from ${table} where ${code}= #{key}
	</select>

    <!-- 通过字典code获取字典数据,可批量查询 -->
    <select id="queryManyDictByKeys" parameterType="String"
            resultType="org.mohrss.leaf.baseinformation.api.dict.vo.DictModelMany">
        SELECT
        AAA100 AS dict_code,
        AAA103 AS "text",
        AAA102 AS "value"
        FROM
        BA10
        WHERE AAA100 IN (
        <foreach item="dictCode" collection="dictCodeList" separator=",">
            #{dictCode}
        </foreach>
        )
        AND AAA102 IN (
        <foreach item="key" collection="keys" separator=",">
            #{key}
        </foreach>
        )
    </select>

DictModel

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

import java.io.Serializable;

/**
 * 查询字典类
 * @author Windows
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@JsonIgnoreProperties(ignoreUnknown = true)
public class DictModel implements Serializable{
	private static final long serialVersionUID = 1L;

	public DictModel() {
	}
	
	public DictModel(String value, String text) {
		this.value = value;
		this.text = text;
	}
	
	/**
	 * 字典value
	 */
	private String value;
	/**
	 * 字典文本
	 */
	private String text;

	/**
	 * 特殊用途: JgEditableTable
	 */
	public String getTitle() {
		return this.text;
	}
	/**
	 * 特殊用途: vue3 Select组件
	 */
	public String getLabel() {
		return this.text;
	}

}

DictModelMany

package org.mohrss.leaf.common.constant;

import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * 查询多个字典时用到
 * @author Windows
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class DictModelMany extends DictModel {

    /**
     * 字典code,根据多个字段code查询时才用到,用于区分不同的字典选项
     */
    private String dictCode;

}

公共工具类

RedisUtils

前提是项目中配置好 redis 连接

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Redis工具类
 *
 * @author Windows
 */
@Configuration
public class RedisUtils {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Bean(name = "redisTemplate")
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, String> template = new RedisTemplate<String, String>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashKeySerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }

    /**
     * 如果使用注解注入RedisTemplate对象,则不需要该setter方法
     */
    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }


    /**
     * 获取Hash键值对的数量
     *
     * @param key 键
     * @return 数量
     */
    public long getHashSize(String key) {
        return redisTemplate.opsForHash().size(key);
    }

    /**
     * 正则表达式匹配key
     *
     * @param pattern 正则表达式
     * @return 结果
     */
    public Object keys(String pattern) {
        if (StrUtil.isNotEmpty(pattern)) {
            return redisTemplate.keys(pattern);
        }
        return null;
    }

    /**
     * String类型缓存获取
     *
     * @param key 键
     * @return 值
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * String类型缓存保存
     *
     * @param key   键
     * @param value 值
     * @return true:成功;false:失败
     */
    public boolean set(String key, Object value) {
        try {
            if (StrUtil.isNotEmpty(key) && null != value) {
                redisTemplate.opsForValue().set(key, value);
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 指定缓存失效时间
     *
     * @param key  键
     * @param time 时间(秒)
     */
    public void expire(String key, long time) {
        try {
            if (StrUtil.isNotEmpty(key) && time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据key获取过期时间
     *
     * @param key 键 不能为null
     * @return 时间(秒)返回0代表为永久有效;-1代表key不存在
     */
    public long getExpire(String key) {
        if (StrUtil.isNotEmpty(key)) {
            return redisTemplate.getExpire(key, TimeUnit.SECONDS);
        }
        return -1L;
    }

    /**
     * 判断key是否存在
     *
     * @param key 键
     * @return true:存在;false:不存在
     */
    public boolean exists(String key) {
        try {
            if (StrUtil.isNotEmpty(key)) {
                return redisTemplate.hasKey(key);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 删除缓存(批量)
     *
     * @param keys(可变参数)可以传一个值或多个
     */
    @SuppressWarnings("unchecked")
    public void delete(String... keys) {
        if (keys != null && keys.length > 0) {
            if (keys.length == 1) {
                redisTemplate.delete(keys[0]);
            } else {
                // 批量删除
                redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(keys));
            }
        }
    }

    /**
     * String类型缓存保存并设置时间
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒):time要大于0 如果time小于等于0 将设置无限期
     * @return true:成功;false:失败
     */
    public boolean set(String key, Object value, long time) {
        try {
            if (StrUtil.isNotEmpty(key) && null != value) {
                if (time > 0) {
                    redisTemplate.opsForValue().set(key, value, time,
                            TimeUnit.SECONDS);
                } else {
                    set(key, value);
                }
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * String类型缓存保存并设置时间
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒):time要大于0 如果time小于等于0 将设置无限期
     * @param unit  时间格式
     * @return true:成功;false:失败
     */
    public boolean set(String key, Object value, long time, TimeUnit unit) {
        try {
            if (StrUtil.isNotEmpty(key) && null != value) {
                if (time > 0) {
                    redisTemplate.opsForValue().set(key, value, time, unit);
                } else {
                    set(key, value);
                }
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 递增
     *
     * @param key   键
     * @param delta 要增加几(大于0)
     * @return 使用事务事为0
     */
    public long increment(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        if (StrUtil.isNotEmpty(key)) {
            return redisTemplate.opsForValue().increment(key, delta);
        } else {
            return -1L;
        }
    }

    /**
     * 递减
     *
     * @param key   键
     * @param delta 要减少几(小于0)
     * @return 使用事务事为0
     */
    public long decrement(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        if (StrUtil.isNotEmpty(key)) {
            return redisTemplate.opsForValue().increment(key, -delta);
        } else {
            return -1L;
        }
    }

    /**
     * 获取hash结构的数据 (Hash类型)
     *
     * @param key  键: 不能为null
     * @param item 项: 不能为null
     * @return 值
     */
    public Object getHash(String key, String item) {
        if (StrUtil.isNotEmpty(key) && StrUtil.isNotEmpty(item)) {
            return redisTemplate.opsForHash().get(key, item);
        } else {
            return null;
        }
    }

    /**
     * 获取Key对应的所有键值HashMap
     *
     * @param key 键
     * @return 对应的多个键值
     */
    public Map<Object, Object> getHashMap(String key) {
        if (StrUtil.isNotEmpty(key)) {
            return redisTemplate.opsForHash().entries(key);
        } else {
            return null;
        }
    }

    /**
     * 保存hashMap结构的数据(HashMap类型)
     * Map<Object,List> 结构
     *
     * @param key 键
     * @param map 对应多个键值
     * @return true:成功;false:失败
     */
    public boolean setHashMapList(String key, Map<Object, List<JSONObject>> map) {
        try {
            if (StrUtil.isNotEmpty(key) && null != map && map.size() > 0) {
                redisTemplate.opsForHash().putAll(key, map);
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 保存hashMap结构的数据(HashMap类型)
     * Map<Object,List> 结构
     *
     * @param key  键
     * @param map  对应多个键值
     * @param time 时间(秒)
     * @return true:成功; false:失败
     */
    public boolean setHashMapList(String key, Map<Object, List<JSONObject>> map, long time) {
        try {
            if (StrUtil.isNotEmpty(key) && null != map && map.size() > 0) {
                redisTemplate.opsForHash().putAll(key, map);
                if (time > 0) {
                    expire(key, time);
                }
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 保存hashMap结构的数据(HashMap类型)
     *
     * @param key 键
     * @param map 对应多个键值
     * @return true:成功;false:失败
     */
    public boolean setHashMap(String key, Map<Object, Object> map) {
        try {
            if (StrUtil.isNotEmpty(key) && null != map && map.size() > 0) {
                redisTemplate.opsForHash().putAll(key, map);
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 保存hashMap结构的数据,并设置时间 (HashMap类型)
     *
     * @param key  键
     * @param map  对应多个键值
     * @param time 时间(秒)
     * @return true:成功; false:失败
     */
    public boolean setHashMap(String key, Map<Object, Object> map, long time) {
        try {
            if (StrUtil.isNotEmpty(key) && null != map && map.size() > 0) {
                redisTemplate.opsForHash().putAll(key, map);
                if (time > 0) {
                    expire(key, time);
                }
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }


    /**
     * 向一个hash类型的数据中保存数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @return true:成功;false:失败
     */
    public boolean setHash(String key, String item, Object value) {
        try {
            if (StrUtil.isNotEmpty(key) && StrUtil.isNotEmpty(item)
                    && null != value) {
                redisTemplate.opsForHash().put(key, item, value);
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 向一个hash类型的数据中保存数据,如果不存在将创建,指定保存时间
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @param time  时间(秒)  注意:如果已存在的hash数据时间,这里将会替换原有的时间
     * @return true:成功;false:失败
     */
    public boolean setHash(String key, String item, Object value, long time) {
        try {
            if (StrUtil.isNotEmpty(key) && StrUtil.isNotEmpty(item)
                    && null != value) {
                redisTemplate.opsForHash().put(key, item, value);
                if (time > 0) {
                    expire(key, time);
                }
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 删除hash数据中的值
     *
     * @param key  键 不能为null
     * @param item 项 可以使多个 不能为null
     */
    public void deleteHash(String key, Object... item) {
        if (StrUtil.isNotEmpty(key) && null != item) {
            redisTemplate.opsForHash().delete(key, item);
        }
    }

    /**
     * 判断hash数据中是否有该项的值
     *
     * @param key  键 不能为null
     * @param item 项 不能为null
     * @return true:存在; false:不存在
     */
    public boolean existHashKey(String key, String item) {
        if (StrUtil.isNotEmpty(key) && StrUtil.isNotEmpty(item)) {
            return redisTemplate.opsForHash().hasKey(key, item);
        } else {
            return false;
        }
    }

    /**
     * hash递增,如果不存在,就会创建一个,并把新增后的值返回
     *
     * @param key  键
     * @param item 项
     * @param by   要增加几(大于0)
     * @return -1,失败
     */
    public double incrementHash(String key, String item, double by) {
        if (StrUtil.isNotEmpty(key) && StrUtil.isNotEmpty(item)) {
            return redisTemplate.opsForHash().increment(key, item, by);
        } else {
            return -1;
        }
    }

    /**
     * hash递减
     *
     * @param key  键
     * @param item 项
     * @param by   要减少记(小于0)
     * @return -1,失败
     */
    public double decrementHash(String key, String item, double by) {
        if (StrUtil.isNotEmpty(key) && StrUtil.isNotEmpty(item)) {
            return redisTemplate.opsForHash().increment(key, item, -by);
        } else {
            return -1;
        }
    }

    // ============================set=============================

    /**
     * 根据key获取Set中的所有值
     *
     * @param key 键
     * @return 值
     */
    public Set<Object> getSet(String key) {
        try {
            if (StrUtil.isNotEmpty(key)) {
                return redisTemplate.opsForSet().members(key);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 根据value从一个set中查询,是否存在
     *
     * @param key   键
     * @param value 值
     * @return true:存在; false:不存在
     */
    public boolean existSetKey(String key, Object value) {
        try {
            if (StrUtil.isNotEmpty(key) && null != value) {
                return redisTemplate.opsForSet().isMember(key, value);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 将数据放入set缓存,设置过期时间
     *
     * @param key    键
     * @param values 值
     * @return 成功个数
     */
    public long setSet(String key, Object... values) {
        try {
            if (StrUtil.isNotEmpty(key) && null != values) {
                return redisTemplate.opsForSet().add(key, values);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 将set数据放入缓存,指定保存时间
     *
     * @param key    键
     * @param time   时间(秒)
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long setSetExpire(String key, long time, Object... values) {
        try {
            if (StrUtil.isNotEmpty(key) && null != values) {
                Long count = redisTemplate.opsForSet().add(key, values);
                if (time > 0) {
                    expire(key, time);
                }
                return count;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 获取set缓存的长度
     *
     * @param key 键
     * @return 使用事务事为0
     */
    public long getSetSize(String key) {
        try {
            if (StrUtil.isNotEmpty(key)) {
                return redisTemplate.opsForSet().size(key);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 移除值为value的
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 移除的个数
     */
    public long removeSet(String key, Object... values) {
        try {
            if (StrUtil.isNotEmpty(key) && null != values) {
                return redisTemplate.opsForSet().remove(key, values);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    // ===============================list=================================

    /**
     * 获取list缓存的内容
     *
     * @param key   键
     * @param start 开始
     * @param end   结束  0 到 -1代表所有值
     * @return 结果集
     */
    public List<Object> getList(String key, long start, long end) {
        try {
            if (StrUtil.isNotEmpty(key)) {
                return redisTemplate.opsForList().range(key, start, end);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取list缓存的长度
     *
     * @param key 键
     * @return 长度
     */
    public long getListSize(String key) {
        try {
            if (StrUtil.isNotEmpty(key)) {
                return redisTemplate.opsForList().size(key);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 通过索引 获取list中的值
     *
     * @param key   键
     * @param index 索引  index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
     * @return 值
     */
    public Object getListIndex(String key, long index) {
        try {
            if (StrUtil.isNotEmpty(key)) {
                return redisTemplate.opsForList().index(key, index);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @return 是否成功
     */
    public boolean setList(String key, List<Object> value) {
        try {
            if (StrUtil.isNotEmpty(key) && null != value) {
                redisTemplate.opsForList().rightPushAll(key, value);
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 将list放入缓存,指定时间
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     * @return 是否成功
     */
    public boolean setListExpire(String key, List<Object> value, long time) {
        try {
            if (StrUtil.isNotEmpty(key) && null != value) {
                redisTemplate.opsForList().rightPushAll(key, value);
                if (time > 0) {
                    expire(key, time);
                }
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 根据索引修改list中的某条数据
     *
     * @param key   键
     * @param index 索引
     * @param value 值
     * @return 是否成功
     */
    public boolean updateListIndex(String key, long index, Object value) {
        try {
            if (StrUtil.isNotEmpty(key) && null != value) {
                redisTemplate.opsForList().set(key, index, value);
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 移除N个值为value
     *
     * @param key   键
     * @param count 移除多少个
     * @param value 值
     * @return 移除的个数
     */
    public long removeList(String key, long count, Object value) {
        try {
            if (StrUtil.isNotEmpty(key) && null != value) {
                return redisTemplate.opsForList().remove(key, count, value);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

}

ConvertUtils

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.BeanUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.sql.Date;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 转换
 * @author Windows
 */
@Slf4j
public class ConvertUtils {
    public static boolean isEmpty(Object object) {
        if (object == null) {
            return (true);
        }
        if ("".equals(object)) {
            return (true);
        }
        if ("null".equals(object)) {
            return (true);
        }
        return (false);
    }

    public static boolean isNotEmpty(Object object) {
        if (object != null && !"".equals(object) && !"null".equals(object)) {
            return (true);
        }
        return (false);
    }

    public static String decode(String strIn, String sourceCode, String targetCode) {
        return code2code(strIn, sourceCode, targetCode);
    }

    public static String StrToUTF(String strIn, String sourceCode, String targetCode) {
        strIn = "";
        try {
            strIn = new String(strIn.getBytes(StandardCharsets.ISO_8859_1), "GBK");
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return strIn;

    }

    private static String code2code(String strIn, String sourceCode, String targetCode) {
        String strOut = null;
        if (strIn == null || "".equals(strIn.trim())) {
            return strIn;
        }
        try {
            byte[] b = strIn.getBytes(sourceCode);
            for (byte value : b) {
                System.out.print(value + "  ");
            }
            strOut = new String(b, targetCode);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return strOut;
    }

    public static int getInt(String s, int defval) {
        if (s == null || "".equals(s)) {
            return (defval);
        }
        try {
            return (Integer.parseInt(s));
        } catch (NumberFormatException e) {
            return (defval);
        }
    }

    public static int getInt(String s) {
        if (s == null || "".equals(s)) {
            return 0;
        }
        try {
            return (Integer.parseInt(s));
        } catch (NumberFormatException e) {
            return 0;
        }
    }

    public static int getInt(String s, Integer df) {
        if (s == null ||  "".equals(s)) {
            return df;
        }
        try {
            return (Integer.parseInt(s));
        } catch (NumberFormatException e) {
            return 0;
        }
    }

    public static Integer[] getInts(String[] s) {
        Integer[] integer = new Integer[s.length];
        if (s == null) {
            return null;
        }
        for (int i = 0; i < s.length; i++) {
            integer[i] = Integer.parseInt(s[i]);
        }
        return integer;

    }

    public static double getDouble(String s, double defval) {
        if (s == null ||  "".equals(s)) {
            return (defval);
        }
        try {
            return (Double.parseDouble(s));
        } catch (NumberFormatException e) {
            return (defval);
        }
    }

    public static double getDou(Double s, double defval) {
        if (s == null) {
            return (defval);
        }
        return s;
    }

	/*public static Short getShort(String s) {
		if (StringUtil.isNotEmpty(s)) {
			return (Short.parseShort(s));
		} else {
			return null;
		}
	}*/

    public static int getInt(Object object, int defval) {
        if (isEmpty(object)) {
            return (defval);
        }
        try {
            return (Integer.parseInt(object.toString()));
        } catch (NumberFormatException e) {
            return (defval);
        }
    }

    public static Integer getInt(Object object) {
        if (isEmpty(object)) {
            return null;
        }
        try {
            return (Integer.parseInt(object.toString()));
        } catch (NumberFormatException e) {
            return null;
        }
    }

    public static int getInt(BigDecimal s, int defval) {
        if (s == null) {
            return (defval);
        }
        return s.intValue();
    }

    public static Integer[] getIntegerArry(String[] object) {
        int len = object.length;
        Integer[] result = new Integer[len];
        try {
            for (int i = 0; i < len; i++) {
                result[i] = new Integer(object[i].trim());
            }
            return result;
        } catch (NumberFormatException e) {
            return null;
        }
    }

    public static String getString(String s) {
        return (getString(s, ""));
    }

    /**
     * 转义成Unicode编码
     */
	/*public static String escapeJava(Object s) {
		return StringEscapeUtils.escapeJava(getString(s));
	}*/
    public static String getString(Object object) {
        if (isEmpty(object)) {
            return "";
        }
        return (object.toString().trim());
    }

    public static String getString(int i) {
        return (String.valueOf(i));
    }

    public static String getString(float i) {
        return (String.valueOf(i));
    }

    public static String getString(String s, String defval) {
        if (isEmpty(s)) {
            return (defval);
        }
        return (s.trim());
    }

    public static String getString(Object s, String defval) {
        if (isEmpty(s)) {
            return (defval);
        }
        return (s.toString().trim());
    }

    public static long stringToLong(String str) {
        long test = 0L;
        try {
            test = Long.parseLong(str);
        } catch (Exception e) {
        }
        return test;
    }

    /**
     * 获取本机IP
     */
    public static String getIp() {
        String ip = null;
        try {
            InetAddress address = InetAddress.getLocalHost();
            ip = address.getHostAddress();

        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return ip;
    }

    /**
     * 判断一个类是否为基本数据类型。
     *
     * @param clazz 要判断的类。
     * @return true 表示为基本数据类型。
     */
    private static boolean isBaseDataType(Class clazz) throws Exception {
        return (clazz.equals(String.class) || clazz.equals(Integer.class) || clazz.equals(Byte.class) || clazz.equals(Long.class) || clazz.equals(Double.class) || clazz.equals(Float.class) || clazz.equals(Character.class) || clazz.equals(Short.class) || clazz.equals(BigDecimal.class) || clazz.equals(BigInteger.class) || clazz.equals(Boolean.class) || clazz.equals(Date.class) || clazz.isPrimitive());
    }

    /**
     * @param request IP
     * @return IP Address
     */
    public static String getIpAddrByRequest(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    /**
     * @return 本机IP
     * @throws SocketException
     */
    public static String getRealIp() throws SocketException {
        String localip = null;// 本地IP,如果没有配置外网IP则返回它
        String netip = null;// 外网IP

        Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();
        InetAddress ip = null;
        boolean finded = false;// 是否找到外网IP
        while (netInterfaces.hasMoreElements() && !finded) {
            NetworkInterface ni = netInterfaces.nextElement();
            Enumeration<InetAddress> address = ni.getInetAddresses();
            while (address.hasMoreElements()) {
                ip = address.nextElement();
                if (!ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) {// 外网IP
                    netip = ip.getHostAddress();
                    finded = true;
                    break;
                } else if (ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) {// 内网IP
                    localip = ip.getHostAddress();
                }
            }
        }

        if (netip != null && !"".equals(netip)) {
            return netip;
        } else {
            return localip;
        }
    }

    /**
     * java去除字符串中的空格、回车、换行符、制表符
     *
     * @param str
     * @return
     */
    public static String replaceBlank(String str) {
        String dest = "";
        if (str != null) {
            Pattern p = Pattern.compile("\\s*|\t|\r|\n");
            Matcher m = p.matcher(str);
            dest = m.replaceAll("");
        }
        return dest;

    }

    /**
     * 判断元素是否在数组内
     *
     * @param substring
     * @param source
     * @return
     */
    public static boolean isIn(String substring, String[] source) {
        if (source == null || source.length == 0) {
            return false;
        }
        for (int i = 0; i < source.length; i++) {
            String aSource = source[i];
            if (aSource.equals(substring)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取Map对象
     */
    public static Map<Object, Object> getHashMap() {
        return new HashMap<Object, Object>();
    }

    /**
     * SET转换MAP
     */
    public static Map<Object, Object> SetToMap(Set<Object> setobj) {
        Map<Object, Object> map = getHashMap();
        for (Iterator iterator = setobj.iterator(); iterator.hasNext(); ) {
            Map.Entry<Object, Object> entry = (Map.Entry<Object, Object>) iterator.next();
            map.put(entry.getKey().toString(), entry.getValue() == null ? "" : entry.getValue().toString().trim());
        }
        return map;

    }

    public static boolean isInnerIP(String ipAddress) {
        boolean isInnerIp = false;
        long ipNum = getIpNum(ipAddress);
        /**
         * 私有IP:A类 10.0.0.0-10.255.255.255 B类 172.16.0.0-172.31.255.255 C类 192.168.0.0-192.168.255.255 当然,还有127这个网段是环回地址
         **/
        long aBegin = getIpNum("10.0.0.0");
        long aEnd = getIpNum("10.255.255.255");
        long bBegin = getIpNum("172.16.0.0");
        long bEnd = getIpNum("172.31.255.255");
        long cBegin = getIpNum("192.168.0.0");
        long cEnd = getIpNum("192.168.255.255");
        isInnerIp = isInner(ipNum, aBegin, aEnd) || isInner(ipNum, bBegin, bEnd) || isInner(ipNum, cBegin, cEnd) || ipAddress.equals("127.0.0.1");
        return isInnerIp;
    }

    private static long getIpNum(String ipAddress) {
        String[] ip = ipAddress.split("\\.");
        long a = Integer.parseInt(ip[0]);
        long b = Integer.parseInt(ip[1]);
        long c = Integer.parseInt(ip[2]);
        long d = Integer.parseInt(ip[3]);

        long ipNum = a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d;
        return ipNum;
    }

    private static boolean isInner(long userIp, long begin, long end) {
        return (userIp >= begin) && (userIp <= end);
    }

    /**
     * 将下划线大写方式命名的字符串转换为驼峰式。
     * 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。</br>
     * 例如:hello_world->helloWorld
     *
     * @param name 转换前的下划线大写方式命名的字符串
     * @return 转换后的驼峰式命名的字符串
     */
    public static String camelName(String name) {
        StringBuilder result = new StringBuilder();
        // 快速检查
        if (name == null || name.isEmpty()) {
            // 没必要转换
            return "";
        } else if (!name.contains("_")) {
            // 不含下划线,仅将首字母小写
            //update-begin--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
            //update-begin--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
            return name.substring(0, 1).toLowerCase() + name.substring(1).toLowerCase();
            //update-end--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
        }
        // 用下划线将原始字符串分割
        String camels[] = name.split("_");
        for (String camel : camels) {
            // 跳过原始字符串中开头、结尾的下换线或双重下划线
            if (camel.isEmpty()) {
                continue;
            }
            // 处理真正的驼峰片段
            if (result.length() == 0) {
                // 第一个驼峰片段,全部字母都小写
                result.append(camel.toLowerCase());
            } else {
                // 其他的驼峰片段,首字母大写
                result.append(camel.substring(0, 1).toUpperCase());
                result.append(camel.substring(1).toLowerCase());
            }
        }
        return result.toString();
    }

    /**
     * 将下划线大写方式命名的字符串转换为驼峰式。
     * 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。</br>
     * 例如:hello_world,test_id->helloWorld,testId
     *
     * @param names 转换前的下划线大写方式命名的字符串
     * @return 转换后的驼峰式命名的字符串
     */
    public static String camelNames(String names) {
        if (names == null || names.equals("")) {
            return null;
        }
        StringBuffer sf = new StringBuffer();
        String[] fs = names.split(",");
        for (String field : fs) {
            field = camelName(field);
            sf.append(field + ",");
        }
        String result = sf.toString();
        return result.substring(0, result.length() - 1);
    }

    //update-begin--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能

    /**
     * 将下划线大写方式命名的字符串转换为驼峰式。(首字母写)
     * 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。</br>
     * 例如:hello_world->HelloWorld
     *
     * @param name 转换前的下划线大写方式命名的字符串
     * @return 转换后的驼峰式命名的字符串
     */
    public static String camelNameCapFirst(String name) {
        StringBuilder result = new StringBuilder();
        // 快速检查
        if (name == null || name.isEmpty()) {
            // 没必要转换
            return "";
        } else if (!name.contains("_")) {
            // 不含下划线,仅将首字母小写
            return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase();
        }
        // 用下划线将原始字符串分割
        String camels[] = name.split("_");
        for (String camel : camels) {
            // 跳过原始字符串中开头、结尾的下换线或双重下划线
            if (camel.isEmpty()) {
                continue;
            }
            // 其他的驼峰片段,首字母大写
            result.append(camel.substring(0, 1).toUpperCase());
            result.append(camel.substring(1).toLowerCase());
        }
        return result.toString();
    }
    //update-end--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能

    /**
     * 将驼峰命名转化成下划线
     *
     * @param para
     * @return
     */
    public static String camelToUnderline(String para) {
        if (para.length() < 3) {
            return para.toLowerCase();
        }
        StringBuilder sb = new StringBuilder(para);
        int temp = 0;//定位
        //从第三个字符开始 避免命名不规范 
        for (int i = 2; i < para.length(); i++) {
            if (Character.isUpperCase(para.charAt(i))) {
                sb.insert(i + temp, "_");
                temp += 1;
            }
        }
        return sb.toString().toLowerCase();
    }

    /**
     * 随机数
     *
     * @param place 定义随机数的位数
     */
    public static String randomGen(int place) {
        String base = "qwertyuioplkjhgfdsazxcvbnmQAZWSXEDCRFVTGBYHNUJMIKLOP0123456789";
        StringBuffer sb = new StringBuffer();
        Random rd = new Random();
        for (int i = 0; i < place; i++) {
            sb.append(base.charAt(rd.nextInt(base.length())));
        }
        return sb.toString();
    }

    /**
     * 获取类的所有属性,包括父类
     *
     * @param object 实体类
     * @return 所有的属性
     */
    public static Field[] getAllFields(Object object) {
        Class<?> clazz = object.getClass();
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        Field[] fields = new Field[fieldList.size()];
        fieldList.toArray(fields);
        return fields;
    }

    /**
     * 将map的key全部转成小写
     *
     * @param list
     * @return
     */
    public static List<Map<String, Object>> toLowerCasePageList(List<Map<String, Object>> list) {
        List<Map<String, Object>> select = new ArrayList<>();
        for (Map<String, Object> row : list) {
            Map<String, Object> resultMap = new HashMap<>();
            Set<String> keySet = row.keySet();
            for (String key : keySet) {
                String newKey = key.toLowerCase();
                resultMap.put(newKey, row.get(key));
            }
            select.add(resultMap);
        }
        return select;
    }

    /**
     * 将entityList转换成modelList
     *
     * @param fromList
     * @param tClass
     * @param <F>
     * @param <T>
     * @return
     */
    public static <F, T> List<T> entityListToModelList(List<F> fromList, Class<T> tClass) {
        if (fromList == null || fromList.isEmpty()) {
            return null;
        }
        List<T> tList = new ArrayList<>();
        for (F f : fromList) {
            T t = entityToModel(f, tClass);
            tList.add(t);
        }
        return tList;
    }

    public static <F, T> T entityToModel(F entity, Class<T> modelClass) {
        log.debug("entityToModel : Entity属性的值赋值到Model");
        Object model = null;
        if (entity == null || modelClass == null) {
            return null;
        }

        try {
            model = modelClass.newInstance();
        } catch (InstantiationException e) {
            log.error("entityToModel : 实例化异常", e);
        } catch (IllegalAccessException e) {
            log.error("entityToModel : 安全权限异常", e);
        }
        BeanUtils.copyProperties(entity, model);
        return (T) model;
    }

    /**
     * 判断 list 是否为空
     *
     * @param list
     * @return true or false
     * list == null		: true
     * list.size() == 0	: true
     */
    public static boolean listIsEmpty(Collection list) {
        return (list == null || list.size() == 0);
    }

    /**
     * 判断 list 是否不为空
     *
     * @param list
     * @return true or false
     * list == null		: false
     * list.size() == 0	: false
     */
    public static boolean listIsNotEmpty(Collection list) {
        return !listIsEmpty(list);
    }

    /**
     * 读取静态文本内容
     *
     * @param url
     * @return
     */
    public static String readStatic(String url) {
        String json = "";
        try {
            //换个写法,解决springboot读取jar包中文件的问题
            InputStream stream = ConvertUtils.class.getClassLoader().getResourceAsStream(url.replace("classpath:", ""));
            json = IOUtils.toString(stream, "UTF-8");
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
        return json;
    }
}

SqlInjectionUtil

防止SQL注入的

	package org.mohrss.leaf.common.utils;

	import cn.hutool.crypto.SecureUtil;
	import lombok.extern.slf4j.Slf4j;
	import org.mohrss.leaf.core.framework.web.exception.BusinessException;

	import javax.servlet.http.HttpServletRequest;
	import java.lang.reflect.Field;
	import java.util.Set;
	import java.util.regex.Matcher;
	import java.util.regex.Pattern;

	/**
	 * sql注入处理工具类
	 * @author Windows
	 */
	@Slf4j
	public class SqlInjectionUtil {
		/**
		 * sign 用于表字典加签的盐值【SQL漏洞】
		 * (上线修改值 20200501,同步修改前端的盐值)
		 */
		private final static String TABLE_DICT_SIGN_SALT = "20200501";
		private final static String XSS_STR = "and |extractvalue|updatexml|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";

		/**
		 * sql注释的正则
		 */
		private final static Pattern SQL_ANNOTATION = Pattern.compile("/\\*.*\\*/");

		/**
		 * 针对表字典进行额外的sign签名校验(增加安全机制)
		 * @param dictCode:
		 * @param sign:
		 * @param request:
		 * @Return: void
		 */
		public static void checkDictTableSign(String dictCode, String sign, HttpServletRequest request) {
			//表字典SQL注入漏洞,签名校验
			String accessToken = request.getHeader("X-Access-Token");
			String signStr = dictCode + SqlInjectionUtil.TABLE_DICT_SIGN_SALT + accessToken;
			String javaSign = SecureUtil.md5(signStr);
			if (!javaSign.equals(sign)) {
				log.error("表字典,SQL注入漏洞签名校验失败 :" + sign + "!=" + javaSign+ ",dicCode=" + dictCode);
				throw new BusinessException("无权限访问!");
			}
			log.info(" 表字典,SQL注入漏洞签名校验成功!sign=" + sign + ",dicCode=" + dictCode);
		}

		/**
		 * sql注入过滤处理,遇到注入关键字抛异常
		 * @param value
		 */
		public static void filterContent(String value) {
			filterContent(value, null);
		}

		/**
		 * 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) {
					log.error("请注意,存在SQL注入关键词---> {}", xssArr[i]);
					log.error("请注意,值可能存在SQL注入风险!---> {}", value);
					throw new RuntimeException("请注意,值可能存在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) {
						log.error("请注意,存在SQL注入关键词---> {}", xssArr2[i]);
						log.error("请注意,值可能存在SQL注入风险!---> {}", value);
						throw new RuntimeException("请注意,值可能存在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 RuntimeException("请注意,值可能存在SQL注入风险!--->" + value);
			}
			return;
		}

		/**
		 * sql注入过滤处理,遇到注入关键字抛异常
		 * @param values
		 */
		public static void filterContent(String[] values) {
			filterContent(values, null);
		}

		/**
		 * sql注入过滤处理,遇到注入关键字抛异常
		 *
		 * @param values
		 * @return
		 */
		public static void filterContent(String[] values, String customXssString) {
			String[] xssArr = XSS_STR.split("\\|");
			for (String value : values) {
				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("/\\*.*\\*/","");

				for (int i = 0; i < xssArr.length; i++) {
					if (value.indexOf(xssArr[i]) > -1) {
						log.error("请注意,存在SQL注入关键词---> {}", xssArr[i]);
						log.error("请注意,值可能存在SQL注入风险!---> {}", value);
						throw new RuntimeException("请注意,值可能存在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) {
							log.error("请注意,存在SQL注入关键词---> {}", xssArr2[i]);
							log.error("请注意,值可能存在SQL注入风险!---> {}", value);
							throw new RuntimeException("请注意,值可能存在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 RuntimeException("请注意,值可能存在SQL注入风险!--->" + value);
				}
			}
			return;
		}

		/**
		 * 【提醒:不通用】
		 * 仅用于字典条件SQL参数,注入过滤
		 *
		 * @param value
		 * @return
		 */
		//@Deprecated
		public static void specialFilterContentForDictSql(String value) {
			String specialXssStr = " exec |extractvalue|updatexml| insert | select | delete | update | drop | count | chr | mid | master | truncate | char | declare |;|+|user()";
			String[] xssArr = specialXssStr.split("\\|");
			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("/\\*.*\\*/","");

			for (int i = 0; i < xssArr.length; i++) {
				if (value.indexOf(xssArr[i]) > -1 || value.startsWith(xssArr[i].trim())) {
					log.error("请注意,存在SQL注入关键词---> {}", xssArr[i]);
					log.error("请注意,值可能存在SQL注入风险!---> {}", value);
					throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value);
				}
			}
			if(Pattern.matches(SHOW_TABLES, value) || Pattern.matches(REGULAR_EXPRE_USER, value)){
				throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value);
			}
			return;
		}


		/**
		 * 【提醒:不通用】
		 *  仅用于Online报表SQL解析,注入过滤
		 * @param value
		 * @return
		 */
		//@Deprecated
		public static void specialFilterContentForOnlineReport(String value) {
			String specialXssStr = " exec |extractvalue|updatexml| insert | delete | update | drop | chr | mid | master | truncate | char | declare |user()";
			String[] xssArr = specialXssStr.split("\\|");
			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("/\\*.*\\*/"," ");

			for (int i = 0; i < xssArr.length; i++) {
				if (value.indexOf(xssArr[i]) > -1 || value.startsWith(xssArr[i].trim())) {
					log.error("请注意,存在SQL注入关键词---> {}", xssArr[i]);
					log.error("请注意,值可能存在SQL注入风险!---> {}", value);
					throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value);
				}
			}

			if(Pattern.matches(SHOW_TABLES, value) || Pattern.matches(REGULAR_EXPRE_USER, value)){
				throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value);
			}
			return;
		}


		/**
		 * 判断给定的字段是不是类中的属性
		 * @param field 字段名
		 * @param clazz 类对象
		 * @return
		 */
		public static boolean isClassField(String field, Class clazz){
			Field[] fields = clazz.getDeclaredFields();
			for(int i=0;i<fields.length;i++){
				String fieldName = fields[i].getName();
				String tableColumnName = ConvertUtils.camelToUnderline(fieldName);
				if(fieldName.equalsIgnoreCase(field) || tableColumnName.equalsIgnoreCase(field)){
					return true;
				}
			}
			return false;
		}

		/**
		 * 判断给定的多个字段是不是类中的属性
		 * @param fieldSet 字段名set
		 * @param clazz 类对象
		 * @return
		 */
		public static boolean isClassField(Set<String> fieldSet, Class clazz){
			Field[] fields = clazz.getDeclaredFields();
			for(String field: fieldSet){
				boolean exist = false;
				for(int i=0;i<fields.length;i++){
					String fieldName = fields[i].getName();
					String tableColumnName = ConvertUtils.camelToUnderline(fieldName);
					if(fieldName.equalsIgnoreCase(field) || tableColumnName.equalsIgnoreCase(field)){
						exist = true;
						break;
					}
				}
				if(!exist){
					return false;
				}
			}
			return true;
		}

		/**
		 * 校验是否有sql注释
		 * @return
		 */
		public static void checkSqlAnnotation(String str){
			Matcher matcher = SQL_ANNOTATION.matcher(str);
			if(matcher.find()){
				String error = "请注意,值可能存在SQL注入风险---> \\*.*\\";
				log.error(error);
				throw new RuntimeException(error);
			}
		}
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LOVE_DDZ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值