利用mybatis 拦截器实现数据权限
查询前执行该方法即可 参数1:字段名,参数2:自定义id可传null,在拦截器更具用户查询。该方法就是为了往ThreadLocal set数据。// 使用matcher方法检查是否匹配字符串的结尾。// 将正则表达式编译成Pattern对象。5.ThreadLocal缓存类型实体类。* 正则校验以什么结尾。
0.注意事项
a.代码可能不全,大致读一下逻辑即可实现
b.重点就是拦截器sql拼接部分,其他可按不同场景个性化
c.如果拦截器不起作用可参考6服务启动时操作或参考下方配置拦截器
@Configuration
@MapperScan("com.rtzh.ejjzhgd.web.**.mapper")
@EnableConfigurationProperties(value = DataScopeProperties.class)
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
interceptor.addInnerInterceptor(new QueryParameterInnerInterceptor());
return interceptor;
}
@Bean
@ConfigurationProperties(prefix = "pagehelper")
public Properties pageHelperProperties() {
return new Properties();
}
}
1.使用方法
查询前执行该方法即可 参数1:字段名,参数2:自定义id可传null,在拦截器更具用户查询
该方法就是为了往ThreadLocal set数据
DataScopeHelper.start("office_id", filterIds);
示例:
@Override
public Object test() {
LambdaQueryWrapper<RtPro> lambdaQuery = new LambdaQueryWrapper();
lambdaQuery.ne(RtPro::getProId,"1");
lambdaQuery.last("limit 10");
DataScopeHelper.start("dept_id", new HashSet<>(Arrays.asList("id")));
return rtProMapper.selectList(lambdaQuery);
}
2.线程缓存
public class DataScopeHelper {
private static final ThreadLocal<DataScope> DATA_PERMISSION = new ThreadLocal();
public DataScopeHelper() {
}
public static void start(DataScope dataScope) {
DATA_PERMISSION.set(dataScope);
}
public static void start(String scopeName, Set<String> deptIds) {
DataScope dataScope = new DataScope();
dataScope.setScopeName(scopeName);
dataScope.setDeptIds(deptIds);
DATA_PERMISSION.set(dataScope);
}
public static void startDataScope(String scopeName, String... deptIds) {
Object idSet;
if (deptIds != null && deptIds.length > 0) {
idSet = (Set)Arrays.stream(deptIds).filter(StringUtils::isNotEmpty).collect(Collectors.toSet());
} else {
idSet = new HashSet();
}
start(scopeName, (Set)idSet);
}
public static DataScope getLocalDataPermissions() {
return (DataScope)DATA_PERMISSION.get();
}
public static void clearDataPermissions() {
DATA_PERMISSION.remove();
}
}
3.mybatis拦截器
@Intercepts({@Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
), @Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
)})
public class DataScopeInterceptor implements Interceptor {
private static final Logger log = LoggerFactory.getLogger(DataScopeInterceptor.class);
private DataScopeHandler dataScopeHandler;
private DataScopeProperties dataScopeProperties;
public Object intercept(Invocation invocation) throws Throwable {
if (!this.dataScopeProperties.getEnabled()) {
return invocation.proceed();
} else {
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement)args[0];
Object parameter = args[1];
RowBounds rowBounds = (RowBounds)args[2];
ResultHandler resultHandler = (ResultHandler)args[3];
Executor executor = (Executor)invocation.getTarget();
CacheKey cacheKey;
BoundSql boundSql;
if (args.length == 4) {
boundSql = ms.getBoundSql(parameter);
cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
} else {
cacheKey = (CacheKey)args[4];
boundSql = (BoundSql)args[5];
}
String originalSql = boundSql.getSql();
Object parameterObject = boundSql.getParameterObject();
DataScope dataScope = this.findDataScopeObject(parameterObject);
if (dataScope == null) {
dataScope = DataScopeHelper.getLocalDataPermissions();
DataScopeHelper.clearDataPermissions();
}
if (dataScope == null) {
return invocation.proceed();
} else {
UserVO userVO = UserInfoUtils.getCurrentUser();
if (this.isAdminRole(userVO.getRoleList(), this.dataScopeProperties.getAdminRoleId())) {
return invocation.proceed();
} else {
String scopeName = StringUtils.isBlank(dataScope.getScopeName()) ? this.dataScopeProperties.getScopeKey() : dataScope.getScopeName();
Set<String> deptIds = dataScope.getDeptIds();
if (CollectionUtil.isEmpty(deptIds)) {
deptIds = this.dataScopeHandler.findDeptIds(userVO);
}
if (CollectionUtil.isNotEmpty(deptIds)) {
String upperCaseSql = originalSql.toUpperCase();
boolean isLimitEnd = StringUtils.endsWithRegex(upperCaseSql, "LIMIT\\s*\\d+\\s*,\\s*\\d+\\s*$|LIMIT\\s*\\d+\\s*");
String join = "'" + CollectionUtil.join(deptIds, "','") + "'";
if(isLimitEnd){
int limit = upperCaseSql.lastIndexOf("LIMIT");
String limitSql = upperCaseSql.substring(limit, upperCaseSql.length());
String selectSql = upperCaseSql.substring(0, limit);
originalSql = "select * from (" + selectSql + ") temp_data_scope where temp_data_scope." + scopeName + " in (" + join + ") " + limitSql;
}else {
originalSql = "select * from (" + originalSql + ") temp_data_scope where temp_data_scope." + scopeName + " in (" + join + ")";
}
boundSql = new BoundSql(ms.getConfiguration(), originalSql, boundSql.getParameterMappings(), parameter);
}
return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
}
}
}
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
}
private DataScope findDataScopeObject(Object parameterObj) {
if (parameterObj instanceof DataScope) {
return (DataScope)parameterObj;
} else {
if (parameterObj instanceof Map) {
Iterator var2 = ((Map)parameterObj).values().iterator();
while(var2.hasNext()) {
Object val = var2.next();
if (val instanceof DataScope) {
return (DataScope)val;
}
}
}
return null;
}
}
private Boolean isAdminRole(List<SysRole> roleList, String adminRoleId) {
if (roleList == null) {
return Boolean.FALSE;
} else {
Iterator var3 = roleList.iterator();
SysRole sysRole;
do {
if (!var3.hasNext()) {
return Boolean.FALSE;
}
sysRole = (SysRole)var3.next();
} while(!adminRoleId.equals(sysRole.getRoleId()));
return Boolean.TRUE;
}
}
public DataScopeInterceptor(final DataScopeHandler dataScopeHandler, final DataScopeProperties dataScopeProperties) {
this.dataScopeHandler = dataScopeHandler;
this.dataScopeProperties = dataScopeProperties;
}
}
4.缓存处理类
public class DataScopeHelper {
private static final ThreadLocal<DataScope> DATA_PERMISSION = new ThreadLocal();
public DataScopeHelper() {
}
public static void start(DataScope dataScope) {
DATA_PERMISSION.set(dataScope);
}
public static void start(String scopeName, Set<String> deptIds) {
DataScope dataScope = new DataScope();
dataScope.setScopeName(scopeName);
dataScope.setDeptIds(deptIds);
DATA_PERMISSION.set(dataScope);
}
public static void startDataScope(String scopeName, String... deptIds) {
Object idSet;
if (deptIds != null && deptIds.length > 0) {
idSet = (Set)Arrays.stream(deptIds).filter(StringUtils::isNotEmpty).collect(Collectors.toSet());
} else {
idSet = new HashSet();
}
start(scopeName, (Set)idSet);
}
public static DataScope getLocalDataPermissions() {
return (DataScope)DATA_PERMISSION.get();
}
public static void clearDataPermissions() {
DATA_PERMISSION.remove();
}
}
5.ThreadLocal缓存类型实体类
public class DataScope {
//字段
private String scopeName;
//数据id
private Set<String> deptIds;
}
/**
* 正则校验以什么结尾
* @param input
* @param regex
* @return
*/
public static boolean endsWithRegex(String input, String regex) {
// 将正则表达式编译成Pattern对象
Pattern pattern = Pattern.compile(regex);
// 使用matcher方法检查是否匹配字符串的结尾
return pattern.matcher(input).find();
}
6.服务启动初始化数据-参考
a/**
* @author mm
* @Date 2024-03-24 14:33
* @Desc
*/
@Slf4j
@Configuration
@EnableConfigurationProperties(value = DataScopeProperties.class)
public class DataScopeInterceptorConfig {
@Autowired
private DataScopeProperties dataScopeProperties;
@Autowired
private List<SqlSessionFactory> sqlSessionFactoryList;
@Autowired
private CloudRedisRepository redisRepository;
@Resource(name="auSysOfficeServiceImpl")
private com.rtzh.ejjzhgd.web.au.service.SysOfficeService sysOfficeService;
//全部数据权限公司id
private static final Set<String> allDataCompanyIds = Sets.newHashSet("000");//国网根节点 最大权限 TODO 参数配置到yml中
public DataScopeInterceptorConfig() {
}
/* @Bean
@ConfigurationProperties(
prefix = "pagehelper"
)
public Properties pageHelperProperties() {
return new Properties();
}*/
/**
* 数据权限插件
* 框架过滤规则: originalSql = "select * from (" + originalSql + ") temp_data_scope where temp_data_scope." + scopeName + " in (" + join + ")";
* 业务sql执行前的配置
* DataScopeHelper.start("office_id", null); // 将会使用本【addDataFilterInterceptor的结果 (deptIds) 过滤数据】,前提 原查询字段里 有 office_id ;
*
* DataScopeHelper.start("office_id", filterIds); // 将会使用 filterIds 过滤 原数据 ,前提 原查询字段里 有 office_id ;
*
* @return DataScopeInterceptor
*/
@PostConstruct
public void addDataFilterInterceptor() {
log.error("addDataFilterInterceptor 数据权限过滤 WEB 配置启动 ");
Iterator var3 = this.sqlSessionFactoryList.iterator();
//数据权限插件
DataScopeInterceptor dataScopeInterceptor = new DataScopeInterceptor(new DataScopeHandler(redisRepository) {
@Override
public Set<String> findDeptIds(UserVO userVO) {
//TODO 以下默认过滤 根据业务实际情况编码
Set<String> deptIds = new HashSet<>();
//当前用户如果找不到对应的数据权限,则默认为所在机构及以下机构
String varCompanyId = null;
String varDeptId = null;
if (StringUtils.isNotBlank(userVO.getCompanyId())) {
varCompanyId = this.getRedisRepository().getHashValues(SecurityConstants.DATA_SCOPE_KEY, userVO.getCompanyId());
}
if (StringUtils.isNotBlank(userVO.getDeptId())) {
varDeptId = this.getRedisRepository().getHashValues(SecurityConstants.DATA_SCOPE_KEY, userVO.getDeptId());
}
if (StringUtils.isBlank(varCompanyId) && StringUtils.isBlank(varDeptId)) {
getDefaultPerm(userVO.getCompanyId(), deptIds);
} else {
if (StringUtils.isNotBlank(varCompanyId)) {
deptIds.addAll(JSONArray.parseArray(varCompanyId, String.class));
}
if (StringUtils.isNotBlank(varDeptId)) {
deptIds.addAll(JSONArray.parseArray(varDeptId, String.class));
}
}
log.warn("DataScopeInterceptorConfig : deptIds = {} ",deptIds);
return deptIds;
}
}, dataScopeProperties);
while (var3.hasNext()) {
SqlSessionFactory sqlSessionFactory = (SqlSessionFactory) var3.next();
sqlSessionFactory.getConfiguration().addInterceptor(dataScopeInterceptor);
}
}
/**
* 根据当前用户获取默认数据权限
* 目前只根据用户所在的公司来判断,不判断部门
* TODO 具体根据产品规定默认权限来进行
* @param companyId 公司id
* @param dataPerms 数据权限
*/
private void getDefaultPerm(String companyId, Set<String> dataPerms) {
//有全数据权限
if (allDataCompanyIds.contains(companyId)) {
dataPerms.clear();
return;
}
//查询下级机构
dataPerms.addAll(sysOfficeService.getChildIds(companyId));
}
}
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)