Przeglądaj źródła

新增部门权限功能

chenminghua 1 rok temu
rodzic
commit
d8fa920978

+ 5 - 0
.gitignore

@@ -0,0 +1,5 @@
+/.idea
+/ims-service/ims-eval/target
+/ims-gateway/target
+/ims-common/target
+/ims-service-api/ims-am-api/target

+ 9 - 1
README.md

@@ -1,3 +1,11 @@
 # evaluation
 
-对标考评项目
+对标考评项目
+
+## 权限配置
+1、使用mybatis的拦截器Interceptor统一处理 
+
+2、使用方式:
+- 在com.ims.eval.dao目录下的Mapper添加@DataPersion注解
+- 在类上添加则默认全部方法都添加此权限,如果某个方法不需要此权限则添加注解值为false
+- 具体可参考IndicatorMapper类的使用

+ 2 - 0
ims-service/ims-eval/src/main/java/com/ims/eval/EvalApplication.java

@@ -2,6 +2,7 @@ package com.ims.eval;
 
 
 import org.springblade.core.launch.BladeApplication;
+import org.springframework.cache.annotation.EnableCaching;
 import org.springframework.cloud.client.SpringCloudApplication;
 import org.springframework.cloud.openfeign.EnableFeignClients;
 import org.springframework.context.annotation.ComponentScan;
@@ -17,6 +18,7 @@ import org.springframework.context.annotation.ComponentScans;
 	@ComponentScan("com.ims.ext.act")
 })
 @EnableFeignClients
+@EnableCaching
 public class EvalApplication {
 
 	public static void main(String[] args) {

+ 22 - 0
ims-service/ims-eval/src/main/java/com/ims/eval/config/permission/DataPermission.java

@@ -0,0 +1,22 @@
+package com.ims.eval.config.permission;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * 数据权限注解
+ * @author gyee
+ */
+@Documented
+@Target({METHOD, ANNOTATION_TYPE, TYPE})
+@Retention(RUNTIME)
+public @interface DataPermission {
+	/**
+	 * 是否要进行数据权限隔离
+	 */
+	boolean isPermission() default true;
+}

+ 219 - 0
ims-service/ims-eval/src/main/java/com/ims/eval/config/permission/PermissionAspect.java

@@ -0,0 +1,219 @@
+package com.ims.eval.config.permission;
+
+import cn.hutool.core.util.ClassUtil;
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
+import com.ims.eval.cache.CacheContext;
+import com.ims.eval.entity.DataDictionary;
+import com.ims.eval.entity.dto.response.MyuserResDTO;
+import com.ims.eval.feign.RemoteServiceBuilder;
+import lombok.extern.slf4j.Slf4j;
+import net.sf.jsqlparser.JSQLParserException;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
+import net.sf.jsqlparser.parser.CCJSqlParserUtil;
+import net.sf.jsqlparser.statement.select.PlainSelect;
+import net.sf.jsqlparser.statement.select.Select;
+import org.apache.ibatis.executor.statement.StatementHandler;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.plugin.Interceptor;
+import org.apache.ibatis.plugin.Intercepts;
+import org.apache.ibatis.plugin.Invocation;
+import org.apache.ibatis.plugin.Signature;
+import org.apache.ibatis.reflection.MetaObject;
+import org.apache.ibatis.reflection.SystemMetaObject;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import springfox.documentation.annotations.Cacheable;
+
+import javax.servlet.http.HttpServletRequest;
+import java.lang.reflect.Method;
+import java.sql.Connection;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author gyee
+ */
+@Slf4j
+@Aspect
+@Component
+@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
+public class PermissionAspect implements Interceptor {
+
+	@Autowired
+	private RemoteServiceBuilder serviceBuilder;
+
+	//扫描的包路径,需要权限的加在mapper类及方法上
+	private String packagePath = "com.ims.eval.dao";
+	private final static String DEPT_ID = "dept";
+	private final static String DEPT_MARK = "BM0001";
+
+	/**  start  以下定义的数据需要和部门表一致       **/
+	//全部数据权限
+	private static final Integer DATA_SCOPE_ALL = 1;
+	//部门数据权限
+	private static final Integer DATA_SCOPE_DEPT = 2;
+	//个人数据权限
+	private static final Integer DATA_SCOPE_CUSTOM = 3;
+	/**  end  以下定义的数据需要和部门表一致       **/
+
+	//请求头code
+	private String code;
+	//缓存权限类名
+	private static List<String> classNames;
+	//缓存权限类名+方法名
+	private static Map<String, Boolean> methodNames = new HashMap<>();
+
+	/**
+	 * 对TestService类下面的所有方法拦截.
+	 */
+	@Pointcut("execution(* com.ims.eval.controller.*.*(..))")
+	public void pointcut() {
+	}
+
+	//前置通知
+	@Before("pointcut()")
+	public void beforeMethod(JoinPoint joinPoint) {
+			ServletRequestAttributes attributes =
+				(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+			HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
+			code =  request.getHeader("code");
+	}
+
+	@Override
+	public Object intercept(Invocation invocation) throws Throwable {
+		MyuserResDTO user = getSysUser(code);
+		DataDictionary dept = getSysDept(user);
+		if (user == null || dept == null) {
+			return invocation.proceed();
+		}
+
+		Integer scope = dept.getScope();
+		if (scope.equals(DATA_SCOPE_ALL)){
+			return invocation.proceed();
+		}
+		if (scope.equals(DATA_SCOPE_DEPT)) {
+			String deptId = dept.getDataKey();
+			try {
+				//反射扫包会比较慢,这里做了个懒加载
+				if (classNames == null) {
+					//扫描指定包路径下所有包含指定注解的类
+					Set<Class<?>> classSet = ClassUtil.scanPackageByAnnotation(packagePath, DataPermission.class);
+					if (classSet == null && classSet.size() == 0) {
+						classNames = new ArrayList<>();
+					} else {
+						//取得类全名
+						classNames = classSet.stream().map(Class::getName).collect(Collectors.toList());
+					}
+				}
+
+				// 拿到mybatis的一些对象
+				StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
+				MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
+				MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
+
+				// mappedStatement.getId()为执行的mapper方法的全路径名,newId为执行的mapper方法的类全名
+				String newId = mappedStatement.getId().substring(0, mappedStatement.getId().lastIndexOf("."));
+				// 如果不是指定的方法,直接结束拦截
+				if (!classNames.contains(newId)) {
+					return invocation.proceed();
+				}
+				String newName = mappedStatement.getId().substring(mappedStatement.getId().lastIndexOf(".") + 1, mappedStatement.getId().length());
+				Class<?> clazz = Class.forName(newId);
+
+				if (!methodNames.containsKey(newId + "-" + newName)){
+					for (Method method : clazz.getDeclaredMethods()) {
+						//方法是否含有DataPermission注解,如果含有注解则将数据结果过滤
+						if (method.isAnnotationPresent(DataPermission.class)) {
+							DataPermission dataPermission = method.getAnnotation(DataPermission.class);
+							if (dataPermission != null) {
+								methodNames.put(newId + "-" + method.getName(), dataPermission.isPermission());
+							}
+						} else {
+							methodNames.put(newId + "-" + method.getName(), true);
+						}
+					}
+				}
+
+				//是否开启数据权限
+				boolean isPermission = true;
+				isPermission = methodNames.get(newId + "-" + newName);
+				if (isPermission) {
+					// 获取到原始sql语句
+					String sql = statementHandler.getBoundSql().getSql();
+					// 解析并返回新的SQL语句,只处理查询sql
+					if (mappedStatement.getSqlCommandType().toString().equals("SELECT")) {
+						sql = getSql(sql, deptId);
+					}
+					// 修改sql
+					metaObject.setValue("delegate.boundSql.sql", sql);
+				}
+			} catch (Exception e) {
+				log.error("数据权限隔离异常:", e);
+			}
+		}
+		return invocation.proceed();
+	}
+
+	/**
+	 * 解析SQL语句,并返回新的SQL语句
+	 * 注意,该方法使用了JSqlParser来操作SQL,该依赖包Mybatis-plus已经集成了。如果要单独使用,请先自行导入依赖
+	 *
+	 * @param sql 原SQL
+	 * @return 新SQL
+	 */
+	private String getSql(String sql, String deptId) {
+		try {
+			// 修改原语句
+			String condition = DEPT_ID + "='" + deptId + "'";
+			Select select = (Select) CCJSqlParserUtil.parse(sql);
+			PlainSelect plainSelect = (PlainSelect)select.getSelectBody();
+			//取得原SQL的where条件
+			final Expression expression = plainSelect.getWhere();
+			//增加新的where条件
+			final Expression envCondition = CCJSqlParserUtil.parseCondExpression(condition);
+			if (expression == null) {
+				plainSelect.setWhere(envCondition);
+			} else {
+				AndExpression andExpression = new AndExpression(expression, envCondition);
+				plainSelect.setWhere(andExpression);
+			}
+			return plainSelect.toString();
+		} catch (JSQLParserException e) {
+			log.error("解析原SQL并构建新SQL错误:" + e);
+			return sql;
+		}
+	}
+
+	@Override
+	public Object plugin(Object target) {
+		return Interceptor.super.plugin(target);
+	}
+
+	@Override
+	public void setProperties(Properties properties) {
+		Interceptor.super.setProperties(properties);
+	}
+
+	@Cacheable(value = "user_code")
+	private MyuserResDTO getSysUser(String code) {
+		Object json = serviceBuilder.getGatewayUrl().getSysUser(code);
+		MyuserResDTO user = JSON.parseObject(json.toString(), MyuserResDTO.class);
+		return user;
+	}
+
+	private DataDictionary getSysDept(MyuserResDTO user){
+		if (user != null && CacheContext.ddSuperKeyMap.containsKey(DEPT_MARK)){
+			Optional<DataDictionary> any = CacheContext.ddSuperKeyMap.get(DEPT_MARK).stream().filter(t -> t.getKeyName().equals(user.getDeptName())).findAny();
+			return any.isPresent() ?  any.get() : null;
+		}
+		return null;
+	}
+}

+ 7 - 0
ims-service/ims-eval/src/main/java/com/ims/eval/dao/IndicatorMapper.java

@@ -3,6 +3,7 @@ package com.ims.eval.dao;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ims.eval.config.permission.DataPermission;
 import com.ims.eval.entity.Indicator;
 import com.ims.eval.entity.dto.request.IndicatorDTO;
 import com.ims.eval.entity.dto.response.IndicatorResDTO;
@@ -18,10 +19,12 @@ import java.util.List;
  * @author wang
  * @since 2023-02-26
  */
+@DataPermission
 public interface IndicatorMapper extends BaseMapper<Indicator> {
 
     Indicator selectByCode(@Param("code") String code);
 
+
 	IPage<Indicator> seleclistPage( Page page,@Param("id") String id,
 							@Param("indicatorName") String indicatorName,
 							@Param("indicatorCode")  String indicatorCode,
@@ -30,6 +33,7 @@ public interface IndicatorMapper extends BaseMapper<Indicator> {
 							@Param("dept") String dept,
 							@Param("company") String company);
 
+
 	List<Indicator> seleclistAll(@Param("id") String id,
 									@Param("indicatorName") String indicatorName,
 									@Param("indicatorCode")  String indicatorCode,
@@ -39,8 +43,11 @@ public interface IndicatorMapper extends BaseMapper<Indicator> {
 									@Param("company") String company);
 
 
+
 	List<IndicatorResDTO> selectListByIds(@Param("indicatorIds")List<String> indicatorIds);
 
+
+	@DataPermission(isPermission = false)
 	IndicatorDTO selectById(@Param("id") String id);
 
 

+ 6 - 0
ims-service/ims-eval/src/main/java/com/ims/eval/entity/DataDictionary.java

@@ -1,5 +1,6 @@
 package com.ims.eval.entity;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.extension.activerecord.Model;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
@@ -77,5 +78,10 @@ public class DataDictionary extends Model {
 	 */
 	private Integer orderNum;
 
+	/**
+	 * 数据权限
+	 */
+	@TableField(exist = true)
+	private Integer scope;
 
 }

+ 1 - 1
pom.xml

@@ -217,7 +217,7 @@
         <repository>
             <id>localrepo</id>
             <name>Local Repository</name>
-            <url>file://E:\资料\信控开发平台\maven</url>
+            <url>file://D:\repositories\eval\maven_pakage\maven</url>
         </repository>
         <repository>
             <id>aliyun-repos</id>