电商商城定制开发深入分析若依数据权限@datascope (注解+AOP+动态sql拼接) 【循序渐进,附分析过程】

电商商城定制开发笔者最近在努力的分析若依框架,电商商城定制开发今天看到了若依对数据电商商城定制开发权限进行控制的部分,电商商城定制开发自定义注解+AOP+动态SQL的注入,电商商城定制开发看的我是眼花缭乱,电商商城定制开发然后我又认真的复盘了一遍整个的实现过程,不由得感叹一句,若依YYDS~~

简单猜测

除了我们平时都知道的 路由权限(即对接口的外),在日常生产开发中,我们还应该有对数据的访问权限。

在若依这个中,通过角色中的数据范围这个属性来对数据权限进行控制。

对应实体类:

深入分析

一个用户肯定是 有一种角色的,也肯定是隶属于一个部门的。

这里咱们就以用户在查询用户时,即 selectUserList时所做的数据权限为例进行分析。

若依在进行数据权限的访问时,持久层(Mapper层)中对数据进行处理,根据用户角色的权限对数据进行过滤。

<select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult">   select u.user_id, u.dept_id, u.login_name, u.user_name, u.user_type, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.salt, 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'   <if test="userId != null and userId != 0">      AND u.user_id = #{userId}   </if>   <if test="loginName != null and loginName != ''">      AND u.login_name like concat('%', #{loginName}, '%')   </if>   <if test="status != null and status != ''">      AND u.status = #{status}   </if>   <if test="phonenumber != null and phonenumber != ''">      AND u.phonenumber like concat('%', #{phonenumber}, '%')   </if>   <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->      AND date_format(u.create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')   </if>   <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->      AND date_format(u.create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')   </if>   <if test="deptId != null and deptId != 0">      AND (u.dept_id = #{deptId} OR u.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE FIND_IN_SET (#{deptId},ancestors) ))   </if>   <!-- 数据范围过滤 -->          ${params.dataScope}</select>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

我们可以看到倒数第二行${params.dataScope}就是对数据范围进行过滤

其中,params指的是parameterType="SysUser"传来的参数 SysUse的一个属性,然后这个属性的dataScope属性。

思考:既然params.dataScope通过占位符嵌入在这里,那么他肯定是一个sql语句。我们返回到sysUser实体类中,发现sysUser中并没有params 这个属性。

这里我们可以看到SysUser继承了BaseEntity,果然我们在BaseEntity这个类中发现了 params 这个属性

public class BaseEntity implements Serializable{    private static final long serialVersionUID = 1L;    /** 搜索值 */    private String searchValue;    /** 创建者 */    private String createBy;    /** 创建时间 */    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")    private Date createTime;    /** 更新者 */    private String updateBy;    /** 更新时间 */    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")    private Date updateTime;    /** 备注 */    private String remark;    /** 请求参数 */    private Map<String, Object> params;    public String getSearchValue()    {        return searchValue;    }    public void setSearchValue(String searchValue)    {        this.searchValue = searchValue;    }    public String getCreateBy()    {        return createBy;    }    public void setCreateBy(String createBy)    {        this.createBy = createBy;    }    public Date getCreateTime()    {        return createTime;    }    public void setCreateTime(Date createTime)    {        this.createTime = createTime;    }    public String getUpdateBy()    {        return updateBy;    }    public void setUpdateBy(String updateBy)    {        this.updateBy = updateBy;    }    public Date getUpdateTime()    {        return updateTime;    }    public void setUpdateTime(Date updateTime)    {        this.updateTime = updateTime;    }    public String getRemark()    {        return remark;    }    public void setRemark(String remark)    {        this.remark = remark;    }    public Map<String, Object> getParams()    {        if (params == null)        {            params = new HashMap<>();        }        return params;    }    public void setParams(Map<String, Object> params)    {        this.params = params;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101

到这里 我们已经知道有 params这个属性的存在,并且能够肯定 params.dataScope 这是一个sql语句。 所以我们下一步就是要确定这个sql语句是什么时候注入的,怎么注入的?

高阶玩法

我们能够发现若依自定义了一个注解 @DataScope

/** * 数据权限过滤注解 *  * @author ruoyi */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface DataScope{    /**     * 部门表的别名     */    public String deptAlias() default "";    /**     * 用户表的别名     */    public String userAlias() default "";}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

我们继续探究这个注解是怎么执行的

我们可以发现在 SysUserServiceImpl Service业务层实现类中发现 @DataScope 的使用

@Override@DataScope(deptAlias = "d", userAlias = "u")public List<SysUser> selectUserList(SysUser user){    return userMapper.selectUserList(user);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

我们现在来观察DataScopeAspect 这个切面类,具体实现步骤我都在旁边进行注解了

/** * 数据过滤处理 *  * @author ruoyi */@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)")    // 这个controllerDataScope是一个形参    public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable    {        clearDataScope(point);        handleDataScope(point, controllerDataScope);    }    protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope)    {        // 获取当前的用户        SysUser currentUser = ShiroUtils.getSysUser();        if (currentUser != null)        {            // 如果是超级管理员,则不过滤数据            if (!currentUser.isAdmin())            {                dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),                        controllerDataScope.userAlias());            }        }    }    /**     * 数据范围过滤     *      * @param joinPoint 切点     * @param user 用户     * @param deptAlias 部门别名     * @param userAlias 用户别名     */    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                {                    // 数据权限为仅本人且没有userAlias别名不查询任何数据                    sqlString.append(" OR 1=0 ");                }            }        }        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属性中 这个属性是一个Map            }        }    }    /**     * 拼接权限sql前先清空params.dataScope参数防止注入     */    private void clearDataScope(final JoinPoint joinPoint)    {        Object params = joinPoint.getArgs()[0];        if (StringUtils.isNotNull(params) && params instanceof BaseEntity)        {            BaseEntity baseEntity = (BaseEntity) params;            baseEntity.getParams().put(DATA_SCOPE, "");        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149

!!!!完结撒花!!!

如果你对这个过程看懂了的话,相信你也能够数据库中 sys_role_dept这个表的含义了吧,笔者当时也是不明白这个到底是个什么意思。其实这张表就是存放那些 自定义数据权限的角色和它对应的部门数据权限。

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发