spring-boot集成mybatis-plus实现数据权限控制
spring-boot
spring-projects/spring-boot: 是一个用于简化Spring应用开发的框架。适合用于需要快速开发企业级Java应用的项目。特点是可以提供自动配置、独立运行和内置的Tomcat服务器,简化Spring应用的构建和部署。
项目地址:https://gitcode.com/gh_mirrors/sp/spring-boot

·
本文章记录使用spring-boot结合mybatis实现简单的数据权限控制,前端逻辑不做概述,主要实现逻辑是使用mybatis自带的方法获取执行的SQL,然后根据存储的数据权限方案拼接where条件,最后拼接到待执行的SQL后面实现数据权限过滤。
本文章涉及知识:spring注解、AOP、mybatis-plus
- 项目结构
依赖
<!-- mybatis-plus begin -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
- 配置
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package eimsp.database.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.SpringBootVFS;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.extension.incrementer.H2KeyGenerator;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import eimsp.config.ConfigValueUtil;
import eimsp.database.data.DataSourceContextHolder;
import eimsp.util.DataSourceUtil;
import eimsp.util.DbConUtil;
import eimsp.util.DbTableConModel;
import eimsp.util.JdbcUtil;
import java.io.IOException;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import javax.sql.DataSource;
import org.apache.ibatis.logging.slf4j.Slf4jImpl;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.JdbcType;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
@Configuration
@ComponentScan(
basePackages = {"eimsp"}
)
@MapperScan(
basePackages = {"eimsp.*.mapper"},
sqlSessionTemplateRef = "sqlSessionTemplate"
)
public class MybatisPlusConfig {
static final String ALIASES_PACKAGE = "eimsp.*.entity";
@Autowired
private DataSourceUtil dataSourceUtil;
@Autowired
private ConfigValueUtil configValueUtil;
public MybatisPlusConfig() {
}
@Primary
@Bean(
name = {"dataSourceSystem"}
)
public DataSource dataSourceOne(Environment env) {
String prefix = "spring.datasource.druid.";
return this.druidDataSource(env, prefix);
}
//数据拦截代码
@Bean(
name = {"sqlSessionFactorySystem"}
)
public SqlSessionFactory sqlSessionFactoryOne(@Qualifier("dataSourceSystem") DataSource dataSource, Interceptor[] interceptors) throws Exception {
//创建拦截代码
return this.createSqlSessionFactory(dataSource, interceptors);
}
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
if (Boolean.parseBoolean(this.configValueUtil.getMultiTenancy())) {
DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
HashMap<String, TableNameHandler> map = new HashMap(150);
String url = this.dataSourceUtil.getUrl().replace("{dbName}", this.dataSourceUtil.getDbInit());
Connection conn = JdbcUtil.getConn(this.dataSourceUtil.getUserName(), this.dataSourceUtil.getPassword(), url);
List<DbTableConModel> dbTableModels = new ArrayList();
if (conn != null) {
if (this.dataSourceUtil.getDataType().toLowerCase().equals(DbType.MYSQL.getDb())) {
dbTableModels = DbConUtil.mysqlgetList(conn, this.dataSourceUtil.getDbInit());
} else if (this.dataSourceUtil.getDataType().toLowerCase().equals(DbType.SQL_SERVER.getDb())) {
dbTableModels = DbConUtil.sqlServergetList(conn, this.dataSourceUtil.getDbInit());
} else if (this.dataSourceUtil.getDataType().toLowerCase().equals(DbType.ORACLE.getDb())) {
dbTableModels = DbConUtil.orcalgetList(conn, this.dataSourceUtil.getDbInit());
}
}
Iterator var7 = ((List)dbTableModels).iterator();
while(var7.hasNext()) {
DbTableConModel dbTableModel = (DbTableConModel)var7.next();
if (this.dataSourceUtil.getDataType().toLowerCase().equals(DbType.MYSQL.getDb())) {
map.put(dbTableModel.getTable(), (sql, tableName) -> {
return DataSourceContextHolder.getDatasourceName() + "." + dbTableModel.getTable();
});
} else if (this.dataSourceUtil.getDataType().toLowerCase().equals(DbType.SQL_SERVER.getDb())) {
map.put(dbTableModel.getTable(), (sql, tableName) -> {
return DataSourceContextHolder.getDatasourceName() + ".dbo." + dbTableModel.getTable();
});
} else if (this.dataSourceUtil.getDataType().toLowerCase().equals(DbType.ORACLE.getDb())) {
map.put(dbTableModel.getTable().toLowerCase(), (sql, tableName) -> {
return DataSourceContextHolder.getDatasourceName().toUpperCase() + "." + dbTableModel.getTable();
});
}
}
dynamicTableNameInnerInterceptor.setTableNameHandlerMap(map);
interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);
}
if (this.dataSourceUtil.getDataType().toLowerCase().equals(DbType.MYSQL.getDb())) {
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
} else if (this.dataSourceUtil.getDataType().toLowerCase().equals(DbType.SQL_SERVER.getDb())) {
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.SQL_SERVER));
} else if (this.dataSourceUtil.getDataType().toLowerCase().equals(DbType.ORACLE.getDb())) {
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.ORACLE));
}
return interceptor;
}
protected DataSource druidDataSource(Environment env, String prefix) {
String url;
String dbNull;
if (Boolean.parseBoolean(this.configValueUtil.getMultiTenancy())) {
dbNull = env.getProperty(prefix + "dbnull");
url = env.getProperty(prefix + "url").replace("{dbName}", dbNull);
} else {
dbNull = env.getProperty(prefix + "dbname");
url = env.getProperty(prefix + "url").replace("{dbName}", dbNull);
}
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername(env.getProperty(prefix + "username"));
dataSource.setUrl(url);
dataSource.setPassword(env.getProperty(prefix + "password"));
dataSource.setDriverClassName(env.getProperty(prefix + "driver-class-name"));
return dataSource;
}
public Resource[] resolveMapperLocations() {
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
List<String> mapperLocations = new ArrayList();
mapperLocations.add("classpath*:mapper/*/*.xml");
List<Resource> resources = new ArrayList();
if (mapperLocations != null) {
Iterator var4 = mapperLocations.iterator();
while(var4.hasNext()) {
String mapperLocation = (String)var4.next();
try {
Resource[] mappers = resourceResolver.getResources(mapperLocation);
resources.addAll(Arrays.asList(mappers));
} catch (IOException var7) {
}
}
}
return (Resource[])resources.toArray(new Resource[resources.size()]);
}
//数据拦截代码
public SqlSessionFactory createSqlSessionFactory(DataSource dataSource, Interceptor[] interceptors) throws Exception {
MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
bean.setDataSource(dataSource);
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setMetaObjectHandler(new MybatisPlusMetaObjectHandler());
bean.setGlobalConfig(globalConfig);
bean.setVfs(SpringBootVFS.class);
bean.setTypeAliasesPackage("eimsp.*.entity");
bean.setMapperLocations(this.resolveMapperLocations());
bean.setConfiguration(this.configuration(interceptors));
return bean.getObject();
}
//数据拦截代码
public MybatisConfiguration configuration(Interceptor[] interceptors) {
MybatisConfiguration mybatisConfiguration = new MybatisConfiguration();
mybatisConfiguration.setMapUnderscoreToCamelCase(true);
mybatisConfiguration.setCacheEnabled(false);
for(int i = interceptors.length - 1; i >= 0; --i) {
Interceptor item = interceptors[i];
mybatisConfiguration.addInterceptor(item);
}
mybatisConfiguration.setLogImpl(Slf4jImpl.class);
mybatisConfiguration.setJdbcTypeForNull(JdbcType.NULL);
return mybatisConfiguration;
}
@Bean
public IKeyGenerator keyGenerator() {
return new H2KeyGenerator();
}
@Bean
public ISqlInjector sqlInjector() {
return (builderAssistant, mapperClass) -> {
};
}
}
- 数据权限注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DataPermission {
String methods() default "";
}
AOP代码
@Aspect
@Component
public class DataScopeAspect {
public DataScopeAspect() {
}
@Pointcut("@annotation(eimsp.database.annotation.DataPermission)")
public void dataPermissionPointCut() {
}
@Before("dataPermissionPointCut()")
public void doBefore(JoinPoint point) throws Throwable {
this.handleDataPermission(point);
}
protected void handleDataPermission(final JoinPoint joinPoint) {
SessionUtil sessionUtil = (SessionUtil)SpringContext.getBean(SessionUtil.class);
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature)signature;
Method method = methodSignature.getMethod();
if (method != null) {
DataPermission controllerDataScope = (DataPermission)method.getAnnotation(DataPermission.class);
sessionUtil.setDataPermissionMethods(controllerDataScope.methods());
}
}
}
拦截器逻辑实现
@Intercepts({@Signature(
type = StatementHandler.class,
method = "prepare",
args = {Connection.class, Integer.class}
)})
@Component
public class DataPermissionInterceptor extends AbstractSqlParserHandler implements Interceptor {
private static final Logger log = LoggerFactory.getLogger(DataPermissionInterceptor.class);
public Object intercept(Invocation invocation) throws Throwable {
String moduleId = ServletUtil.getHeader("ModuleId");
if (moduleId == null) {
return invocation.proceed();
} else {
UserProvider userProvider = (UserProvider)SpringContext.getBean(UserProvider.class);
UserInfo userInfo = userProvider.get();
if (userInfo.getIsAdministrator()) {
return invocation.proceed();
} else {
StatementHandler statementHandler = (StatementHandler)PluginUtils.realTarget(invocation.getTarget());
MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
this.sqlParser(metaObject);
MappedStatement mappedStatement = (MappedStatement)metaObject.getValue("delegate.mappedStatement");
if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
return invocation.proceed();
} else {
SessionUtil sessionUtil = (SessionUtil)SpringContext.getBean(SessionUtil.class);
String dataPermissionMethods = sessionUtil.getDataPermissionMethods();
List<String> methods = Lists.newArrayList(dataPermissionMethods.split(","));
String methodName = mappedStatement.getId();
if (Strings.isNullOrEmpty(dataPermissionMethods)) {
return invocation.proceed();
} else if (methods.stream().anyMatch((it) -> {
return methodName.contains(it);
})) {
BoundSql boundSql = (BoundSql)metaObject.getValue("delegate.boundSql");
String originalSql = boundSql.getSql();
Object parameterObject = boundSql.getParameterObject();
AuthorizeService authorizeService = (AuthorizeService)SpringContext.getBean(AuthorizeService.class);
String whereSql = authorizeService.GetConditionSql(userInfo, moduleId);
if (StringUtil.isEmpty(whereSql)) {
whereSql = "1 = 2";
}
Select select = (Select)CCJSqlParserUtil.parse(originalSql);
Expression sourceWhere = ((PlainSelect)select.getSelectBody()).getWhere();
whereSql = sourceWhere != null ? sourceWhere.toString() + whereSql : whereSql.replaceFirst("AND|OR", "");
Expression where = CCJSqlParserUtil.parseCondExpression(whereSql);
((PlainSelect)select.getSelectBody()).setWhere(where);
originalSql = select.toString();
metaObject.setValue("delegate.boundSql.sql", originalSql);
return invocation.proceed();
} else {
return invocation.proceed();
}
}
}
}
}
public Object plugin(Object target) {
return target instanceof StatementHandler ? Plugin.wrap(target, this) : target;
}
public void setProperties(Properties properties) {
}
public DataPermissionInterceptor() {
}
}




spring-projects/spring-boot: 是一个用于简化Spring应用开发的框架。适合用于需要快速开发企业级Java应用的项目。特点是可以提供自动配置、独立运行和内置的Tomcat服务器,简化Spring应用的构建和部署。
最近提交(Master分支:23 天前 )
e2ba4dad - 23 小时前
7a595f1b
Closes gh-47333
1 天前
更多推荐
所有评论(0)