1)点击若依的系统用户管理页面,测试各种数据权限生成的sql,若依调用的后台方法是:

@DataScope(deptAlias = "d", userAlias = "u")
public List<SysUser> selectUserList(SysUser user)
{
    return userMapper.selectUserList(user);
}

  修改角色表中的data_scope字段,进行各种数据权限的设置。由于spring security会将用户的角色信息存起来,需要用户重新登陆。

  不能用admin用户测试

  

若依框架新建mysql表 若依框架数据权限_sql

  测试结果:

  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去查询当前角色和部门的对应关系,在系统中对应的操作界面是 

       

若依框架新建mysql表 若依框架数据权限_数据_02

   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参数。

若依框架新建mysql表 若依框架数据权限_若依框架_03

若依框架新建mysql表 若依框架数据权限_若依框架_04

@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,"");
        }
    }
}

View Code

  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值后,需要重新访问登陆页面进行登陆。

    

若依框架新建mysql表 若依框架数据权限_若依框架新建mysql表_05

 

    

若依框架新建mysql表 若依框架数据权限_数据_06

  5)总结

  数据权限过滤其实可能平常业务代码中通过写死sql的方式实现过,若依的这种实现和业务关联的比较紧密,还需要请求参数继承BaseEntity,使用场景上应该尽量少用。

 

 

代码地址:https://github.com/hunji/RYMirror/releases/tag/2.4%E6%95%B0%E6%8D%AE%E6%9D%83%E9%99%90