好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Mybatis拦截器实现数据权限的示例代码

在我们日常开发过程中,通常会涉及到数据权限问题,下面以我们常见的一种场景举例:

一个公司有很多部门,每个人所处的部门和角色也不同,所以数据权限也可能不同,比如超级管理员可以查看某张

表的素有信息,部门领导可以查看该部门下的相关信息,部门普通人员只可以查看个人相关信息,而且由于角色的

不同,各个角色所能查看到的数据库字段也可能不相同,那么此处就涉及到了数据权限相关的问题。那么我们该如

何处理数据权限相关的问题呢?我们提供一种通过Mybatis拦截器实现的方式,下面我们来具体实现一下

pom.xml依赖

?

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

< parent >

     < groupId >org.springframework.boot</ groupId >

     < artifactId >spring-boot-starter-parent</ artifactId >

     < version >2.1.13.RELEASE</ version >

</ parent >

 

< properties >

     < java.version >1.8</ java.version >

     < mybatis-plus.version >3.2.0</ mybatis-plus.version >

</ properties >

 

< dependencies >

     < dependency >

         < groupId >org.springframework.boot</ groupId >

         < artifactId >spring-boot-starter-web</ artifactId >

     </ dependency >

     < dependency >

         < groupId >mysql</ groupId >

         < artifactId >mysql-connector-java</ artifactId >

     </ dependency >

     < dependency >

         < groupId >com.baomidou</ groupId >

         < artifactId >mybatis-plus-boot-starter</ artifactId >

         < version >${mybatis-plus.version}</ version >

     </ dependency >

     < dependency >

         < groupId >org.projectlombok</ groupId >

         < artifactId >lombok</ artifactId >

     </ dependency >

</ dependencies >

application.yml文件

?

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

server:

   port: 80

 

spring:

   application:

     name: data-scope

   datasource:

     url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC

     username: root

     password: 123456

     druid:

       # 验证连接是否有效。此参数必须设置为非空字符串,下面三项设置成true才能生效

       validation-query: SELECT 1

       # 连接是否被空闲连接回收器(如果有)进行检验. 如果检测失败, 则连接将被从池中去除

       test-while-idle: true

       # 是否在从池中取出连接前进行检验, 如果检验失败, 则从池中去除连接并尝试取出另一个

       test-on-borrow: true

       # 是否在归还到池中前进行检验

       test-on-return: false

       # 连接在池中最小生存的时间,单位是毫秒

       min-evictable-idle-time-millis: 30000

 

#mybatis配置

mybatis-plus:

   type-aliases-package: com.mk.entity

   mapper-locations: classpath:mapper/**/*.xml

   global-config:

     db-config:

       id-type: auto

       field-strategy: not_empty

       logic-delete-value: 1

       logic-not-delete-value: 0

   configuration:

     map-underscore-to-camel-case: true

     cache-enabled: false

     call-setters-on-nulls: true

     log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

代码实现

DataScode.java

?

1

2

3

4

5

6

7

8

9

10

@Data

public class DataScope {

 

     // sql过滤条件

     String sqlCondition;

 

     // 需要过滤的结果字段

     String[] filterFields;

 

}

MybatisPlusConfig.java

?

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

@Configuration

public class MybatisPlusConfig {

 

     @Bean

     @ConditionalOnMissingBean

     public DataScopeInterceptor dataScopeInterceptor() {

         return new DataScopeInterceptor();

     }

 

     @Bean

     public PaginationInterceptor paginationInterceptor() {

         PaginationInterceptor page = new PaginationInterceptor();

         page.setDialectType(DbType.MYSQL.getDb());

         return page;

     }

 

     @Bean

     public ConfigurationCustomizer mybatisConfigurationCustomizer(){

         return new ConfigurationCustomizer() {

             @Override

             public void customize(MybatisConfiguration configuration) {

                 configuration.setObjectWrapperFactory( new MybatisMapWrapperFactory());

             }

         };

     }

}

DataScopeInterceptor.java

?

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

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

@Slf4j

@Intercepts ({ @Signature (type = Executor. class , method = "query" ,

         args = {MappedStatement. class , Object. class , RowBounds. class ,ResultHandler. class })})

public class DataScopeInterceptor implements Interceptor {

 

     @Override

     public Object intercept(Invocation invocation) throws Throwable {

 

         log.info( "执行intercept方法:{}" , invocation.toString());

 

         Object[] args = invocation.getArgs();

         MappedStatement ms = (MappedStatement) args[ 0 ];

         Object parameterObject = args[ 1 ];

 

         // 查找参数中包含DataScope类型的参数

         DataScope dataScope = findDataScopeObject(parameterObject);

         if (dataScope == null ) {

             return invocation.proceed();

         }

 

 

         if (!ObjectUtils.isEmpty(dataScope.getSqlCondition())) {

             // id为执行的mapper方法的全路径名,如com.mapper.UserMapper

             String id = ms.getId();

 

             // sql语句类型 select、delete、insert、update

             String sqlCommandType = ms.getSqlCommandType().toString();

 

             // 仅拦截 select 查询

             if (!sqlCommandType.equals(SqlCommandType.SELECT.toString())) {

                 return invocation.proceed();

             }

 

             BoundSql boundSql = ms.getBoundSql(parameterObject);

             String origSql = boundSql.getSql();

             log.info( "原始SQL: {}" , origSql);

 

             // 组装新的 sql

             String newSql = String.format( "%s%s%s%s" , "select * from (" , origSql, ") " , dataScope.getSqlCondition());

 

             // 重新new一个查询语句对象

             BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), newSql,

                     boundSql.getParameterMappings(), boundSql.getParameterObject());

 

             // 把新的查询放到statement里

             MappedStatement newMs = newMappedStatement(ms, new BoundSqlSqlSource(newBoundSql));

             for (ParameterMapping mapping : boundSql.getParameterMappings()) {

                 String prop = mapping.getProperty();

                 if (boundSql.hasAdditionalParameter(prop)) {

                     newBoundSql.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop));

                 }

             }

 

             Object[] queryArgs = invocation.getArgs();

             queryArgs[ 0 ] = newMs;

 

             log.info( "改写的SQL: {}" , newSql);

         }

 

         Object result = invocation.proceed();

 

         return handleReslut(result, Arrays.asList(dataScope.getFilterFields()));

     }

 

     /**

      * 定义一个内部辅助类,作用是包装 SQL

      */

     class BoundSqlSqlSource implements SqlSource {

         private BoundSql boundSql;

         public BoundSqlSqlSource(BoundSql boundSql) {

             this .boundSql = boundSql;

         }

         public BoundSql getBoundSql(Object parameterObject) {

             return boundSql;

         }

 

     }

 

     private MappedStatement newMappedStatement (MappedStatement ms, SqlSource newSqlSource) {

         MappedStatement.Builder builder = new

                 MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());

         builder.resource(ms.getResource());

         builder.fetchSize(ms.getFetchSize());

         builder.statementType(ms.getStatementType());

         builder.keyGenerator(ms.getKeyGenerator());

         if (ms.getKeyProperties() != null && ms.getKeyProperties().length > 0 ) {

             builder.keyProperty(ms.getKeyProperties()[ 0 ]);

         }

         builder.timeout(ms.getTimeout());

         builder.parameterMap(ms.getParameterMap());

         builder.resultMaps(ms.getResultMaps());

         builder.resultSetType(ms.getResultSetType());

         builder.cache(ms.getCache());

         builder.flushCacheRequired(ms.isFlushCacheRequired());

         builder.useCache(ms.isUseCache());

         return builder.build();

     }

 

     @Override

     public Object plugin(Object target) {

         log.info( "plugin方法:{}" , target);

 

         if (target instanceof Executor) {

             return Plugin.wrap(target, this );

         }

         return target;

 

     }

 

     @Override

     public void setProperties(Properties properties) {

         // 获取属性

         // String value1 = properties.getProperty("prop1");

         log.info( "properties方法:{}" , properties.toString());

     }

 

 

 

     /**

      * 查找参数是否包括DataScope对象

      *

      * @param parameterObj 参数列表

      * @return DataScope

      */

     private DataScope findDataScopeObject(Object parameterObj) {

         if (parameterObj instanceof DataScope) {

             return (DataScope) parameterObj;

         } else if (parameterObj instanceof Map) {

             for (Object val : ((Map<?, ?>) parameterObj).values()) {

                 if (val instanceof DataScope) {

                     return (DataScope) val;

                 }

             }

         }

         return null ;

     }

 

 

     public Object handleReslut(Object returnValue, List<String> filterFields){

         if (returnValue != null && !ObjectUtils.isEmpty(filterFields)){

             if (returnValue instanceof ArrayList<?>){

                 List<?> list = (ArrayList<?>) returnValue;

                 List<Object> newList  = new ArrayList<Object>();

                 if ( 1 <= list.size()) {

                     for (Object object:list){

                         if (object instanceof Map) {

                             Map map = (Map) object;

                             for (String key : filterFields) {

                                 map.remove(key);

                             }

                             newList.add(map);

                         } else {

                             newList.add(decrypt(filterFields, object));

                         }

                     }

                     returnValue = newList;

                 }

             } else {

                 if (returnValue instanceof Map) {

                     Map map = (Map) returnValue;

                     for (String key : filterFields) {

                         map.remove(key);

                     }

                 } else {

                     returnValue = decrypt(filterFields, returnValue);

                 }

             }

         }

         return returnValue;

     }

 

 

     public static <T> T decrypt(List<String> filterFields, T t) {

         Field[] declaredFields = t.getClass().getDeclaredFields();

         try {

             if (declaredFields != null && declaredFields.length > 0 ) {

                 for (Field field : declaredFields) {

                     if (filterFields.contains(field.getName())) {

                         field.setAccessible( true );

                         field.set(t, null );

                         field.setAccessible( false );

                     }

                 }

             }

         } catch (IllegalAccessException e) {

             throw new RuntimeException(e);

         }

 

         return t;

     }

}

SalariesMapper.xml

?

1

2

3

4

5

6

7

8

9

< mapper namespace = "com.mk.mapper.SalariesMapper" >

     < select id = "pageList" resultType = "com.mk.entity.Salaries" >

         SELECT * from salaries where salary between #{start} and #{end}

     </ select >

 

     < select id = "getByEmpNo" resultType = "java.util.Map" >

         select * from salaries where emp_no = #{empNo} limit 0,1

     </ select >

</ mapper >

SalariesMapper.java

?

1

2

3

4

5

6

7

@Mapper

public interface SalariesMapper extends BaseMapper<Salaries> {

 

     List<Salaries> pageList(DataScope dataScope, @Param ( "start" ) int start,  @Param ( "end" ) int end, Page<Salaries> page);

 

     Map<String, Object> getByEmpNo(DataScope dataScope, @Param ( "empNo" ) int empNo);

}

SalariesService.java

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

@Service

public class SalariesService extends ServiceImpl<SalariesMapper, Salaries> {

 

     @Autowired

     private SalariesMapper salariesMapper;

 

     public List<Salaries> getList(){

         Page<Salaries> page = new Page<>( 1 , 10 );

         DataScope dataScope = new DataScope();

         // 设置查询条件

         dataScope.setSqlCondition( "s where 1=1 and s.emp_no = '10001'" );

         // 将结果集过滤掉salary和toDate字段

         dataScope.setFilterFields( new String[]{ "salary" , "toDate" });

         return salariesMapper.pageList(dataScope, 60000 , 70000 , page);

 

     }

 

     public Map<String, Object> getByEmpNo() {

         DataScope dataScope = new DataScope();

         // 将结果集过滤掉salary和toDate字段

         dataScope.setFilterFields( new String[]{ "salary" , "toDate" });

         return salariesMapper.getByEmpNo(dataScope, 10001 );

     }

}

启动服务,执行相关操作,sql在执行之前会执行DataScopeInterceptor拦截器中的逻辑,从而改变sql,具体的

相关操作就是将原来的sql语句origSql在外层包装一层过滤条件,如:select * from (origSql) 过滤条件,

此处的过滤条件要封装到DataScope对象中,例如:dataScope.setSqlCondition("s where 1=1 and

s.emp_no = '10001'") , 那么在经过拦截器处理以后要执行的sql语句为

select * from (origSql) s where 1=1 and s.emp_no = '10001', 从而实现数据权限相操作,当然此处的过滤条件只是为了演示效果举的一个例子

而已,在实际开发过程中要根据用户角色等等设置具体的过滤条件。

到此这篇关于Mybatis拦截器实现数据权限的示例代码的文章就介绍到这了,更多相关Mybatis拦截器实现数据权限内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

原文链接:https://blog.csdn.net/chaojunma/article/details/123400521

查看更多关于Mybatis拦截器实现数据权限的示例代码的详细内容...

  阅读:22次