mybatis 自定义数据权限
就是在mybatis sql组装时,拼接自己的逻辑进去
1、定义注解
该注解,作用在mapper层 方法上,用来开启数据权限,并识别权限表名,列名
package com.wss.common.mybatis.annotation;
import com.wss.common.mybatis.constant.DbPermissionTypeEnum;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 数据权限注解,该注解需要添加在mapper层 方法上面
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataPermission {
/**
* 表名:必填,可以填多个
*/
String[] table_name() default {};
/**
* 拼接权限字段名称:必填,可以填多个,和table_name对应
*/
String[] column_name() default {};
/**
* 权限类型
*/
DbPermissionTypeEnum dbPermissionTypeEnum() default DbPermissionTypeEnum.manageUser;
}
2、定义切面,处理自定义注解 DataPermission
主要是用来保存sql拼接前的数据处理,如当前登录用户信息,用户管理的人员信息,方便sql拼接的时候,拼接权限数据
package com.wss.common.mybatis.aspect;
import com.wss.common.core.constant.SecurityConstants;
import com.wss.common.core.domain.dto.SysUserDto;
import com.wss.common.core.exception.BusinessException;
import com.wss.common.core.feign.RemoteUserService;
import com.wss.common.core.handler.LoginUserContextHolder;
import com.wss.common.core.util.R;
import com.wss.common.core.util.SpringContextHolder;
import com.wss.common.mybatis.annotation.DataPermission;
import com.wss.common.mybatis.config.DataPermissionContext;
import com.wss.common.mybatis.config.DataPermissionDto;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import java.lang.reflect.Method;
import java.util.List;
/**
* 数据权限注解切面
*/
@Aspect
public class DataPermissionAspect {
/**
* 环绕通知,拦截带有 @DataPermission 注解的方法
*/
@Around("@annotation(com.wss.common.mybatis.annotation.DataPermission)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
// 获取方法上的 @DataPermission 注解
DataPermission dataPermission = method.getAnnotation(DataPermission.class);
if (dataPermission != null) {
DataPermissionDto dataPermissionDto = new DataPermissionDto();
// 将注解信息存储在上下文中,供 MyBatis 拦截器使用
dataPermissionDto.setDataPermission(dataPermission);
SysUserDto sysUserDto = LoginUserContextHolder.get();
if(null != sysUserDto){
if(!sysUserDto.isAdmin()){
RemoteUserService remoteUserService = SpringContextHolder.getBean(RemoteUserService.class);
R<List<SysUserDto>> manageDeptUserResult = remoteUserService.getManageDeptUserByUsername(sysUserDto.getUsername(),null,null,null, SecurityConstants.FROM_IN);
if(!manageDeptUserResult.isSuccess()){
throw new BusinessException("获取管理用户失败:"+manageDeptUserResult.getMsg());
}
dataPermissionDto.setManageUserList(manageDeptUserResult.getData());
}
}
DataPermissionContext.set(dataPermissionDto);
}
try {
// 执行目标方法
return joinPoint.proceed();
} finally {
// 方法执行完毕,清除数据权限上下文,避免内存泄露
DataPermissionContext.clear();
}
}
}
package com.wss.common.mybatis.config;
import com.wss.common.core.domain.dto.SysUserDto;
import com.wss.common.mybatis.annotation.DataPermission;
import lombok.Data;
import java.util.List;
@Data
public class DataPermissionDto {
private DataPermission dataPermission;
private List<SysUserDto> manageUserList;
}
定义sql拼接拦截器
自定义类,并实现接口:MultiDataPermissionHandler,重写方法:getSqlSegment 该自定义类,需要放在spring context中
package com.wss.common.mybatis.config;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler;
import com.wss.common.core.domain.dto.SysUserDto;
import com.wss.common.core.handler.LoginUserContextHolder;
import com.wss.common.mybatis.annotation.DataPermission;
import com.wss.common.mybatis.constant.DbPermissionTypeEnum;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Table;
import org.apache.ibatis.exceptions.PersistenceException;
import java.util.List;
import java.util.stream.Collectors;
/**
* Mybatis数据权限拦截器
*/
public class MybatisDataPermissionHandler implements MultiDataPermissionHandler {
/**
* @param table
* @param where
* @param mappedStatementId com.wss.admin.mapper.xxxxxMapper.selectList
* @return
*/
@Override
public Expression getSqlSegment(Table table, Expression where, String mappedStatementId) {
try {
// 获取当前线程中的数据权限信息
DataPermissionDto dataPermissionDto = DataPermissionContext.get();
if(null == dataPermissionDto){
return null;
}
DataPermission dataPermission = dataPermissionDto.getDataPermission();
if (dataPermission == null) {
return null;
}
String[] tableNames = dataPermission.table_name();
if(ArrayUtil.isEmpty(tableNames)){
return null;
}
String[] columnNames = dataPermission.column_name();
if(ArrayUtil.isEmpty(columnNames)){
return null;
}
if(columnNames.length != 1 && columnNames.length != tableNames.length){
throw new PersistenceException("注解[DataPermission]参数错误");
}
String tableName = table.getName();
boolean isDoTable = false;
String columnName = columnNames[0];
for (int i = 0; i < tableNames.length; i++) {
if(tableName.equals(tableNames[i])){
isDoTable = true;
if(columnNames.length != 1){
columnName = columnNames[i];
}
break;
}
}
if(!isDoTable){
return null;
}
String sqlSegment = "";
if(DbPermissionTypeEnum.manageUser.equals(dataPermission.dbPermissionTypeEnum())){
SysUserDto sysUserDto = LoginUserContextHolder.get();
if(null == sysUserDto){
return null;
}
if(sysUserDto.isAdmin()){
return null;
}
List<SysUserDto> manageUserList = dataPermissionDto.getManageUserList();
String manageUsernameListStr = "";
if(CollUtil.isEmpty(manageUserList)){
manageUsernameListStr = "'@'";
}else {
manageUsernameListStr = manageUserList.stream().map(SysUserDto::getUsername).distinct().collect(Collectors.joining("','"));
manageUsernameListStr = "'"+manageUsernameListStr+"'";
}
String alias = null;
Alias aliasObj = table.getAlias();
if(null != aliasObj){
alias = aliasObj.getName();
}else {
alias = tableName;
}
// a.sys_user_name in ('user1','user2')
if(StrUtil.isNotBlank(alias)){
sqlSegment = alias+"."+columnName +" in (" + manageUsernameListStr + ")";
}else {
sqlSegment = columnName +" in (" + manageUsernameListStr + ")";
}
}
return CCJSqlParserUtil.parseCondExpression(sqlSegment);
} catch (JSQLParserException e) {
e.printStackTrace();
return null;
}
}
}
mapper层接口方法添加注解
@Mapper
public interface SysUserMapper extends BaseMapper<SysUser> {
@DataPermission(table_name = {"sys_user","sys_user_dept"},column_name = {"username","user_id","user_id"})
List<SysUser> getUser1();
}
效果:
SELECT user_id,
xxxx,
dept_id
FROM sys_user
WHERE del_flag = '0'
AND (user_id = ?)
AND sys_user.username IN ('ayy09874', 'd_test', 'ces_123', 'abcd_4423234', 'ces_1113');
Mybatis自带的一些方法使用
1、重写BaseMapper的自带方法
如:
@Mapper
public interface SysUserMapper extends BaseMapper<SysUser> {
@DataPermission(table_name = {"sys_user"},column_name = {"username"})
List<SysUser> selectList(@Param(Constants.WRAPPER) Wrapper<SysUser> queryWrapper);
}