最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

聚焦:一步一步实现若依框架--2.4数据权限 data_scope

来源:博客园
1、点击若依的系统用户管理页面,测试各种数据权限生成的sql,若依调用的后台方法是:
@DataScope(deptAlias = "d", userAlias = "u")public List 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@Componentpublic 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 conditions = new ArrayList();        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 list = userService.selectUserList(user);        AjaxResult ajax = AjaxResult.success();        ajax.put("list",list);        return ajax;    }}
@Override@DataScope(deptAlias = "d", userAlias = "u")public List selectUserList(SysUser user){    return mapper.selectUserList(user);}
springsecurity默认的认证模式formloggin,这里要在页面上进行登陆,然后请求:http://localhost:8080/system/user/list。同样修改sys_role的data_scope值后,需要重新访问登陆页面进行登陆。

5、总结

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

关键词: 测试结果 请求参数 角色信息