1.创建注解
当此注解打在类上,不需要传参,该类下所有查询接口开启数据隔离;打在方法上默认开启数据隔离,传参为false则该方法关闭验证
@Documented
@Target({METHOD, ANNOTATION_TYPE, TYPE})
@Retention(RUNTIME)
public @interface DataPermission {
boolean isPermi() default true;
}
2. 具体实现
@Component
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class DataPermissionInterceptor implements Interceptor {
private static final Logger logger = LoggerFactory.getLogger(DataPermissionInterceptor.class);
@Autowired
private TokenService tokenService;
//扫描的包路径(根据自己的项目路径来),这里是取的配置里的包路径
@Value("${permission.package-path}")
private String packagePath;
private final static String DEPT_ID = "dept_id";
private final static String USER_ID = "create_user";
private static List<String> classNames;
@Override
public Object intercept(Invocation invocation) throws Throwable {
try {
LoginInfo user = tokenService.getLoginInfo();
if (user == null){
return invocation.proceed();
}
List<Long> deptIds = (List<Long>) Convert.toList(user.getDataScope());
if (deptIds == null){
deptIds = new ArrayList<>();
}
//反射扫包会比较慢,这里做了个懒加载
if (classNames == null) {
synchronized (LazyInit.class){
if (classNames == null){
//扫描指定包路径下所有包含指定注解的类
Set<Class<?>> classSet = ClassUtil.scanPackageByAnnotation(packagePath, DataPermission.class);
if (classSet == null && classSet.size() == 0){
classNames = new ArrayList<>();
} else {
//取得类全名
classNames = classSet.stream().map(Class::getName).collect(Collectors.toList());
}
}
}
}
// 拿到mybatis的一些对象
StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
// mappedStatement.getId()为执行的mapper方法的全路径名,newId为执行的mapper方法的类全名
String newId = mappedStatement.getId().substring(0, mappedStatement.getId().lastIndexOf("."));
// 如果不是指定的方法,直接结束拦截
if (!classNames.contains(newId)) {
return invocation.proceed();
}
String newName = mappedStatement.getId().substring(mappedStatement.getId().lastIndexOf(".") + 1, mappedStatement.getId().length());
//是否开启数据权限
boolean isPermi = true;
Class<?> clazz = Class.forName(newId);
//遍历方法
for (Method method : clazz.getDeclaredMethods()) {
//方法是否含有DataPermission注解,如果含有注解则将数据结果过滤
if (method.isAnnotationPresent(DataPermission.class) && newName.equals(method.getName())) {
DataPermission dataPermission = method.getAnnotation(DataPermission.class);
if (dataPermission != null) {
//不验证
if (!dataPermission.isPermi()) {
isPermi = false;
} else { //开启验证
isPermi = true;
}
}
}
}
if (isPermi){
// 获取到原始sql语句
String sql = statementHandler.getBoundSql().getSql();
// 解析并返回新的SQL语句,只处理查询sql
if (mappedStatement.getSqlCommandType().toString().equals("SELECT")) {
// String newSql = getNewSql(sql,deptIds,user.getUserId());
sql = getSql(sql,deptIds,user.getUserId());
}
// 修改sql
metaObject.setValue("delegate.boundSql.sql", sql);
}
return invocation.proceed();
} catch (Exception e){
logger.error("数据权限隔离异常:", e);
return invocation.proceed();
}
}
private String getSql(String sql,List<Long> deptIds,Long userId) {
try {
String condition = "";
String permissionSql = "(";
if (deptIds.size() > 0){
for (Long deptId : deptIds) {
if ("(".equals(permissionSql)){
permissionSql = permissionSql + deptId;
} else {
permissionSql = permissionSql + "," + deptId;
}
}
permissionSql = permissionSql + ")";
// 修改原语句
condition = DEPT_ID +" in " + permissionSql;
} else {
condition = USER_ID +" = " + userId;
}
if (StringUtils.isBlank(condition)){
return sql;
}
Select select = (Select)CCJSqlParserUtil.parse(sql);
PlainSelect plainSelect = (PlainSelect)select.getSelectBody();
//取得原SQL的where条件
final Expression expression = plainSelect.getWhere();
//增加新的where条件
final Expression envCondition = CCJSqlParserUtil.parseCondExpression(condition);
if (expression == null) {
plainSelect.setWhere(envCondition);
} else {
AndExpression andExpression = new AndExpression(expression, envCondition);
plainSelect.setWhere(andExpression);
}
return plainSelect.toString();
} catch (JSQLParserException e) {
logger.error("解析原SQL并构建新SQL错误:" + e);
return sql;
}
}
到此这篇关于Springboot+mybatis-plus+注解实现数据权限隔离的文章就介绍到这了,更多相关Springboot+mybatis-plus+注解实现数据权限隔离内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!