理论知识:
Mybatis Interceptor 拦截器
1.创建注解
当此注解打在类上,不需要传参,该类下所有查询接口开启数据隔离;打在方法上默认开启数据隔离,传参为false则该方法关闭验证
/**
* 数据权限验证注解
* @author xiaohua
* @date 2021/6/23
*/
@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 classNames;
@Override
public Object intercept(Invocation invocation) throws Throwable {
try {
LoginInfo user = tokenService.getLoginInfo();
if (user == null){
return invocation.proceed();
}
List deptIds = (List) Convert.toList(user.getDataScope());
if (deptIds == null){
deptIds = new ArrayList<>();
}
//反射扫包会比较慢,这里做了个懒加载
if (classNames == null) {
synchronized (LazyInit.class){
if (classNames == null){
//扫描指定包路径下所有包含指定注解的类
Set> 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();
}
}
/**
* 解析SQL语句,并返回新的SQL语句
* 注意,该方法使用了JSqlParser来操作SQL,该依赖包Mybatis-plus已经集成了。如果要单独使用,请先自行导入依赖
*
* @param sql 原SQL
* @return 新SQL
*/
private String getSql(String sql,List 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;
}
}
摘自: https://juejin.cn/post/6981280887216275492
相似文章: https://blog.csdn.net/hzh1234565/article/details/70226248
JSqlParser官网: https://github.com/JSQLParser/JSqlParser