当前位置:网站首页>In-depth analysis if according to data authority @datascope (annotation + AOP + dynamic sql splicing) [step by step, with analysis process]
In-depth analysis if according to data authority @datascope (annotation + AOP + dynamic sql splicing) [step by step, with analysis process]
2022-08-05 06:45:00 【User Nickname 23】
The author is currently working hard to analyze the open source project Ruoyi framework,Today I saw the part of Ruoyi's control of data permissions,自定义注解+AOP+动态SQL的注入,看的我是眼花缭乱,Then I seriously reviewed the entire implementation process again,不由得感叹一句,若依YYDS~~
简单猜测
Except what we usually know 路由权限(i.e. outside the access rights to the interface),in daily production development,We should also have access to the data.
In the framework of Zoe,Data permissions are controlled through the attribute of the data scope in the role.
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8JiPrHVq-1649742911724)(D:softTyporaimageimage-20220412121559708.png)]](/img/b5/03f55bb9058c08a48eae368233376c.png)
对应实体类:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uJq5nvTm-1649742911726)(D:softTyporaimageimage-20220412121959106.png)]](/img/50/644d36403ab7690b2d32112cc50cb0.png)
深入分析
One user definitely is have a role,It must also belong to a department.
Here we use the user when querying the user,即 selectUserListTake the data authorization as an example for analysis.
If you are accessing data rights,持久层(Mapper层)中对数据进行处理,Data is filtered based on the permissions of the user role.
<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') >= 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') <= 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>
We can see the penultimate row${params.dataScope}It is to filter the data range
其中,params指的是parameterType="SysUser"传来的参数 SysUse的一个属性,Then this propertydataScope属性.
思考:既然params.dataScopeEmbed here via placeholder,Then he must be onesql语句.我们返回到sysUser实体类中,发现sysUser中并没有params 这个属性.
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mb8Z0tn3-1649742911728)(D:softTyporaimageimage-20220412131444860.png)]](/img/04/f1d27e14a50ffee263bdfc8bb44688.png)
这里我们可以看到SysUser继承了BaseEntity,果然我们在BaseEntityfound in this class 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;
}
}
到这里 我们已经知道有 params这个属性的存在,And can be sure params.dataScope 这是一个sql语句. So our next step is to determine thissql语句是什么时候注入的,怎么注入的?
高阶玩法
We can find that Zoe has customized an annotation @DataScope
/**
* 数据权限过滤注解
*
* @author ruoyi
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataScope
{
/**
* 部门表的别名
*/
public String deptAlias() default "";
/**
* 用户表的别名
*/
public String userAlias() default "";
}
We continue to explore how this annotation is implemented
我们可以发现在 SysUserServiceImpl ServiceFound in the business layer implementation class @DataScope 的使用
@Override
@DataScope(deptAlias = "d", userAlias = "u")
public List<SysUser> selectUserList(SysUser user)
{
return userMapper.selectUserList(user);
}
Let's observe nowDataScopeAspect 这个切面类,I have annotated the specific implementation steps next to it
/**
* 数据过滤处理
*
* @author ruoyi
*/
@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)") // 这个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(); //Get data permissions for a role
// If the data permission for this role is 全部数据权限
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) + ")");
//将完成好的sqlStatements are placed in the entity class 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, "");
}
}
}
!!!!完结撒花!!!
If you understand the process,I believe you can also in the database sys_role_deptThe meaning of this table,I didn't even know what this meant at the time.In fact, this table is to store those The role of the custom data permission and its corresponding department data permission.
先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在.深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小.自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前.因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担.添加下方名片,即可获取全套学习资料哦
边栏推荐
- Cocos Creator Mini Game Case "Stick Soldier"
- 【考研结束第一天,过于空虚,想对自己进行总结一下】
- BIO, NIO, AIO practical study notes (easy to understand theory)
- cs231n学习记录
- 数组&的运算
- 指针常量与常量指针 巧记
- 多用户商城多商户B2B2C拼团砍价秒杀支持小程序H5+APP全开源
- Configuration of routers and static routes
- One-arm routing experiment and three-layer switch experiment
- 文件内音频的时长统计并生成csv文件
猜你喜欢

LeetCode中常用语言的一些基本方法记录

【FAQ】CCAPI Compatible EOS Camera List (Updated in August 2022)

vs2017关于函数命名方面的注意事项

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

Into the pre-service, thought they play so flowers

VLAN is introduced with the experiment

DevOps process demo (practical record)

摆脱极域软件的限制

Autoware--Beike Tianhui rfans lidar uses the camera & lidar joint calibration file to verify the fusion effect of point cloud images

selenium模块的操作之拉钩
随机推荐
Problems encountered in installing Yolo3 target detection module in Autoware
disabledDate 日期选择器 datePicker
cs231n学习记录
【FAQ】什么是 Canon CCAPI
VLAN is introduced with the experiment
System basics - study notes (some command records)
DevOps - Understanding Learning
Drools规则引擎快速入门(一)
深夜小酌,50道经典SQL题,真香~
Collision, character controller, Cloth components (cloth), joints in the Unity physics engine
Browser Storage for H5
单片机期末复习大题
Four ways to obtain Class objects through reflection
The future of cloud gaming
Media query, rem mobile terminal adaptation
云计算基础-学习笔记
GetEnumerator method and MoveNext and Reset methods in Unity
图像处理、分析与机器视觉一书纠错笔记
Cloud Computing Basics - Study Notes
Vim tutorial: vimtutor