若依DataScopeAspect数据权限解析和ew.customSqlSegment源码解析

这篇具有很好参考价值的文章主要介绍了若依DataScopeAspect数据权限解析和ew.customSqlSegment源码解析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


使用方法:

ew是mapper方法里的@Param(Constants.WRAPPER) Wrapper queryWrapper对象

首先判断ew.emptyOfWhere是否存在where条件,有的话再拼接上去,ew.customSqlSegment是WHERE + sql语句
没有where的时候加上 == false

使用${ew.sqlSegment} 如果是连表查询且查询条件是连表的字段则需在service层拼接查询条件时字段前指定别名

sqlSegment:

<where>
    ${ew.sqlSegment}
</where>

customSqlSegment:

${ew.customSqlSegment} 则省略 <where></where>

eg: select * from a<where> ${ew.sqlSegment} </where>

select * from a ${ew.customSqlSegment}

注:在对应的dao方法中 一样要指定 @Param(Constants.WRAPPER) Wrapper<User> ew,其中Constants.WRAPPER="ew"源码中可以看到
————————————————


一、DataScopeAspect

从这里可以看出:表sys_role_dept 的用途,之前一起不清楚 role_dept关联表的含义。
自定义数据权限时(DATA_SCOPE_CUSTOM),通过角色 可以管理哪几个部门,来实现的。

@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) throws Throwable
    {
        clearDataScope(point);
        handleDataScope(point, controllerDataScope);
    }

    protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope)
    {
        // 获取当前的用户
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (StringUtils.isNotNull(loginUser))
        {
            SysUser currentUser = loginUser.getUser();
            // 如果是超级管理员,则不过滤数据
            if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin())
            {
                dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
                        controllerDataScope.userAlias());
            }
        }
    }

    /**
     * 数据范围过滤
     *
     * @param joinPoint 切点
     * @param user 用户
     * @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))
            {
                // 自定义数据权限,通过 role_dept, roleid可以查看哪些部门deptid的数据
                // 关于 OR  关键字,大胆猜测是考虑 有多个角色时(因为是for),用OR 连接。当然第一个OR 肯定得去掉,(看后面验证) 
                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;
                // 验证了前面的猜测:一个user多个role时,会拼接多个OR 部门的数据, 猜测第一个OR ,按sql 逻辑,理应去掉。 
                // 这里substring(4) 去掉第一个" OR "
                baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
            }
        }
    }
    上面拼接后: and dept_id in (),  放到 baseEntity的 params里 。

    /**
     * 拼接权限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, "");
        }
    }
}
-----------------------------------

${ew.customsqlsegment},鉴权,前端,java,mybatis

使用场景

二、ew.customSqlSegment

${ew.customSqlSegment}

${ew.customsqlsegment},鉴权,前端,java,mybatis

build:

${ew.customsqlsegment},鉴权,前端,java,mybatis

${ew.customsqlsegment},鉴权,前端,java,mybatis

this.normal : queryWrapper where 条件不为空的时候,才有normal

${ew.customsqlsegment},鉴权,前端,java,mybatis

get

${ew.customsqlsegment},鉴权,前端,java,mybatis

${ew.customsqlsegment},鉴权,前端,java,mybatis

${ew.customsqlsegment},鉴权,前端,java,mybatis

第二次 进来add(), 已经拼接完 ew.customSqlSegment 了, 因为@DataPermission 注解进来的 动态拼接

${ew.customsqlsegment},鉴权,前端,java,mybatis

${ew.customsqlsegment},鉴权,前端,java,mybatis
${ew.customsqlsegment},鉴权,前端,java,mybatis
${ew.customsqlsegment},鉴权,前端,java,mybatis

${ew.customsqlsegment},鉴权,前端,java,mybatis

${ew.customsqlsegment},鉴权,前端,java,mybatis
${ew.customsqlsegment},鉴权,前端,java,mybatis

Mybatis-plus 在自定义的sql语句中 调用QueryWrapper 的查询条件

需求

项目中需要实现自定义的查询,但是仍然想用QueryWrapper对象里面的那些查询,

解决

在接口中方法中:@Param(Constants.WRAPPER) QueryWrapper queryWrapper; 里面的Constants.WRAPPER其实就是:“ew”

${ew.customsqlsegment},鉴权,前端,java,mybatis

在我们的Mapper.xml文件中:

直接使用:${ew.customSqlSegment} 就可以调用QueryWrapper对象的条件sql

<select id="selectCustExtraInfoPage">
        SELECT * FROM cust_info ${ew.customSqlSegment}
</select>

分析

就上面就可以直接用了,下面来分析怎么调用的

首先${ew.customSqlSegment} 这里,我们首先猜测Query Wrapper里面是不是有这个属性,因为我们用过mybaits都知道, 传入一个对象之后,直接对象.属性就可以获取值
————————————————

附上Query Wrapper的关系图

${ew.customsqlsegment},鉴权,前端,java,mybatis
我在Wrapper类中找到了getCustomSqlSegment()方法,所以我猜测肯定是调用了这个get方法,

但是奇怪的是这个类里面并没有customSqlSegment属性字段

    public String getCustomSqlSegment() {
        MergeSegments expression = this.getExpression();
        if (Objects.nonNull(expression)) {
            NormalSegmentList normal = expression.getNormal();
            String sqlSegment = this.getSqlSegment();
            if (StringUtils.isNotBlank(sqlSegment)) {
            // 注意这里,如果没有where 条件,不拼接 customSqlSegment
                if (normal.isEmpty()) {
                    return sqlSegment;
                }

                return "WHERE " + sqlSegment;
            }
        }

        return "";
    }

注意这里,如果没有where 条件,不拼接 customSqlSegment

所以猜测是直接调用的这个get方法,根据我们传入的属性名称,拼接成get+属性名称,这样来调用的吗?

扩展

所以我就写了一个demo

1、没有属性值

2、但是有一个getUserForId()的方法,

3、在mapper.xml文件中 :对象.userForId 但是注意:我的类里面并没有这个属性

试一下是否可以成功查询出数据!

实体类:
${ew.customsqlsegment},鉴权,前端,java,mybatis

xml文件

${ew.customsqlsegment},鉴权,前端,java,mybatis
调用:居然真的查询成功了!!!!!!!!!!!!!

${ew.customsqlsegment},鉴权,前端,java,mybatis

end 查看源码中:

Reflector类中:
构造方法里面,根据该类名获取所有的get方法

${ew.customsqlsegment},鉴权,前端,java,mybatis
到这里大家应该就明白了,

是根据所有的get方法,经过处理

  private void addSetMethods(Method[] methods) {
    Map<String, List<Method>> conflictingSetters = new HashMap<>();
    Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 1 && PropertyNamer.isSetter(m.getName()))
      .forEach(m -> addMethodConflict(conflictingSetters, PropertyNamer.methodToProperty(m.getName()), m));
    resolveSetterConflicts(conflictingSetters);
  }

  private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) {
    if (isValidPropertyName(name)) {
      List<Method> list = MapUtil.computeIfAbsent(conflictingMethods, name, k -> new ArrayList<>());
      list.add(method);
    }
  }

  private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) {
    for (Entry<String, List<Method>> entry : conflictingSetters.entrySet()) {
      String propName = entry.getKey();
      List<Method> setters = entry.getValue();
      Class<?> getterType = getTypes.get(propName);
      boolean isGetterAmbiguous = getMethods.get(propName) instanceof AmbiguousMethodInvoker;
      boolean isSetterAmbiguous = false;
      Method match = null;
      for (Method setter : setters) {
        if (!isGetterAmbiguous && setter.getParameterTypes()[0].equals(getterType)) {
          // should be the best match
          match = setter;
          break;
        }
        if (!isSetterAmbiguous) {
          match = pickBetterSetter(match, setter, propName);
          isSetterAmbiguous = match == null;
        }
      }
      if (match != null) {
        addSetMethod(propName, match);
      }
    }
  }

保存到:getMethods中

  private void addGetMethod(String name, Method method, boolean isAmbiguous) {
    MethodInvoker invoker = isAmbiguous
        ? new AmbiguousMethodInvoker(method, MessageFormat.format(
            "Illegal overloaded getter method with ambiguous type for property ''{0}'' in class ''{1}''. This breaks the JavaBeans specification and can cause unpredictable results.",
            name, method.getDeclaringClass().getName()))
        : new MethodInvoker(method);
    getMethods.put(name, invoker);
    Type returnType = TypeParameterResolver.resolveReturnType(method, type);
    getTypes.put(name, typeToClass(returnType));
  }

${ew.customsqlsegment},鉴权,前端,java,mybatis

调用的时候:根据xml里面的属性名称,在我们上面初始化的getMethods的map中找到对应的方法,执行方法的调用

${ew.customsqlsegment},鉴权,前端,java,mybatis

总结
所以这就是为啥 ${ew.customSqlSegment} 可以调用的原因了文章来源地址https://www.toymoban.com/news/detail-772814.html

到了这里,关于若依DataScopeAspect数据权限解析和ew.customSqlSegment源码解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 若依框架基于@PreAuthorize注解的权限控制

    目录 一、Java注解(Annotation) 1. 概述         2. Annotation通用定义 (1)@interface (2)@Documented (3)@Target(ElementType.TYPE) (4)@Retention(RetentionPolicy.RUNTIME) 二、基于注解的权限控制 1. 数据权限  2. 角色权限         Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注

    2023年04月24日
    浏览(49)
  • 若依的权限管理 v-hasPermi

    v-hasPermi 用法: 然后在后台管理系统的菜单栏中找到这个模块。 把权限字符串 managementDocument-note-add 复制进去 点击确定 然后这个按钮的权限就设置好了 希望有所帮助

    2024年02月13日
    浏览(26)
  • RuoYi若依管理系统最新版 基于SpringBoot的权限管理系统

    RuoYi是一个后台管理系统,基于经典技术组合(Spring Boot、Apache Shiro、MyBatis、Thymeleaf)主要目的让开发者注重专注业务,降低技术难度,从而节省人力成本,缩短项目周期,提高软件安全质量。 本地版本为截止2023-9-10最新版本V4.7.7 完全响应式布局(支持电脑、平板、手机等所

    2024年02月09日
    浏览(32)
  • 若依框架解析

    1.框架介绍 RuoYi 是一个基于Java技术开发的后台管理系统,基于技术组合(SpringBoot+Vue),内置模块有:部门管理、角色用户、菜单即按钮授权、数据权限、系统参数、日志管理、代码生成、表单构建等。支持多数据源、支持分布式事务。 2.创建相应模块 3.在主pom文件中加入依赖

    2024年02月06日
    浏览(40)
  • 若依使用及源码解析(前后端分离版)

    部署环境 JDK = 1.8 MYSQL = 5.7 Maven = 3.0 Node = 12 Redis = 3 运行若依项目 下载若依源码  若依官网 若依项目源码(前后端分离) 运行后端项目  ruoyi-ui就是vue项目(这里使用vscode打开)   整体用idea打开 1.配置数据库(sql提供sql文件中的sql脚本配置) 创建数据库 ruoyi_vue数据库并导入qua

    2024年02月07日
    浏览(27)
  • 若依@Excel注解自动获取导出字段,字典解析

    若依系统中实体类导出字段使用了 @Execl 如不是若依系统 最后附有excel接口 普通导出: @Excel (name = \\\"单位\\\") private String unit; 带字典解析导出: @Excel (name = \\\"状态\\\", dictType = \\\"sys_true_false\\\") private Integer attributeCategory; 系统后台字典配置: 新增的字典也会存在redis缓存中(直接查询缓存

    2024年02月11日
    浏览(66)
  • 若依使用easyexcel读取解析excel文件示例

    睿洛医疗 目标:结构化自定义列数和列标题的excel数据。   创建数据类 创建监听类 创建数据类 创建监听类 参考:EasyExcel

    2024年02月11日
    浏览(30)
  • 全面解析若依框架(springboot-vue前后分离--后端部分)

    前端启动 后端启动 创建数据库ry-vue,导入ry_2021xxxx.sql,quartz.sql,加载好依赖直接启动。 后端技术 SpringBoot Spring Security JWT MyBatis Druid Fastjson 分页实现 导入导出 上传下载(框架使用的简单,不做讲解) 权限控制 事务管理(这里使用@Transactional,不做讲解,具体和Spring的8种事务

    2024年01月16日
    浏览(41)
  • 【基于若依开发的后台管理系统通用模板-低代码权限管理框架系统-代码一键生成-可视化拖拽-PC端与手机全端支持】

    LessCode是基于若依系统开发的一款后台管理系统通用模板,低代码权限管理框架系统,可多终端适配,同时支持PC端、APP、小程序、H5;LessCode内置代码生成功能,可一键生成java后端、vue前端、uniapp手机端代码,极大提高了程序员的开发效率;其内置框架功能包含用户角色权限

    2024年04月24日
    浏览(39)
  • ❤ 全面解析若依框架vue2版本(springboot-vue前后分离--前端部分)

    一般在vue项目public文件夹下命名为“favicon.ico” ❤ 处理步骤 第1步:将图标重命名为“favicon.ico”,并放在项目根目录 下。 第2步:然后在index.html中引入,title中修改页面标题。 第3步:修改build文件夹下 webpack .dev.conf.js和webpack.prod.conf.js文件中的内容。 第五步:重新已经成功

    2024年02月12日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包