mybatis-plus统一处理数据权限
问题:项目要求数据权限配置查询,全部、自定义、部门、部门及下级部门、个人。要求做统一处理。分析:数据权限精确到个人。那么每张表里面都要有创建人字段。每次插入数据都要保存创建人。查询的时候才能区分。mybatis-plus统一处理创建时间,创建人,更新时间,更新人:统一处理创建人查询的时候每个查询加条件代码太复杂。所以sql加统一处理例:SELECT %s FROM (%s) temp_data_s
·
- 问题:项目要求数据权限配置查询,全部、自定义、部门、部门及下级部门、个人。要求做统一处理。
- 分析:
- 数据权限精确到个人。那么每张表里面都要有创建人字段。每次插入数据都要保存创建人。查询的时候才能区分。
- mybatis-plus统一处理创建时间,创建人,更新时间,更新人:统一处理创建人
- 查询的时候每个查询加条件代码太复杂。所以sql加统一处理例:
-
SELECT %s FROM (%s) temp_data_scope WHERE temp_data_scope.%s IN (%s)
- 第一个%s:要查询的字段
- 第二个%s:sql语句例:select * from user
- 第三个%s:条件字段,从 第二个%s里面挑选
- 第四个%s:查询的值
具体实现:
创建一个公共的entity实体类,所有的实体类继承这个类,当然你的表里面要有相应的字段
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 父级实体-公共属性
* @param
*/
@Data
@Accessors(chain = true)
public class OkayxBaseEntity<T extends OkayxBaseEntity<?>> extends Model{
/**
* 更新者
*/
@ApiModelProperty(value="更新者")
@TableField(fill= FieldFill.INSERT_UPDATE)
private Integer updateBy;
/**
* 创建者
*/
@ApiModelProperty(value="创建者")
@TableField(fill= FieldFill.INSERT)
private Integer createBy;
/**
* 创建时间
*/
@ApiModelProperty(value="创建时间")
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
@TableField(fill= FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 更新时间
*/
@ApiModelProperty(value="更新时间")
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
@TableField(fill= FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
/**
* 是否删除
*/
@ApiModelProperty(value="delFlag")
private String delFlag;
}
数据权限等级枚举类,枚举类一般对应权限管理,一起存在权限表。例:
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 数据权限类型
*/
@Getter
@AllArgsConstructor
public enum DataScopeTypeEnum {
/**
* 查询全部数据
*/
ALL(0, "全部"),
/**
* 自定义
*/
CUSTOM(1, "自定义"),
/**
* 本级及子级
*/
OWN_CHILD_LEVEL(2, "本级及子级"),
/**
* 本级
*/
OWN_LEVEL(3, "本级"),
/**
* 个人(自己)
*/
OWN(4,"个人");
/**
* 类型
*/
private final int type;
/**
* 描述
*/
private final String description;
}
数据权限的函数类型,(查询全部字段,查询条数),看上面的分析 ,也就是第一个%s
/**
* 数据权限函数类型
*/
@Getter
@AllArgsConstructor
public enum DataScopeFuncEnum {
/**
* 查询全部数据 SELECT * FROM (originSql) temp_data_scope WHERE temp_data_scope.dept_id IN
* (1)
*/
ALL("*", "全部"),
/**
* 查询函数COUNT SELECT COUNT(1) FROM (originSql) temp_data_scope WHERE
* temp_data_scope.dept_id IN (1)
*/
COUNT("COUNT(1)", "自定义");
/**
* 类型
*/
private final String type;
/**
* 描述
*/
private final String description;
}
数据字段查询参数:
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class DataScope extends HashMap {
/**
* 限制范围的字段名称
*/
// private String scopeName = "dept_id";
/**
* 限制范围的字段名称-到个人
*/
private String scopeName = "create_by";
/**
* 具体的数据范围-部门
*/
private List<Integer> deptIds = new ArrayList<>();
/**
* 具体的数据范围-创建人
*/
private List<Integer> createBys = new ArrayList<>();
/**
* 是否只查询本部门
*/
private Boolean isOnly = false;
/**
* 函数名称,默认 SELECT * ;
*
* <ul>
* <li>COUNT(1)</li>
* </ul>
*/
private DataScopeFuncEnum func = DataScopeFuncEnum.ALL;
}
判断处理器,抽象服务扩展。实现类处理用户权限
import java.util.List;
/**
* data scope 判断处理器,抽象服务扩展
*/
public interface DataScopeHandle {
/**
* 计算用户数据权限
* @param userList 用户ID,如果为空表示没有任何数据权限。
* @return 返回true表示无需进行数据过滤处理,返回false表示需要进行数据过滤
*/
Boolean calcScope(List<Integer> userList );
}
判断处理器实现类。处理用户id。(选择部门返回本部门所有人的id,选择个人返回个人id......)
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.okay.okayx.admin.api.entity.SysDeptRelation;
import com.okay.okayx.admin.api.entity.SysRole;
import com.okay.okayx.admin.api.entity.SysUser;
import com.okay.okayx.admin.api.feign.RemoteDataScopeService;
import com.okay.okayx.common.core.constant.SecurityConstants;
import com.okay.okayx.common.security.service.OkayxUser;
import com.okay.okayx.common.security.util.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author xuxiang
* @date 2021/0714
*/
public class OkayxUserDatascopeHandle implements DataScopeHandle {
@Autowired
private RemoteDataScopeService dataScopeService;
/**
* 计算用户数据权限
* @param userIdList
* @return
*/
@Override
public Boolean calcScope(List<Integer> userIdList) {
OkayxUser user = SecurityUtils.getUser();
List<Integer> deptIds = new ArrayList<>();
List<String> roleIdList = user.getAuthorities().stream().map(GrantedAuthority::getAuthority)
.filter(authority -> authority.startsWith(SecurityConstants.ROLE))
.map(authority -> authority.split(StrUtil.UNDERLINE)[1]).collect(Collectors.toList());
// 当前用户的角色为空
if (CollectionUtil.isEmpty(roleIdList)) {
return false;
}
SysRole role = dataScopeService.getRoleList(roleIdList).getData().stream()
.min(Comparator.comparingInt(SysRole::getDsType)).orElse(null);
// 角色有可能已经删除了
if (role == null) {
return false;
}
Integer dsType = role.getDsType();
// 查询全部
if (DataScopeTypeEnum.ALL.getType() == dsType) {
return true;
}
// 自定义
if (DataScopeTypeEnum.CUSTOM.getType() == dsType && StrUtil.isNotBlank(role.getDsScope())) {
String dsScope = role.getDsScope();
deptIds.addAll(
Arrays.stream(dsScope.split(StrUtil.COMMA)).map(Integer::parseInt).collect(Collectors.toList()));
userIdList = dataScopeService.getUserList(deptIds).getData().stream().map(SysUser::getUserId).collect(Collectors.toList());
}
// 查询本级及其下级
if (DataScopeTypeEnum.OWN_CHILD_LEVEL.getType() == dsType) {
deptIds = dataScopeService.getDescendantList(user.getDeptId()).getData().stream()
.map(SysDeptRelation::getDescendant).collect(Collectors.toList());
userIdList = dataScopeService.getUserList(deptIds).getData().stream().map(SysUser::getUserId).collect(Collectors.toList());
}
// 只查询本级
if (DataScopeTypeEnum.OWN_LEVEL.getType() == dsType) {
Integer deptId = user.getDeptId();
deptIds.add(deptId);
userIdList = dataScopeService.getUserList(deptIds).getData().stream().map(SysUser::getUserId).collect(Collectors.toList());
}
// 只查询本级
if (DataScopeTypeEnum.OWN.getType() == dsType) {
userIdList.add(user.getId());
}
return false;
}
}
数据权限拦截,sql执行前拦截
// 查找参数中包含DataScope类型的参数
DataScope dataScope = findDataScopeObject(parameterObject);
看这段代码也就是参数里面含有DataScope的都会做数据过虑
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import lombok.Setter;
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 java.util.List;
import java.util.Map;
/**
* 数据权限拦截
* @author xuxaing
* @date 2021/07/14
*/
public class DataScopeInnerInterceptor1 implements InnerInterceptor {
@Setter
private DataScopeHandle dataScopeHandle;
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds,
ResultHandler resultHandler, BoundSql boundSql) {
PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
String originalSql = boundSql.getSql();
Object parameterObject = boundSql.getParameterObject();
// 查找参数中包含DataScope类型的参数
DataScope dataScope = findDataScopeObject(parameterObject);
if (dataScope == null) {
return;
}
String scopeName = dataScope.getScopeName();
List<Integer> createBys = dataScope.getCreateBys();
// 优先获取赋值数据
if (CollUtil.isEmpty(createBys) && dataScopeHandle.calcScope(createBys)) {
originalSql = String.format("SELECT %s FROM (%s) temp_data_scope", dataScope.getFunc().getType(),
originalSql);
mpBs.sql(originalSql);
return;
}
if (createBys.isEmpty()) {
originalSql = String.format("SELECT %s FROM (%s) temp_data_scope WHERE 1 = 2",
dataScope.getFunc().getType(), originalSql);
}
else {
String join = CollectionUtil.join(createBys, ",");
originalSql = String.format("SELECT %s FROM (%s) temp_data_scope WHERE temp_data_scope.%s IN (%s)",
dataScope.getFunc().getType(), originalSql, scopeName, join);
}
mpBs.sql(originalSql);
}
/**
* 查找参数是否包括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;
}
}
mybatis配置类:
数据拦截器DataScopeInnerInterceptor1
判断处理器OkayxUserDatascopeHandle
配置到配置类
import java.util.List;
import javax.sql.DataSource;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import com.okay.okayx.common.data.datascope.*;
import com.okay.okayx.common.data.resolver.SqlFilterArgumentResolver;
import com.okay.okayx.common.data.tenant.OkayxTenantHandler;
import com.okay.okayx.common.security.service.OkayxUser;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
*/
@Configuration
@ConditionalOnBean(DataSource.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisPlusConfiguration implements WebMvcConfigurer {
/**
* 增加请求参数解析器,对请求中的参数注入SQL 检查
* @param resolverList
*/
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolverList) {
resolverList.add(new SqlFilterArgumentResolver());
}
/**
* okayx 默认数据权限处理器
* @return OkayxDefaultDatascopeHandle
*/
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(OkayxUser.class)
public DataScopeHandle dataScopeHandle() {
return new OkayxUserDatascopeHandle();
}
/**
* mybatis plus 拦截器配置
* @return OkayxDefaultDatascopeHandle
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 数据权限-新
DataScopeInnerInterceptor1 dataScopeInnerInterceptor = new DataScopeInnerInterceptor1();
dataScopeInnerInterceptor.setDataScopeHandle(dataScopeHandle());
interceptor.addInnerInterceptor(dataScopeInnerInterceptor);
// 分页支持
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
/**
* 扩展 mybatis-plus baseMapper 支持数据权限
* @return
*/
@Bean
@ConditionalOnBean(DataScopeHandle.class)
public DataScopeSqlInjector dataScopeSqlInjector() {
return new DataScopeSqlInjector();
}
}
前面过虑的事情都做写了,然后我们来处理业务。
写一个公共Mapper,所有的Mapper都实现这个Mapper
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 扩展通用 Mapper,支持数据权限 和批量插入
*
* @author okayx
* @date 2020-06-17
*/
public interface OkayxBaseMapper<T> extends BaseMapper<T> {
/**
* 根据 entity 条件,查询全部记录
* @param queryWrapper 实体对象封装操作类(可以为 null)
* @param scope 数据权限范围
* @return List<T>
*/
List<T> selectListByScope(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper, DataScope scope);
/**
* 根据 entity 条件,查询全部记录(并翻页)
* @param page 分页查询条件(可以为 RowBounds.DEFAULT)
* @param queryWrapper 实体对象封装操作类(可以为 null)
* @param scope 数据权限范围
* @return Page
*/
<E extends IPage<T>> E selectPageByScope(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper,
DataScope scope);
/**
* 根据 Wrapper 条件,查询总记录数
* @param queryWrapper 实体对象封装操作类(可以为 null)
* @param scope 数据权限范围
* @return Integer
*/
Integer selectCountByScope(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper, DataScope scope);
/**
* 批量插入 仅适用于 mysql
* @param entityList 实体列表
* @return 影响行数
*/
Integer insertBatchSomeColumn(List<T> entityList);
}
service层代码:这里只展示一个方法。跟分页查询相似,只多了一个DataScope的默认对象
@Override
public IPage<CloudManagerSjml> getSjmlRoles(Page page, QueryWrapper<CloudManagerSjml> queryWrapper) {
return cloudManagerSjmlMapper.selectPageByScope(page,queryWrapper,new DataScope());
}
重启项目,成功
更多推荐
已为社区贡献3条内容
所有评论(0)