Mybatis-plus 添加拦截器,实现简单数据权限
系统需要根据用户所属的公司,来做一下数据权限控制。
具体一点,就是通过表中的 company_id
进行权限控制。
项目使用的是 mybatis-plus
,所以通过添加拦截器的方式,修改查询 sql,实现数据权限。
1 配置文件中的配置
# 数据权限配置
data-permission:
# 不再数据权限的表,目前主要是公司信息表,和一些关联表
not-control-tables: role_menu,user_company,user_role
# 权限控制表,即基于哪个表的数据来做权限区分,目前是公司信息表
base-table: company_info
# 特殊的uri,不进行数据权限控制
not-control-uri: /checkCompany-post
2 在权限处理时,将请求 uri 放入到内存中
/**
* 自定义权限处理
*/
@Component
@Slf4j
public class CustomAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {
@Override
public AuthorizationDecision check(
Supplier<Authentication> authentication,
RequestAuthorizationContext requestAuthorizationContext
) {
// ……
HttpServletRequest request = requestAuthorizationContext.getRequest();
String method = request.getMethod();
String path = request.getRequestURI();
// 将当前的请求的信息,放入到user中,用户后面的数据权限
LoginUser loginUser = (LoginUser) authentication.get().getPrincipal();
loginUser.setUri(path + "-" + method.toLowerCase());
// ……
}
}
关于 AuthorizationManager
请参见: www.51cto.com/article/700…
另外,用户在的登录系统之后,有一个选择公司的动作,这时将用户选择的公司信息放入缓存中:
// ……
// 缓存用户选择的公司
RBucket<String> bucket = redissonClient.getBucket(OPERATION_COMPANY + loginUserId);
bucket.set(companyId, Duration.ofHours(2));
// ……
3 拦截器中的配置
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
/**
* 数据权限控制
*/
@Data
@Component
@Slf4j
public class DataPermissionInterceptor implements InnerInterceptor {
@Autowired
private RedissonClient redissonClient;
@Value("${data-permission.not-control-tables}")
public String notControlTables;
@Value("${data-permission.base-table}")
public String baseTable;
@Value("${data-permission.not-control-uri}")
public String notControlUri;
@Override
public boolean willDoQuery(
Executor executor,
MappedStatement ms,
Object parameter,
RowBounds rowBounds,
ResultHandler resultHandler,
BoundSql boundSql
) throws SQLException {
return InnerInterceptor.super.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
}
@Override
public void beforeQuery(
Executor executor,
MappedStatement ms,
Object parameter,
RowBounds rowBounds,
ResultHandler resultHandler,
BoundSql boundSql
) throws SQLException {
log.debug("数据权限处理……");
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (ObjectUtil.isNull(authentication)) {
log.debug("数据权限处理, 未登录!");
return;
}
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
LoginUser loginUser = (LoginUser) principal;
String username = loginUser.getUsername();
// 如果是系统管理员,不需做作权限处理
if (SYSTEM_ADMINISTRATOR_ACCOUNT.equals(username)) {
log.debug("数据权限处理,当前为管理员,不需要处理数据权限。");
return;
}
String uri = loginUser.getUri();
log.debug("数据权限处理,当前uri为:{}", uri);
if (notControlUri.contains(uri)) {
log.debug("数据权限处理,当前uri,不需要处理数据权限。");
return;
}
String sql = boundSql.getSql();
Select select;
try {
select = (Select) CCJSqlParserUtil.parse(sql);
} catch (JSQLParserException e) {
throw new RuntimeException(e);
}
// 系统自动生成的sql,一般都是单表查询,所以这里暂时不考虑复杂的情况
PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
net.sf.jsqlparser.schema.Table table = (net.sf.jsqlparser.schema.Table) plainSelect.getFromItem();
String tableName = table.getName();
// 排除一些不需要控制的表
List<String> notControlTablesList = Arrays.asList(notControlTables.split(","));
if (notControlTablesList.contains(tableName.toLowerCase())) {
log.debug("数据权限处理,当前表不做权限控制,table is {}", tableName);
return;
}
String userId = loginUser.getUser().getPkId();
RBucket<String> bucket = redissonClient.getBucket(OPERATION_COMPANY + userId);
String companyId = bucket.get();
if (StrUtil.isBlank(companyId)) {
throw new BaseException("公司id不存在!");
}
// 处理SQL语句
// 基础表,根据主键进行控制
log.debug("数据权限处理,处理之前的sql为: {}", sql);
Expression where = plainSelect.getWhere();
Expression envCondition;
try {
if (baseTable.equals(tableName.toLowerCase())) {
envCondition = CCJSqlParserUtil.parseCondExpression("PK_ID = " + companyId);
} else {
envCondition = CCJSqlParserUtil.parseCondExpression("COMPANY_ID = " + companyId);
}
} catch (JSQLParserException e) {
throw new RuntimeException(e);
}
if (where == null) {
plainSelect.setWhere(envCondition);
} else {
AndExpression andExpression = new AndExpression(where, envCondition);
plainSelect.setWhere(andExpression);
}
sql = plainSelect.toString();
log.debug("数据权限处理,处理之后的sql为: {}", sql);
PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
mpBs.sql(sql);
}
}
4 启用插件
@Configuration
@MapperScan("xxx.xxx.xx.mapper")
public class MybatisPlusConfig {
@Autowired
private DataPermissionInterceptor dataPermissionInterceptor;
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/ @Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(dataPermissionInterceptor);
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
转载自:https://juejin.cn/post/7272558903102734372