查看原文
其他

Spring Boot中使用注解的方式实现数据权限控制

life_牛 SpringForAll社区 2023-07-17
关注我,回复关键字“spring”
免费领取Spring学习资料

来源:http://spring4all.com/forum-post/2738.html

在业务开发过程中,经常会碰到需要查询某个人所在部门的数据或者某个人所在部门相关子部门的数据等需要做数据权限的场景,这里结合若依开发平台描述下如何实现数据权限的控制。

原理:

通过自定义注解的方式在查询数据之前查询配置的权限标记(如:当前部门数据权限等),拼接出相应的sql作为变量传递给Mapper层进行关联查询

1、定义数据权限注解

import java.lang.annotation.Documented;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;


/**

* 数据权限过滤注解

*

*/

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface DataScope

{

/**

* 部门表的别名

*/

public String deptAlias() default "";


/**

* 用户表的别名

*/

public String userAlias() default "";


/**

* 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@ss获取,多个权限用逗号分隔开来

*/

public String permission() default "";

}

2、定义注解切面处理逻辑

/**

* 数据过滤处理

*

*

*/

@Aspect

@Component

public class DataScopeAspect

{

/**

* 全部数据权限

*/

public static final String DATA_SCOPE_ALL = "1";


/**

* 自定数据权限

*/

public static final String DATA_SCOPE_CUSTOM = "2";


/**

* 部门数据权限

*/

public static final String DATA_SCOPE_DEPT = "3";


/**

* 部门及以下数据权限

*/

public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";


/**

* 仅本人数据权限

*/

public static final String DATA_SCOPE_SELF = "5";


/**

* 数据权限过滤关键字

*/

public static final String DATA_SCOPE = "dataScope";


@Before("@annotation(controllerDataScope)")

public voiddoBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable

{

clearDataScope(point);

handleDataScope(point, controllerDataScope);

}


protected voidhandleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope)

{

// 获取当前的用户

LoginUser loginUser = SecurityUtils.getLoginUser();

if(StringUtils.isNotNull(loginUser))

{

SysUser currentUser = loginUser.getUser();

// 如果是超级管理员,则不过滤数据

if(StringUtils.isNotNull(currentUser)&& !currentUser.isAdmin())

{

String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext());

dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),

controllerDataScope.userAlias(), permission);

}

}

}


/**

* 数据范围过滤

*

* @param joinPoint 切点

* @param user 用户

* @param deptAlias 部门别名

* @param userAlias 用户别名

* @param permission 权限字符

*/

public static voiddataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission)

{

StringBuilder sqlString = newStringBuilder();

List<String> conditions = new ArrayList<String>();


for(SysRole role : user.getRoles())

{

String dataScope = role.getDataScope();

if(!DATA_SCOPE_CUSTOM.equals(dataScope)&& conditions.contains(dataScope))

{

continue;

}

if(StringUtils.isNotEmpty(permission)&& StringUtils.isNotEmpty(role.getPermissions())

&& !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))

{

continue;

}

if(DATA_SCOPE_ALL.equals(dataScope))

{

sqlString = newStringBuilder();

conditions.add(dataScope);

break;

}

elseif(DATA_SCOPE_CUSTOM.equals(dataScope))

{

sqlString.append(StringUtils.format(

" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,

role.getRoleId()));

}

elseif(DATA_SCOPE_DEPT.equals(dataScope))

{

sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));

}

elseif(DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))

{

sqlString.append(StringUtils.format(

" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",

deptAlias, user.getDeptId(), user.getDeptId()));

}

elseif(DATA_SCOPE_SELF.equals(dataScope))

{

if(StringUtils.isNotBlank(userAlias))

{

sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));

}

else

{

// 数据权限为仅本人且没有userAlias别名不查询任何数据

sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));

}

}

conditions.add(dataScope);

}


// 多角色情况下,所有角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据

if(StringUtils.isEmpty(conditions))

{

sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));

}


if(StringUtils.isNotBlank(sqlString.toString()))

{

Object params = joinPoint.getArgs()[0];

if(StringUtils.isNotNull(params)&& params instanceof BaseEntity)

{

BaseEntity baseEntity = (BaseEntity) params;

baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");

}

}

}


/**

* 拼接权限sql前先清空params.dataScope参数防止注入

*/

private voidclearDataScope(final JoinPoint joinPoint)

{

Object params = joinPoint.getArgs()[0];

if(StringUtils.isNotNull(params)&& params instanceof BaseEntity)

{

BaseEntity baseEntity = (BaseEntity) params;

baseEntity.getParams().put(DATA_SCOPE, "");

}

}

}

3、在service层使用

/**

* 根据条件分页查询用户列表

*

* @param user 用户信息

* @return 用户信息集合信息

*/

@Override

@DataScope(deptAlias = "d", userAlias = "u")

public List<SysUser>selectUserList(SysUser user)

{

return userMapper.selectUserList(user);

}

需要注意的是,注解中使用的表别名应与mapper定义的别名一致,否则sql无法运行

4、在Mapper中映射使用

<select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult">

select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader from sys_user u

left join sys_dept d on u.dept_id = d.dept_id

where u.del_flag = '0'

<!-- 数据范围过滤 -->

${params.dataScope}

</select>

以上就是通过注解方式实现数据权限控制的方式。

SpringForAll社区已开启积分换福利的活动,日常分享均可获得积分,每周均有各种福利等待热爱学习与分享的小伙伴们领取,详情可见:http://spring4all.com/fuli-huodong

END



Spring Boot 玩一玩代码混淆,防止反编译代码泄露
支付宝二面:使用 try-catch 捕获异常会影响性能吗?
Spring Boot 一个注解,优雅的实现重试机制
干掉Random,这个类已经成为获取随机数的王者

高质量交流群,关注:SpringForAll,回复关键词:加群

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存