Springboot+mybatis-plus+注解實(shí)現(xiàn)數(shù)據(jù)權(quán)限隔離
1.創(chuàng)建注解
當(dāng)此注解打在類上,不需要傳參,該類下所有查詢接口開啟數(shù)據(jù)隔離;打在方法上默認(rèn)開啟數(shù)據(jù)隔離,傳參為false則該方法關(guān)閉驗(yàn)證
/**
* 數(shù)據(jù)權(quán)限驗(yàn)證注解
* @author xiaohua
* @date 2021/6/23
*/
@Documented
@Target({METHOD, ANNOTATION_TYPE, TYPE})
@Retention(RUNTIME)
public @interface DataPermission {
/**
* 是否要進(jìn)行數(shù)據(jù)權(quán)限隔離
*/
boolean isPermi() default true;
}
2. 具體實(shí)現(xiàn)
@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;
//掃描的包路徑(根據(jù)自己的項(xiàng)目路徑來),這里是取的配置里的包路徑
@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<>();
}
//反射掃包會比較慢,這里做了個(gè)懶加載
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()為執(zhí)行的mapper方法的全路徑名,newId為執(zhí)行的mapper方法的類全名
String newId = mappedStatement.getId().substring(0, mappedStatement.getId().lastIndexOf("."));
// 如果不是指定的方法,直接結(jié)束攔截
if (!classNames.contains(newId)) {
return invocation.proceed();
}
String newName = mappedStatement.getId().substring(mappedStatement.getId().lastIndexOf(".") + 1, mappedStatement.getId().length());
//是否開啟數(shù)據(jù)權(quán)限
boolean isPermi = true;
Class<?> clazz = Class.forName(newId);
//遍歷方法
for (Method method : clazz.getDeclaredMethods()) {
//方法是否含有DataPermission注解,如果含有注解則將數(shù)據(jù)結(jié)果過濾
if (method.isAnnotationPresent(DataPermission.class) && newName.equals(method.getName())) {
DataPermission dataPermission = method.getAnnotation(DataPermission.class);
if (dataPermission != null) {
//不驗(yàn)證
if (!dataPermission.isPermi()) {
isPermi = false;
} else { //開啟驗(yàn)證
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("數(shù)據(jù)權(quán)限隔離異常:", e);
return invocation.proceed();
}
}
/**
* 解析SQL語句,并返回新的SQL語句
* 注意,該方法使用了JSqlParser來操作SQL,該依賴包Mybatis-plus已經(jīng)集成了。如果要單獨(dú)使用,請先自行導(dǎo)入依賴
*
* @param sql 原SQL
* @return 新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 + ")";
// 修改原語句
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并構(gòu)建新SQL錯(cuò)誤:" + e);
return sql;
}
}
到此這篇關(guān)于Springboot+mybatis-plus+注解實(shí)現(xiàn)數(shù)據(jù)權(quán)限隔離的文章就介紹到這了,更多相關(guān)Springboot+mybatis-plus+注解實(shí)現(xiàn)數(shù)據(jù)權(quán)限隔離內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java打包文件成zip、壓縮文件及file.mkdir和mkdirs的區(qū)別詳解
這篇文章主要給大家介紹了關(guān)于java打包文件成zip、壓縮文件及file.mkdir和mkdirs區(qū)別詳解的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-12-12
Java執(zhí)行cmd命令兩種實(shí)現(xiàn)方法解析
這篇文章主要介紹了Java執(zhí)行cmd命令兩種實(shí)現(xiàn)方法解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07
Java通過導(dǎo)出超大Excel文件解決內(nèi)存溢出問題
導(dǎo)出excel是咱Java開發(fā)的必備技能,下面這篇文章主要給大家介紹了關(guān)于Java通過導(dǎo)出超大Excel文件解決內(nèi)存溢出問題的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-09-09
PageHelper在springboot+mybatis框架中的使用步驟及原理解析
這篇文章主要介紹了PageHelper在springboot+mybatis框架中的使用步驟及原理解析,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03

