1,Mybaits-plus Config
@AutoConfigureAfter(PageHelperAutoConfiguration.class)
@Configuration
public class MybatisInterceptorAutoConfiguration implements InitializingBean {
@Autowired
private List<SqlSessionFactory> sqlSessionFactoryList;
@Override
@PostConstruct
public void afterPropertiesSet() throws Exception {
//创建自定义mybatis拦截器,添加到chain的最后面
MybatisDataPermissionIntercept mybatisInterceptor = new MybatisDataPermissionIntercept();
for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
org.apache.ibatis.session.Configuration configuration = sqlSessionFactory.getConfiguration();
//自己添加
configuration.addInterceptor(mybatisInterceptor);
}
}
}
2,SecurityUtils
在代码中增加方法
/**
* 获取当前用的数据权限范围
*
* @param userId 用户ID
* @return 结果
*/
public static String getUserDataScope()
{
return getLoginUser().getUser().getRoles().get(0).getDataScope();
}
protected static final ThreadLocal<String> threadLocal = new ThreadLocal();
/**
* 设置权限标识
*/
public static void startDataScope(String ...dataScope){
threadLocal.set(Arrays.toString(dataScope));
}
/**
* 获取权限标识
*/
public static String getDataScope(){
return threadLocal.get();
}
/**
* 清除权限标识
*/
public static void cleanDataScope(){
threadLocal.remove();
}
3,数据权限处理
@Intercepts(
{
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
}
)
@Slf4j
public class MybatisDataPermissionIntercept implements Interceptor {
CCJSqlParserManager parserManager = new CCJSqlParserManager();
/**
* 全部数据权限
*/
public static final String DATA_SCOPE_ALL = "1";
/**
* 自定数据权限
*/
public static final String DATA_SCOPE_CUSTOM = "2";
/**
* 车型数据权限
*/
public static final String DATA_SCOPE_VEHICLE_CONFIGURE = "3";
/**
* 车系及以下数据权限
*/
public static final String DATA_SCOPE_VEHICLE_MODEL_AND_CHILD = "4";
/**
* 仅本人数据权限
*/
public static final String DATA_SCOPE_SELF = "5";
@Override
public Object intercept(Invocation invocation) throws Throwable {
try {
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
Object parameter = args[1];
RowBounds rowBounds = (RowBounds) args[2];
ResultHandler resultHandler = (ResultHandler) args[3];
Executor executor = (Executor) invocation.getTarget();
CacheKey cacheKey;
BoundSql boundSql;
//由于逻辑关系,只会进入一次
if (args.length == 4) {
//4 个参数时
boundSql = ms.getBoundSql(parameter);
cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
} else {
//6 个参数时
cacheKey = (CacheKey) args[4];
boundSql = (BoundSql) args[5];
}
String sql = boundSql.getSql();
log.info("原始SQL: {}", sql);
//判断线程内是否有权限信息
String dataScopes = SecurityUtils.getDataScope();
if (dataScopes!= null && dataScopes.contains("dataScope")){
// 增强sql
Select select = (Select) parserManager.parse(new StringReader(sql));
SelectBody selectBody = select.getSelectBody();
if (selectBody instanceof PlainSelect) {
this.setWhere((PlainSelect) selectBody,dataScopes);
} else if (selectBody instanceof SetOperationList) {
SetOperationList setOperationList = (SetOperationList) selectBody;
List<SelectBody> selectBodyList = setOperationList.getSelects();
selectBodyList.forEach((s) -> {
this.setWhere((PlainSelect) s,dataScopes);
});
}
String dataPermissionSql = select.toString();
log.info("增强SQL: {}", dataPermissionSql);
BoundSql dataPermissionBoundSql = new BoundSql(ms.getConfiguration(), dataPermissionSql, boundSql.getParameterMappings(), parameter);
return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, dataPermissionBoundSql);
}
//注:下面的方法可以根据自己的逻辑调用多次,在分页插件中,count 和 page 各调用了一次
return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
} finally {
//清除线程中权限参数
SecurityUtils.cleanDataScope();
}
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
protected void setWhere(PlainSelect plainSelect,String dataScopes) {
Expression sqlSegment = this.getSqlSegment(plainSelect.getWhere(),dataScopes);
if (null != sqlSegment) {
plainSelect.setWhere(sqlSegment);
}
}
@SneakyThrows
public Expression getSqlSegment(Expression where,String dataScopes) {
//用户信息是在gateway中去获取的,获取完之后直接存入到header中,然后再调用过程中传递header
//getLoginUser 是获取用户信息,在header中去获取
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser == null){
return where;
}
SysUser user = loginUser.getUser();
StringBuilder sqlString = new StringBuilder();
sqlString = getStringBuilder(user,sqlString,dataScopes);
if (StringUtils.isNotBlank(sqlString.toString())) {
if (where == null){
where = new HexValue(" 1 = 1 ");
}
sqlString.insert(0," AND (");
sqlString.append(")");
sqlString.delete(7, 9);
//判断是不是分页, 分页完成之后 清除权限标识
return CCJSqlParserUtil.parseCondExpression(where + sqlString.toString());
}else {
return where;
}
}
private static StringBuilder getStringBuilder(SysUser user, StringBuilder sqlString,String dataScopes) {
for (SysRole role : user.getRoles())
{
String dataScope = role.getDataScope();
if (DATA_SCOPE_ALL.equals(dataScope))
{
sqlString = new StringBuilder();
break;
}
else if (DATA_SCOPE_CUSTOM.equals(dataScope))
{
sqlString.append(StringUtils.format(
" OR {}.id IN ( SELECT vehicle_configure_id FROM sys_role_vehicle_configure WHERE role_id = {} ) ", DataScopeConstants.DATA_SCOPE_VEHICLE_ALIAS,
role.getRoleId()));
if(dataScopes.contains(DataScopeConstants.DATA_SCOPE_USER)){
sqlString.append(StringUtils.format(" AND {}.user_id = {} ", DataScopeConstants.DATA_SCOPE_USER_ALIAS, user.getUserId()));
}
}
else if (DATA_SCOPE_VEHICLE_CONFIGURE.equals(dataScope))
{
sqlString.append(StringUtils.format(" OR {}.vehicle_configure_id = {} ", DataScopeConstants.DATA_SCOPE_VEHICLE_ALIAS, user.getVehicleConfigureId()));
}
else if (DATA_SCOPE_VEHICLE_MODEL_AND_CHILD.equals(dataScope))
{
sqlString.append(StringUtils.format(
" OR {}.id IN ( SELECT id FROM vehicle_configure WHERE vehicle_model_id = {} )",
DataScopeConstants.DATA_SCOPE_VEHICLE_ALIAS, user.getVehicleModelId()));
}
else if (DATA_SCOPE_SELF.equals(dataScope))
{
if (org.apache.commons.lang3.StringUtils.isNotBlank(DataScopeConstants.DATA_SCOPE_USER))
{
sqlString.append(StringUtils.format(" OR {}.user_id = {} ", DataScopeConstants.DATA_SCOPE_USER_ALIAS, user.getUserId()));
}
else
{
// 数据权限为仅本人且没有userAlias别名不查询任何数据
sqlString.append(" OR 1=0 ");
}
}
}
return sqlString;
}
}
StringBuilder 方法就是若依的实现,此次更改同时整合了mybatis-plus-join
下面看实际使用过程:
1,在查询前调用start方法
SecurityUtils.startDataScope(DataScopeConstants.DATA_SCOPE_VEHICLE);
这里的leftjoin要定义好别名,与StringBuilder 里面的一致,不然对应不上数据权限
文笔不好,有什么疑问可以联系我, VX:liuhui_plus