Springboot+mybatis-plus+注解实现数据权限隔离

目录

  • 1.创建注解
  • 2. 具体实现

1.创建注解
当此注解打在类上,不需要传参,该类下所有查询接口开启数据隔离;打在方法上默认开启数据隔离,传参为false则该方法关闭验证
/** * 数据权限验证注解 * @author xiaohua * @date 2021/6/23 */@Documented@Target({METHOD, ANNOTATION_TYPE, TYPE})@Retention(RUNTIME)public @interface DataPermission {/*** 是否要进行数据权限隔离*/boolean isPermi() default true; }


2. 具体实现
@Component@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})public class DataPermissionInterceptor implements Interceptor {private static final Logger logger = LoggerFactory.getLogger(DataPermissionInterceptor.class); @Autowiredprivate TokenService tokenService; //扫描的包路径(根据自己的项目路径来),这里是取的配置里的包路径@Value("${permission.package-path}")private String packagePath; private final static String DEPT_ID = "dept_id"; private final static String USER_ID = "create_user"; private static List classNames; @Overridepublic Object intercept(Invocation invocation) throws Throwable {try {LoginInfo user = tokenService.getLoginInfo(); if (user == null){return invocation.proceed(); }List deptIds = (List) Convert.toList(user.getDataScope()); if (deptIds == null){deptIds = new ArrayList<>(); }//反射扫包会比较慢,这里做了个懒加载if (classNames == null) {synchronized (LazyInit.class){if (classNames == null){//扫描指定包路径下所有包含指定注解的类Set> 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()); //是否开启数据权限boolean isPermi = true; Class clazz = Class.forName(newId); //遍历方法for (Method method : clazz.getDeclaredMethods()) {//方法是否含有DataPermission注解,如果含有注解则将数据结果过滤if (method.isAnnotationPresent(DataPermission.class) && newName.equals(method.getName())) {DataPermission dataPermission =method.getAnnotation(DataPermission.class); if (dataPermission != null) {//不验证if (!dataPermission.isPermi()) {isPermi = false; } else { //开启验证isPermi = true; }}}}if (isPermi){// 获取到原始sql语句String sql = statementHandler.getBoundSql().getSql(); // 解析并返回新的SQL语句,只处理查询sqlif (mappedStatement.getSqlCommandType().toString().equals("SELECT")) {//String newSql = getNewSql(sql,deptIds,user.getUserId()); sql = getSql(sql,deptIds,user.getUserId()); }// 修改sqlmetaObject.setValue("delegate.boundSql.sql", sql); }return invocation.proceed(); } catch (Exception e){logger.error("数据权限隔离异常:", e); return invocation.proceed(); }}/*** 解析SQL语句,并返回新的SQL语句* 注意,该方法使用了JSqlParser来操作SQL,该依赖包Mybatis-plus已经集成了。如果要单独使用,请先自行导入依赖** @param sql 原SQL* @return 新SQL*/private String getSql(String sql,List deptIds,Long userId) {try {String condition = ""; String permissionSql = "("; if (deptIds.size() > 0){for (Long deptId : deptIds) {if ("(".equals(permissionSql)){permissionSql = permissionSql + deptId; } else {permissionSql = permissionSql + "," + deptId; }}permissionSql = permissionSql + ")"; // 修改原语句condition = DEPT_ID +" in " + permissionSql; } else {condition = USER_ID +" = " + userId; }if (StringUtils.isBlank(condition)){return sql; }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) {logger.error("解析原SQL并构建新SQL错误:" + e); return sql; }}

【Springboot+mybatis-plus+注解实现数据权限隔离】到此这篇关于Springboot+mybatis-plus+注解实现数据权限隔离的文章就介绍到这了,更多相关Springboot+mybatis-plus+注解实现数据权限隔离内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    推荐阅读