1、点击若依的系统用户管理页面,测试各种数据权限生成的sql,若依调用的后台方法是:
@DataScope(deptAlias = "d", userAlias = "u") public List<SysUser> selectUserList(SysUser user) { return userMapper.selectUserList(user); }
修改角色表中的data_scope字段,进行各种数据权限的设置。由于spring security会将用户的角色信息存起来,需要用户重新登陆。
不能用admin用户测试测试结果:
DATA_SCOPE_ALL 全部数据权限
SELECT count(0) FROM sys_user u LEFT JOIN sys_dept d ON u.dept_id = d.dept_id WHERE u.del_flag = '0'
DATA_SCOPE_CUSTOM 自定义数据权限
SELECT count(0) FROM sys_user u LEFT JOIN sys_dept d ON u.dept_id = d.dept_id WHERE u.del_flag = '0' AND (d.dept_id IN (SELECT dept_id FROM sys_role_dept WHERE role_id = 2))
去关联了表sys_role_dept去查询当前角色和部门的对应关系,在系统中对应的操作界面是
DATA_SCOPE_DEPT 部门数据权限
SELECT count(0) FROM sys_user u LEFT JOIN sys_dept d ON u.dept_id = d.dept_id WHERE u.del_flag = '0' AND (d.dept_id = 101)
DATA_SCOPE_DEPT_AND_CHILD 部门及以下数据权限
SELECT count(0) FROM sys_user u LEFT JOIN sys_dept d ON u.dept_id = d.dept_id WHERE u.del_flag = '0' AND (d.dept_id IN (SELECT dept_id FROM sys_dept WHERE dept_id = 101 OR find_in_set(101, ancestors)))
DATA_SCOPE_SELF 仅本人数据权限
SELECT count(0) FROM sys_user u LEFT JOIN sys_dept d ON u.dept_id = d.dept_id WHERE u.del_flag = '0' AND (u.user_id = 3)
2、原理
根据测试结果,数据权限的控制就是根据部门或者人员信息来进行的,也就是说,想要进行数据权限过滤的数据必须包含部门编号dept_id或者人员编号user_id。根据系统总结的5种数据类型,生成sql语句,通过在mybatis中添加${params.dataScope} 把生成的sql语句拼接到原sql里。
3、实现 注解+AOP
a、需要使用到的数据表:sys_user\sys_role\sys_user_role\sys_dept\sys_role_dept。 b、这里要能保证用户能登陆并且能获取到自己的角色信息,权限后面会单独说这里只进行了简单处理能满足当前使用。权限方面的处理,sysuser实现了UserDetails接口;新增了UserDetailServiceImpl 实现了UserDetailsService;提供了pssswordEncoder 密码工具。 c、给请求参数,拼接params参数(通过继承baseEntity实现),把拼接号的sql通过params的字段dataScope传入到mybatis的xml中,实现了拼接sql。核心的切面处理方法就是在根据数据权限类型,来拼接sql,传递sql参数。@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 void doBefore(JoinPoint point, DataScope controllerDataScope) { clearDataScope(point); handleDataScope(point, controllerDataScope); } private void handleDataScope(JoinPoint point, DataScope controllerDataScope) { // 获取用户 Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); SysUser currentUser = (SysUser) authentication.getPrincipal(); if(currentUser!=null && !currentUser.isAdmin()){ dataScopeFilter(point, currentUser, controllerDataScope.deptAlias(), controllerDataScope.userAlias()); } } private void dataScopeFilter(JoinPoint point, SysUser currentUser, String deptAlias, String userAlias) { StringBuilder sqlString = new StringBuilder(); // 考虑多个角色的数据权限可能重复的情况 List<String> conditions = new ArrayList<String>(); for (SysRole role : currentUser.getRoles()) { String dataScope = role.getDataScope(); if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope)) { continue; } if (DATA_SCOPE_ALL.equals(dataScope)) { sqlString = new StringBuilder(); break; } else if (DATA_SCOPE_CUSTOM.equals(dataScope)) { sqlString.append(StrUtil.format( " OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, role.getRoleId())); } else if (DATA_SCOPE_DEPT.equals(dataScope)) { sqlString.append(StrUtil.format(" OR {}.dept_id = {} ", deptAlias, currentUser.getDeptId())); } else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) { sqlString.append(StrUtil.format( " OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", deptAlias, currentUser.getDeptId(), currentUser.getDeptId())); } else if (DATA_SCOPE_SELF.equals(dataScope)) { if (StrUtil.isNotBlank(userAlias)) { sqlString.append(StrUtil.format(" OR {}.user_id = {} ", userAlias, currentUser.getUserId())); } else { // 数据权限为仅本人且没有userAlias别名不查询任何数据 sqlString.append(StrUtil.format(" OR {}.dept_id = 0 ", deptAlias)); } } conditions.add(dataScope); } if (StrUtil.isNotBlank(sqlString.toString())) { Object params = point.getArgs()[0]; if (params!=null && params instanceof BaseEntity) { BaseEntity baseEntity = (BaseEntity) params; baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")"); } } } /** * 因为这个参数是后台拼接出来的,在接受到参数的时候先清除了由后台生成 * 避免参数注入 * * @param point */ private void clearDataScope(final JoinPoint point) { Object params = point.getArgs()[0]; if (params != null && params instanceof BaseEntity) { BaseEntity base = (BaseEntity) params; base.getParams().put(DATA_SCOPE,""); } } }
4、测试
@RestController @RequestMapping("/system/user") public class SysUserController { @Autowired private ISysUserService userService; @GetMapping("/list") public AjaxResult list(SysUser user) { List<SysUser> list = userService.selectUserList(user); AjaxResult ajax = AjaxResult.success(); ajax.put("list",list); return ajax; } }
@Override @DataScope(deptAlias = "d", userAlias = "u") public List<SysUser> selectUserList(SysUser user) { return mapper.selectUserList(user); }spring security默认的认证模式formloggin,这里要在页面上进行登陆,然后请求:http://localhost:8080/system/user/list。同样修改sys_role的data_scope值后,需要重新访问登陆页面进行登陆。
5、总结
数据权限过滤其实可能平常业务代码中通过写死sql的方式实现过,若依的这种实现和业务关联的比较紧密,还需要请求参数继承BaseEntity,使用场景上应该尽量少用。
标签:一步,--,DATA,sys,dept,user,SCOPE,data,id From: https://www.cnblogs.com/hunji-fight/p/17078799.html