Home > Java > javaTutorial > How to use Springboot+mybatis-plus+annotations to achieve data permission isolation

How to use Springboot+mybatis-plus+annotations to achieve data permission isolation

王林
Release: 2023-05-14 23:52:10
forward
2439 people have browsed it

1. Create annotation

When this annotation is placed on a class, there is no need to pass parameters. All query interfaces under the class enable data isolation; when placed on a method, data isolation is enabled by default and parameters are passed. If false, the method turns off verification

/**
 * 数据权限验证注解
 * @author xiaohua
 * @date 2021/6/23
 */
@Documented
@Target({METHOD, ANNOTATION_TYPE, TYPE})
@Retention(RUNTIME)
public @interface DataPermission {
    /**
     * 是否要进行数据权限隔离
     */
    boolean isPermi() default true;
}
Copy after login

2. Specific implementation

@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);

    @Autowired
    private 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<String> classNames;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        try {
            LoginInfo user = tokenService.getLoginInfo();
            if (user == null){
                return invocation.proceed();
            }

            List<Long> deptIds = (List<Long>) Convert.toList(user.getDataScope());
            if (deptIds == null){
                deptIds = new ArrayList<>();
            }
            //反射扫包会比较慢,这里做了个懒加载
            if (classNames == null) {
                synchronized (LazyInit.class){
                    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());
            //是否开启数据权限
            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语句,只处理查询sql
                if (mappedStatement.getSqlCommandType().toString().equals("SELECT")) {
    //                    String newSql = getNewSql(sql,deptIds,user.getUserId());
                    sql = getSql(sql,deptIds,user.getUserId());
                }
                // 修改sql
                metaObject.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<Long> 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;
        }
    }
Copy after login

The above is the detailed content of How to use Springboot+mybatis-plus+annotations to achieve data permission isolation. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:yisu.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template