Ver código fonte

省市区县校验

wangwl 3 semanas atrás
pai
commit
5bb3a8a0a9

+ 20 - 0
leromro-common/src/main/java/com/leromro/common/constant/CommonConstants.java

@@ -0,0 +1,20 @@
+package com.leromro.common.constant;
+
+import cn.hutool.core.collection.CollectionUtil;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 通过静态常量
+ */
+public class CommonConstants {
+    //权限初始化集合
+    public static final List<String> GROUP_CONSTANT = Arrays.asList("province_code","city_code","district_code");
+    //校验省
+    public static final String CHECK_PROVINCE = "province_code";
+    //校验省市
+    public static final String CHECK_PROVINCE_CITY= "province_code,city_code";
+    //校验省市区
+    public static final String CHECK_PROVINCE_CITY_DISTRICT = "province_code,city_code,district_code";
+}

+ 31 - 0
leromro-common/src/main/java/com/leromro/common/core/domain/ListPermission.java

@@ -0,0 +1,31 @@
+package com.leromro.common.core.domain;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ *  1 对于SQL进行条件拼接,先从参数实体中获取是否包含校验对象
+ * 	2 如果包含校验对象,和实体类对象,则进入下阶段
+ */
+@Data
+public class ListPermission implements Serializable {
+    /**
+     * 业务表对象
+     */
+    private Class<?> aClass;
+
+    /**
+     * 权限判断分组结合,多个逗号隔开:province_code city_code, district_code
+     */
+    private String permissionGroup;
+
+    public ListPermission(Class<?> aClass) {
+        this.aClass = aClass;
+    }
+
+    public ListPermission(Class<?> aClass, String permissionGroup) {
+        this.aClass = aClass;
+        this.permissionGroup = permissionGroup;
+    }
+}

+ 78 - 0
leromro-common/src/main/java/com/leromro/common/core/domain/entity/SysUser.java

@@ -5,6 +5,7 @@ import java.util.List;
 import javax.validation.constraints.*;
 
 import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModelProperty;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
 import com.leromro.common.annotation.Excel;
@@ -105,6 +106,83 @@ public class SysUser extends BaseEntity
     /** 1 用户  2 志愿者 **/
     private Integer userOrWorker;
 
+    @TableField("province_code")
+    @ApiModelProperty("省级区划编号")
+    private String provinceCode;
+
+    /** 省级名称 */
+    @TableField("province_name")
+    @ApiModelProperty("省级名称")
+    private String provinceName;
+
+    /** 市级编号 */
+    @TableField("city_code")
+    @ApiModelProperty("市级编号")
+    private String cityCode;
+
+    /** 市级名称 */
+    @TableField("city_name")
+    @ApiModelProperty("市级名称")
+    private String cityName;
+
+    /** 区级区划编号 */
+    @TableField("district_code")
+    @ApiModelProperty("区级区划编号")
+    private String districtCode;
+
+    /** 区级名称 */
+    @TableField("district_name")
+    @ApiModelProperty("区级名称")
+    private String districtName;
+
+    public String getProvinceCode() {
+        return provinceCode;
+    }
+
+    public void setProvinceCode(String provinceCode) {
+        this.provinceCode = provinceCode;
+    }
+
+    public String getProvinceName() {
+        return provinceName;
+    }
+
+    public void setProvinceName(String provinceName) {
+        this.provinceName = provinceName;
+    }
+
+    public String getCityCode() {
+        return cityCode;
+    }
+
+    public void setCityCode(String cityCode) {
+        this.cityCode = cityCode;
+    }
+
+    public String getCityName() {
+        return cityName;
+    }
+
+    public void setCityName(String cityName) {
+        this.cityName = cityName;
+    }
+
+    public String getDistrictCode() {
+        return districtCode;
+    }
+
+    public void setDistrictCode(String districtCode) {
+        this.districtCode = districtCode;
+    }
+
+    public String getDistrictName() {
+        return districtName;
+    }
+
+    public void setDistrictName(String districtName) {
+        this.districtName = districtName;
+    }
+
     public Integer getUserPlatform() {
         return userPlatform;
     }

+ 59 - 0
leromro-common/src/main/java/com/leromro/common/utils/PermissionUtil.java

@@ -0,0 +1,59 @@
+package com.leromro.common.utils;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.leromro.common.constant.CommonConstants;
+import com.leromro.common.core.domain.ListPermission;
+import org.apache.commons.lang3.reflect.FieldUtils;
+
+import java.lang.reflect.Field;
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class PermissionUtil {
+
+    /**
+     * 从参数实体获取数据权限对象
+     *
+     * @param parameterObject mapper参数集合
+     * @return
+     */
+    public static Optional<ListPermission> findPermission(Object parameterObject) {
+        if (parameterObject != null) {
+            if (parameterObject instanceof Map) {
+                Map<?, ?> parameterMap = (Map) parameterObject;
+                Iterator var2 = parameterMap.entrySet().iterator();
+
+                while (var2.hasNext()) {
+                    Map.Entry entry = (Map.Entry) var2.next();
+                    if (entry.getValue() != null && entry.getValue() instanceof ListPermission) {
+                        return Optional.ofNullable((ListPermission) entry.getValue());
+                    }
+                }
+            } else if (parameterObject instanceof ListPermission) {
+                return Optional.of((ListPermission) parameterObject);
+            }
+        }
+        return Optional.empty();
+    }
+
+    /**
+     * 通过反射判断存在TableField的字段,并返回校验类中的字段
+     * @param tableEntity
+     * @return
+     */
+    public static List<String> getGroupMappers(Class<?> tableEntity) {
+        List<Field> fields = FieldUtils.getFieldsListWithAnnotation(tableEntity, TableField.class);
+        return fields.stream()
+                .map(f ->{
+                    TableField annotation = f.getAnnotation(TableField.class);
+                    if (Objects.isNull(annotation)){
+                        return null;
+                    }
+                    return annotation.value();
+                })
+                .filter(CommonConstants.GROUP_CONSTANT::contains)
+                .collect(Collectors.toList());
+
+    }
+
+}

+ 28 - 0
leromro-common/src/main/java/com/leromro/common/utils/TableUtil.java

@@ -0,0 +1,28 @@
+package com.leromro.common.utils;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import java.lang.reflect.Field;
+import java.util.List;
+
+/**
+ * 反射Table工具类
+ */
+public class TableUtil {
+
+    //创建getTableIdName方法,参数为List<Field> fields,从fields中获取表主键名称
+    public static String getTableIdName(List<Field> fields) {
+        for (java.lang.reflect.Field field : fields) {
+            if (field.isAnnotationPresent(TableId.class)) {
+                return field.getName();
+            }
+        }
+        return null;
+    }
+    //创建getTableName方法,参数为Class<?> tableEntity,从tableEntity中获取表名
+    public static String getTableName(Class<?> tableEntity) {
+        //从tableEntity中获取mysql表名
+        return tableEntity.getAnnotation(TableName.class).value();
+    }
+}

+ 3 - 0
leromro-framework/src/main/java/com/leromro/framework/config/MybatisPlusConfig.java

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import com.leromro.framework.interceptor.ListPermissionInterceptor;
 import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -25,6 +26,8 @@ public class MybatisPlusConfig {
         interceptor.addInnerInterceptor(paginationInnerInterceptor());
         // 乐观锁插件
         interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
+        //省市区拦截器
+        interceptor.addInnerInterceptor(new ListPermissionInterceptor());
         // 防止全表更新与删除插件
         interceptor.addInnerInterceptor(blockAttackInnerInterceptor());
         return interceptor;

+ 177 - 0
leromro-framework/src/main/java/com/leromro/framework/interceptor/ListPermissionInterceptor.java

@@ -0,0 +1,177 @@
+package com.leromro.framework.interceptor;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
+import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
+import com.leromro.common.core.domain.ListPermission;
+import com.leromro.common.core.domain.entity.SysUser;
+import com.leromro.common.core.domain.model.LoginUser;
+import com.leromro.common.utils.PermissionUtil;
+import com.leromro.common.utils.SecurityUtils;
+import com.leromro.common.utils.ServletUtils;
+import com.leromro.common.utils.TableUtil;
+import lombok.extern.slf4j.Slf4j;
+import net.sf.jsqlparser.JSQLParserException;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.StringValue;
+import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
+import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
+import net.sf.jsqlparser.parser.CCJSqlParserUtil;
+import net.sf.jsqlparser.parser.SimpleNode;
+import net.sf.jsqlparser.schema.Column;
+import net.sf.jsqlparser.schema.Table;
+import net.sf.jsqlparser.statement.select.*;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.ibatis.executor.Executor;
+import org.apache.ibatis.executor.statement.StatementHandler;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.plugin.Intercepts;
+import org.apache.ibatis.plugin.Signature;
+import org.apache.ibatis.session.ResultHandler;
+import org.apache.ibatis.session.RowBounds;
+
+
+import java.lang.reflect.Field;
+import java.sql.Connection;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * @author yanglongping
+ * Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) 拦截执行器的方法;
+ * ParameterHandler (getParameterObject, setParameters) 拦截参数的处理;
+ * ResultSetHandler (handleResultSets, handleOutputParameters) 拦截结果集的处理;
+ * StatementHandler (prepare, parameterize, batch, update, query) 拦截Sql语法构建的处理
+ */
+@Slf4j
+@Intercepts({@Signature(type = StatementHandler.class, method = "query", args = {Connection.class, Integer.class})})
+public class ListPermissionInterceptor implements InnerInterceptor {
+
+    private static final Map<String, String> fieldMap = MapUtil.builder(new HashMap<String, String>())
+            .put("province_code","provinceCode")
+            .put("city_code","cityCode")
+            .put("district_code", "districtCode")
+            .build();
+
+    @Override
+    public void beforeQuery(Executor executor, MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
+        // 1.判断是否需要修改SQL
+        ListPermission permission = PermissionUtil.findPermission(parameterObject).orElse(null); //获取数据权限参数
+        //如果不存在校验类,或者校验类中不存在校验字段,或权限实体为空,则跳过拦截器
+        if (permission == null || StringUtils.isBlank(permission.getPermissionGroup()) || permission.getAClass() == null) return;
+        //获取待校验字段
+        List<String> groupList = Stream.of(StringUtils.split(permission.getPermissionGroup(), ","))
+                .filter(StringUtils::isNotBlank)
+                .map(String::trim)
+                .collect(Collectors.toList());
+        SysUser user = SecurityUtils.getLoginUser().getUser();
+        //获取权限实体所有字段
+        List<Field> fields = FieldUtils.getAllFieldsList(permission.getAClass());
+        //获取表主键名称
+        String tableIdName = TableUtil.getTableIdName(fields);
+        //获取表名
+        String tableName = TableUtil.getTableName(permission.getAClass());
+        //获取权限实体所有校验字段
+        List<String> fieldList = PermissionUtil.getGroupMappers(permission.getAClass());
+        if (CollUtil.isEmpty(fieldList)) return;
+        //获取fieldList中groupList的字段
+        List<String> groupFieldList = groupList.stream()
+                .filter(fieldList::contains)
+                .collect(Collectors.toList());
+        if (CollUtil.isEmpty(groupFieldList)) return;
+        // 2.根据职责、权限实体修改SQL
+        String oldSql = boundSql.getSql();
+//        log.info("targetSql:{}", oldSql);
+        String targetSql = "";
+        try {
+            Select selectStatement = (Select) CCJSqlParserUtil.parse(oldSql);
+            SelectBody selectBody = selectStatement.getSelectBody();
+            if (selectBody == null) {
+                return;
+            }
+            if (selectBody instanceof PlainSelect) {
+                PlainSelect plainSelect = (PlainSelect) selectBody;
+                // 获取所有表及其别名
+                Map<String, String> tableAliases = getAllTableAliases(plainSelect);
+                //获取别名
+                String alias = tableAliases.getOrDefault(tableName, null);
+                //拼接where
+                Expression where = plainSelect.getWhere();
+                for (String column : groupFieldList) {
+                    // 检查 column 是否与用户字段相同
+                    String userFieldValue = getUserFieldValue(user, column);
+                    if (StringUtils.isBlank(userFieldValue)) {
+                        continue;
+                    }
+                    if (StringUtils.isNotBlank(alias)){
+                        column = alias + "." + column;
+                    }
+                    EqualsTo usesEqualsTo = new EqualsTo();
+                    usesEqualsTo.setLeftExpression(new Column(column));
+                    //当column和user字段相同时,setRightExpression为user中字段的值
+                    usesEqualsTo.setRightExpression(new StringValue(userFieldValue));
+                    if (where == null) {
+                        where = usesEqualsTo;
+                    } else {
+                        where = new AndExpression(where, usesEqualsTo);
+                    }
+                }
+                // 将新的where条件sql 设置回去
+                plainSelect.setWhere(where);
+                targetSql = plainSelect.toString();
+//                log.info("targetSql:{}", targetSql);
+            }
+        } catch (JSQLParserException e) {
+            e.printStackTrace();
+        }
+
+        // 3.修改完成的sql 再设置回去
+        PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
+        mpBoundSql.sql(targetSql);
+    }
+
+    private Map<String, String> getAllTableAliases(PlainSelect plainSelect) {
+        Map<String, String> tableAliases = new HashMap<>();
+        Table table = plainSelect.getFromItem(Table.class);
+        if (ObjectUtil.isNotNull(table.getAlias())) {
+            tableAliases.put(table.getName(),table.getAlias().getName());
+        }
+        List<Join> joins = plainSelect.getJoins();
+        if (CollUtil.isNotEmpty(joins)) {
+            for (Join join : joins) {
+                Table rightItem = (Table)join.getRightItem();
+                if (ObjectUtil.isNotNull(rightItem.getAlias())) {
+                    tableAliases.put(rightItem.getName(),rightItem.getAlias().getName());
+                }
+            }
+        }
+
+        return tableAliases;
+    }
+
+    private String getUserFieldValue(SysUser user, String column) {
+        try {
+            Field field = FieldUtils.getDeclaredField(user.getClass(), fieldMap.get(column), true);
+            if (field != null) {
+                field.setAccessible(true);
+                Object value = field.get(user);
+                if (value != null) {
+                    return value.toString();
+                }
+            }
+        } catch (Exception e) {
+            log.error("Error getting user field value for column: {}", column, e);
+        }
+        return null;
+    }
+
+
+}

+ 7 - 0
leromro-system/src/main/resources/mapper/system/SysUserMapper.xml

@@ -25,6 +25,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="remark"       column="remark"       />
         <result property="userOrWorker"       column="user_or_worker"       />
         <result property="userPlatform"       column="user_platform"       />
+        <result property="provinceCode"       column="province_code"       />
+        <result property="provinceName"       column="province_name"       />
+        <result property="cityCode"       column="city_code"       />
+        <result property="cityName"       column="city_name"       />
+        <result property="districtCode"       column="district_code"       />
+        <result property="districtName"       column="district_name"       />
         <association property="dept"    javaType="SysDept"         resultMap="deptResult" />
         <collection  property="roles"   javaType="java.util.List"  resultMap="RoleResult" />
     </resultMap>
@@ -50,6 +56,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 	
 	<sql id="selectUserVo">
         select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, u.user_or_worker as user_or_worker,u.user_platform as user_platform,
+               u.province_code as province_code, u.province_name as province_name, u.city_code as city_code, u.city_name as city_name, u.district_code as district_code, u.district_name as district_name,
         d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status,
         r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status
         from sys_user u