I think authority management is the most important and core function of this system, other functions are just some modifications, how to complete the management of data authority? To see how Ruoyi works, first understand what the RBAC model (role-based access control model) is, and if you don’t understand it, learn it first.

This is Ruoyi’s database table partitioning, and I’ve used a purple rectangle to show how he provides flexible control by adding a layer of roles between users and permissions.

For example, control which menu bars one of your roles frontend accesses through sys_menu and sys_ROLE. Similarly, this project is based on the division of authority, because an employee must belong to a department

First of all, the system has five permissions:

All data permissions: No data filtering Custom data permissions In this department Data permissions in this department and the following data permissions are only personal data permissionsCopy the code

Custom data permissions

The sys_ROLE_DEPT table is used to map the role ID and department ID, so the permissions are divided by department. The permissions can be allocated in the role management interface.

The core code is implemented using Spring’S AOP, through a custom annotation @datascope, annotation specific functionality implemented in DataScopeAspect.

if (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()));
}
Copy the code

As you can see, he concatenates the SQL using the roleId of our current logged-in user (there can be multiple roles, so this code is in the loop; in the following example, only one role is used).

select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.password, 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' <! -- Omit some if judgments --> <! ${params.datascope} --> ${params.datascope}Copy the code

With the previous AOP concatenated SQL, data filtering is completed by inserting mybatis interpolation expressions into the existing SQL.

For example, in this user query, the current logged-in user role is 2. Before filtering, all users can be queried. Then, after adding the splicing SQL, only the department id of the user is returned among 100,101,105,110.

The complete SQL is shown below

select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.password, 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' <! AND u.dept_id IN (SELECT dept_id FROM sys_role_dept WHERE role_id = 2)Copy the code

This completes the data permission filtering for user queries. This is the code called.

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

In addition to this call, you can also find the following data filters. You can see that the mapper files corresponding to these service method calls all use dept_id in SQL. It can be seen that the department is used to manage data permissions.

If we want data permissions on our own systems, we also need an ID that role can identify.

Other permissions

public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias)
{
    StringBuilder sqlString = new StringBuilder();

    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 {}.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(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));
        }
        else if (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()));
        }
        else if (DATA_SCOPE_SELF.equals(dataScope))
        {
            if (StringUtils.isNotBlank(userAlias))
            {
                sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
            }
            else
            {
                // Data permissions are only for myself without the userAlias alias and no data is queried
                sqlString.append(" OR 1=0 "); }}}Copy the code

Department Data permissions

StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId())
Copy the code

Only users in the same department can be queried. If user A is in department 1 and user B is in department 1, user A can query only itself and user B

Department and the following data permissions

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())
Copy the code

There is a field rooted to the sys_DEPT table that represents all the superior departments

If you query department 105, you will find yourself and departments 110 and 11 (built-in function find_in_set).

Own data permissions

if (StringUtils.isNotBlank(userAlias))
 {
sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
 }
else
{
// Data permissions are only for myself without the userAlias alias and no data is queried
sqlString.append(" OR 1=0 ");
}
Copy the code

For example, SysDeptServiceImpl and SysRoleSerivceImp do not add userAlias. When the user has only its own data permission and views department information, it returns an empty data set.

conclusion

Overall, Ruoyi does a good job of managing data permissions and can be used as a scaffold. When accessing the data that needs to be filtered, we spliced the SQL to the department and only queried the data that meets the condition dept_ID.