Springboot+mybatis plus + Annotation for Data Permission Isolation
- 2021-10-24 22:44:30
- OfStack
Catalog 1. Create annotations
2. Implementation
1. Create comments
2. Implementation
2. Implementation
1. Create comments
When this annotation is typed on the class, there is no need to pass parameters, and all query interfaces under the class turn on data isolation; Data isolation is turned on by default on the method, and validation is turned off if the parameter is false
/**
* Data permission validation annotation
* @author xiaohua
* @date 2021/6/23
*/
@Documented
@Target({METHOD, ANNOTATION_TYPE, TYPE})
@Retention(RUNTIME)
public @interface DataPermission {
/**
* Do you want to isolate data permissions
*/
boolean isPermi() default true;
}
2. Implementation
@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;
// Scanned package path (according to your own project path), here is the package path in the taken configuration
@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<>();
}
// Reflection scanning packets will be slow, so there is a lazy load here
if (classNames == null) {
synchronized (LazyInit.class){
if (classNames == null){
// Scan all classes containing the specified annotations under the specified package path
Set<Class<?>> classSet = ClassUtil.scanPackageByAnnotation(packagePath, DataPermission.class);
if (classSet == null && classSet.size() == 0){
classNames = new ArrayList<>();
} else {
// Get the full name of the class
classNames = classSet.stream().map(Class::getName).collect(Collectors.toList());
}
}
}
}
// Get mybatis Adj. 1 Objects
StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
// mappedStatement.getId() For the execution of mapper The full pathname of the method ,newId For the execution of mapper The full name of the class of the method
String newId = mappedStatement.getId().substring(0, mappedStatement.getId().lastIndexOf("."));
// If it is not the specified method, end the interception directly
if (!classNames.contains(newId)) {
return invocation.proceed();
}
String newName = mappedStatement.getId().substring(mappedStatement.getId().lastIndexOf(".") + 1, mappedStatement.getId().length());
// Do you want to turn on data permissions
boolean isPermi = true;
Class<?> clazz = Class.forName(newId);
// Traversal method
for (Method method : clazz.getDeclaredMethods()) {
// Method contains DataPermission Annotation, if it contains annotations, filter the data results
if (method.isAnnotationPresent(DataPermission.class) && newName.equals(method.getName())) {
DataPermission dataPermission = method.getAnnotation(DataPermission.class);
if (dataPermission != null) {
// Do not validate
if (!dataPermission.isPermi()) {
isPermi = false;
} else { // Enable authentication
isPermi = true;
}
}
}
}
if (isPermi){
// Get to the original sql Statement
String sql = statementHandler.getBoundSql().getSql();
// Parses and returns the new SQL Statement, processing only queries sql
if (mappedStatement.getSqlCommandType().toString().equals("SELECT")) {
// String newSql = getNewSql(sql,deptIds,user.getUserId());
sql = getSql(sql,deptIds,user.getUserId());
}
// Modify sql
metaObject.setValue("delegate.boundSql.sql", sql);
}
return invocation.proceed();
} catch (Exception e){
logger.error(" Data permission isolation exception: ", e);
return invocation.proceed();
}
}
/**
* Analyse SQL Statement and returns the new SQL Statement
* Note that this method uses the JSqlParser To operate SQL That dependency package Mybatis-plus Has been integrated. If you want to use it alone, please import the dependencies yourself first
*
* @param sql Original SQL
* @return New SQL
*/
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 + ")";
// Modify the original statement
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();
// Obtain the original SQL Adj. where Condition
final Expression expression = plainSelect.getWhere();
// Add new where Condition
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(" Analytic primitive SQL And build a new SQL Error: " + e);
return sql;
}
}