服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|JavaScript|易语言|R语言|

服务器之家 - 编程语言 - Java教程 - Springboot+mybatis-plus+注解实现数据权限隔离

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

2021-09-27 10:07我与黎明道晚安 Java教程

本文将结合实例代码,介绍Springboot+mybatis-plus+注解实现数据权限隔离,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧

1.创建注解

当此注解打在类上,不需要传参,该类下所有查询接口开启数据隔离;打在方法上默认开启数据隔离,传参为false则该方法关闭验证

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
 * 数据权限验证注解
 * @author xiaohua
 * @date 2021/6/23
 */
@Documented
@Target({METHOD, ANNOTATION_TYPE, TYPE})
@Retention(RUNTIME)
public @interface DataPermission {
    /**
     * 是否要进行数据权限隔离
     */
    boolean isPermi() default true;
}

2. 具体实现

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
@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;
        }
    }

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

原文链接:https://juejin.cn/post/6981280887216275492

延伸 · 阅读

精彩推荐
  • Java教程Spring Cloud Gateway入门解读

    Spring Cloud Gateway入门解读

    本篇文章主要介绍了Spring Cloud Gateway入门解读,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    Aoho''s Blog12072021-04-24
  • Java教程在java List中进行模糊查询的实现方法

    在java List中进行模糊查询的实现方法

    下面小编就为大家带来一篇在java List中进行模糊查询的实现方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    jingxian9042020-07-04
  • Java教程Java中的默认接口方法和静态接口方法

    Java中的默认接口方法和静态接口方法

    通常基于抽象的概念设计中,抽象往往有多个实现,如果没有多个实现也不太必要去抽象。而且一个接口的实现必须实现该接口所有的抽象方法,这并不是...

    码农小胖哥7152021-09-14
  • Java教程详解SpringBoot restful api的单元测试

    详解SpringBoot restful api的单元测试

    本篇文章主要介绍了详解SpringBoot restful api的单元测试,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    Meet相识_bfa59592020-12-31
  • Java教程Java instanceof用法详解及实例代码

    Java instanceof用法详解及实例代码

    这篇文章主要介绍了Java instanceof用法详解及实例代码的相关资料,需要的朋友可以参考下...

    Java教程网2382020-08-20
  • Java教程Java定时清理过期文件的实例代码

    Java定时清理过期文件的实例代码

    这篇文章主要介绍了Java定时清理过期文件的实例代码,非常不错,具有一定的参考借鉴价值 ,需要的朋友可以参考下...

    二十六度半6842021-06-19
  • Java教程深入理解spring事务

    深入理解spring事务

    这篇文章主要介绍了spring事务深入理解,介绍了事物特性,事物隔离级别,事物的具体使用实例,有感兴趣的同学可以研究下...

    徘徊在深夜中的猫5782021-08-17
  • Java教程Java 线程池原理深入分析

    Java 线程池原理深入分析

    这篇文章主要介绍了Java 线程池原理深入分析的相关资料,需要的朋友可以参考下...

    Java之家1702020-08-14