1.自定义interceptor做数据权限
- 自定义dataScope
@Data
@EqualsAndHashCode(callSuper = true)
public class DataScope extends HashMap {
/**
* 限制范围的字段名称 (除个人外)
*/
private String scopeName = "org_id";
/**
* 限制范围为个人时的字段名称
*/
private String selfScopeName = "user_id";
private String selfName = "create_by";
/**
* 当前用户ID
*/
private Integer userId;
/**
* 具体的数据范围
*/
private List<Long> orgIds;
private DataPermissionType Type;
/**
* 限制范围为插入时的类型
*/
private String dataType = "type";
}
@Getter
@AllArgsConstructor
@ApiModel(value = "DataScopeType", description = "数据权限类型-枚举")
public enum DataScopeType implements BaseEnum {
/**
* ALL=5全部
*/
ALL(5, "全部"),
/**
* THIS_LEVEL=4本级
*/
THIS_LEVEL(4, "本级"),
/**
* THIS_LEVEL_CHILDREN=3本级以及子级
*/
THIS_LEVEL_CHILDREN(3, "本级以及子级"),
/**
* CUSTOMIZE=2自定义
*/
CUSTOMIZE(2, "自定义"),
/**
* SELF=1个人
*/
SELF(1, "个人"),
;
@ApiModelProperty(value = "描述")
private final int val;
private final String desc;
public static DataScopeType match(String val, DataScopeType def) {
return Stream.of(values()).parallel().filter((item) -> item.name().equalsIgnoreCase(val)).findAny().orElse(def);
}
public static DataScopeType match(Integer val, DataScopeType def) {
return Stream.of(values()).parallel().filter((item) -> val != null && item.getVal() == val).findAny().orElse(def);
}
public static DataScopeType get(String val) {
return match(val, null);
}
public static DataScopeType get(Integer val) {
return match(val, null);
}
public boolean eq(final DataScopeType val) {
return val != null && eq(val.name());
}
@Override
@ApiModelProperty(value = "编码", allowableValues = "ALL,THIS_LEVEL,THIS_LEVEL_CHILDREN,CUSTOMIZE,SELF", example = "ALL")
public String getCode() {
return this.name();
}
public static DataScopeType getEnum(String desc) {
for (DataScopeType value : DataScopeType.values()) {
if (value.getDesc().equals(desc)) {
return value;
}
}
return null;
}
public static String getType(Integer code) {
DataScopeType[] values = DataScopeType.values();
for (DataScopeType value : values) {
if (code.equals(value.getCode())) {
return value.getDesc();
}
}
return "";
}
}
- 自定义Interceptor
@Slf4j
@AllArgsConstructor
public class DataScopeInnerInterceptor implements InnerInterceptor {
private final Function<Integer, Map<String, Object>> function;
/**
* 查找参数是否包括DataScope对象
*
* @param parameterObj 参数列表
* @return DataScope
*/
protected Optional<DataScope> findDataScope(Object parameterObj) {
if (parameterObj == null) {
return Optional.empty();
}
if (parameterObj instanceof DataScope) {
return Optional.of((DataScope) parameterObj);
} else if (parameterObj instanceof Map) {
for (Object val : ((Map<?, ?>) parameterObj).values()) {
if (val instanceof DataScope) {
return Optional.of((DataScope) val);
}
}
}
return Optional.empty();
}
/**
*其原理就是sql外包装一层查询
**/
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
DataScope dataScope = findDataScope(parameter).orElse(null);
if (dataScope == null) {
return;
}
String originalSql = boundSql.getSql();
String scopeName = dataScope.getScopeName();
String selfScopeName = dataScope.getSelfScopeName();
String typeScopeName = dataScope.getDataType();
DataPermissionType dataType = dataScope.getType();
Integer userId = dataScope.getUserId() == null ? ContextUtil.getUserId().intValue() : dataScope.getUserId();
List<Long> orgIds = dataScope.getOrgIds();
DataScopeType dsType = DataScopeType.SELF;
if (CollectionUtil.isEmpty(orgIds)) {
//查询当前用户的 角色 最小权限
//userId
//dsType orgIds
Map<String, Object> result = function.apply(userId);
if (result == null) {
return;
}
Integer type = (Integer) result.get("dsType");
dsType = DataScopeType.get(type);
orgIds = (List<Long>) result.get("orgIds");
}
//查全部
if (DataScopeType.ALL.eq(dsType)) {
return;
}
//查个人
if (DataScopeType.SELF.eq(dsType)) {
originalSql = "select * from (" + originalSql + ") temp_data_scope where temp_data_scope." + dataScope.getSelfName() + " = " + userId;
}
//查其他
else if (StrUtil.isNotBlank(scopeName) && CollUtil.isNotEmpty(orgIds)) {
String join = CollectionUtil.join(orgIds, ",");
originalSql = "select * from (" + originalSql + ") temp_data_scope where temp_data_scope." + scopeName + " in (" + join + ")";
}
PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
mpBoundSql.sql(originalSql);
}
}
- 添加自定义interceptor进mybatis
protected List<InnerInterceptor> getPaginationBeforeInnerInterceptor() {
List<InnerInterceptor> list = new ArrayList<>();
Boolean isDataScope = databaseProperties.getIsDataScope();
if (isDataScope) {
// todo UserService.class 改方法是查询当前用户的组织以及角色权限等信息
list.add(new DataScopeInnerInterceptor(userId -> userServiceApi.getDataScope(userId)));
}
return list;
}
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
List<InnerInterceptor> beforeInnerInterceptor = getPaginationBeforeInnerInterceptor();
if (!beforeInnerInterceptor.isEmpty()) {
beforeInnerInterceptor.forEach(interceptor::addInnerInterceptor);
}
- 添加interceptor进入sqlSessionFactory
@Configuration
public class DataSourceConfiguration {
//会依赖注入Spring容器中所有的mybatis的Interceptor拦截器
@Autowired(required = false)
private Interceptor[] interceptors;
@Autowired
private ApplicationContext applicationContext;
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
@Bean
public SqlSessionFactory dataHubSqlSessionFactory(DataSource dataSource) throws Exception {
MybatisConfiguration configuration = new MybatisConfiguration();
// configuration.addInterceptor(paginationInterceptor());
MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
sqlSessionFactoryBean.setConfiguration(configuration);
sqlSessionFactoryBean.setDataSource(dataSource);
//如果自定义sqlFactoryBean加入这个自定义的mybatis拦截器才会生效
sqlSessionFactoryBean.setPlugins(interceptors);
//设置globalConfig自定义注入器才会生效
sqlSessionFactoryBean.setGlobalConfig(getGlobalConfig());
return sqlSessionFactoryBean.getObject();
}
@Bean
public GlobalConfig getGlobalConfig() {
if (this.applicationContext.getBeanNamesForType(MySqlInjector.class, false, false).length > 0 ) {
LampSqlInjector lampSqlInjector = applicationContext.getBean(MySqlInjector.class);
GlobalConfig globalConfig = new GlobalConfig().setSqlInjector(mySqlInjector);
return globalConfig;
}
return new GlobalConfig();
}
}
这种情况如果使用mybatis-plus框架无法使用其baseMapper的一些基础方法比如selectList等等,我们可以自定义mybaits-plus injector来实现
2.自定义injector配合数据权限使用
- 首先自定义通用Mapper
public interface SuperMapper<T> extends BaseMapper<T> {
/**模仿BaseMapper做的带数据权限查询的接口
* @Author sff
* @Description
* @Date 13:57 2021/9/9
* @Param [queryWrapper, dataScope]
* @Return java.util.List<T>
*/
List<T> selectListByScope(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper, DataScope dataScope);
/**
* 通用数据权限分页查询
*
* @Author sff
* @Description
* @Date 17:19 2021/9/6
* @Param [page, queryWrapper, dataScopeType]
* @Return E
*/
<E extends IPage<T>> E selectPageByScope(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper, DataScope dataScope);
}
- 自定义selectListByScepe
/**
* 相当于selectList的重载,加了数据权限
* @Author sff
* @Description
* @Date 14:45 2021/9/9
*/
public class SelectListByScope extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
//给sql脚本赋值
SqlMethod sqlMethod = SqlMethod.SELECT_LIST;
//给sql脚本赋值
String sql = String.format(sqlMethod.getSql(), sqlFirst(), sqlSelectColumns(tableInfo, true), tableInfo.getTableName(),
sqlWhereEntityWrapper(true, tableInfo), sqlComment());
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
return this.addSelectMappedStatementForTable(mapperClass, "selectListByScope", sqlSource, tableInfo);
}
}
public class SelectPageByScope extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
SqlMethod sqlMethod = SqlMethod.SELECT_PAGE;
String sql = String.format(sqlMethod.getSql(), sqlFirst(), sqlSelectColumns(tableInfo, true),
tableInfo.getTableName(), sqlWhereEntityWrapper(true, tableInfo), sqlComment());
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
return this.addSelectMappedStatementForTable(mapperClass, "selectPageByScope", sqlSource, tableInfo);
}
}
MappedStatement对象:里封装了XML当中一个标签的所有信息,主要用于保存配置的SQL语句。
示例: insert into c_table_permission (data_id, org_id, is_query, is_visible) values (#{item.tableId}, #{orgId}, #{item.isQuery}, #{item.isVisible})
3. 自定义mybtis注入器
public class MySqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
//增加自定义方法
methodList.add(new SelectListByScope());
methodList.add(new SelectPageByScope());
return methodList;
}
}
- 增加配置类将SQL注入器添加到容器
@Bean
@ConditionalOnMissingBean
public MySqlInjector getMySqlInjector() {
return new MySqlInjector();
}
- 重复interceptor步骤(如果自定义了sqlSessionFactoryBean则需要手动放到sqlSessionFactory容器中)
@Configuration
public class DataSourceConfiguration {
//会依赖注入Spring容器中所有的mybatis的Interceptor拦截器
@Autowired(required = false)
private Interceptor[] interceptors;
@Autowired
private ApplicationContext applicationContext;
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
@Bean
public SqlSessionFactory dataHubSqlSessionFactory(DataSource dataSource) throws Exception {
MybatisConfiguration configuration = new MybatisConfiguration();
// configuration.addInterceptor(paginationInterceptor());
MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
sqlSessionFactoryBean.setConfiguration(configuration);
sqlSessionFactoryBean.setDataSource(dataSource);
//如果自定义sqlFactoryBean加入这个自定义的mybatis拦截器才会生效
sqlSessionFactoryBean.setPlugins(interceptors);
//设置globalConfig自定义注入器才会生效
sqlSessionFactoryBean.setGlobalConfig(getGlobalConfig());
return sqlSessionFactoryBean.getObject();
}
@Bean
public GlobalConfig getGlobalConfig() {
if (this.applicationContext.getBeanNamesForType(MySqlInjector.class, false, false).length > 0 ) {
LampSqlInjector lampSqlInjector = applicationContext.getBean(MySqlInjector.class);
GlobalConfig globalConfig = new GlobalConfig().setSqlInjector(mySqlInjector);
return globalConfig;
}
return new GlobalConfig();
}
}
3.实际应用
@Service
public class EventServiceImpl extends ServiceImpl<EventMapper, Event> implements EventService {
@Override
public IPage<Event> permissionList(User user) {
DataScope dataScope = new DataScope();
dataScope.setUserId(user.getId());
IPage<Event> eventIPage = new Page<>(1, 10);
IPage<Event> events = baseMapper.selectPageByScope(eventIPage, Wraps.<Event>lbQ()
.eq(Event::getWorkspaceId, user.getWorkspaceId())
.orderByDesc(Event::getUpdateTime), dataScope);
// List<Event> eventList = baseMapper.selectListByScope(Wraps.<Event>lbQ().eq(Event::getWorkspaceId, user.getWorkspaceId()).orderByAsc(Event::getCreateBy), dataScope);
return events;
}
}