Example of data filtering and permission processing according to ruoyiAOP aspect

content

1. What is AOP

Introduction

2. Ruoyi's AOP implementation

2.1 If AOP is filtered by data

define annotations

Implement the aspect class

2.2 Data Source AOP


1. What is AOP

Introduction

AOP is the abbreviation of Aspect Oriented Programming, which means: Aspect-oriented programming , a technology that realizes unified maintenance of program functions through pre-compilation and dynamic proxy during runtime. It is one of the three core ideas of Spring. AOP is the continuation of OOP , a hot spot in software development, and an important part of the Spring framework. It is a derivative paradigm of functional programming . Using AOP can isolate each part of the business logic, thereby reducing the coupling between each part of the business logic , improving the reusability of the program, and improving the efficiency of development.

In other words, a simple understanding is to cut across the project and place a filter to intercept data or implement code reuse.

original project

insert image description here

 Implement code reuse and reduce the amount of code, but it needs to be continuously introduced and configured.

insert image description here

 In the process of implementing AOP, it not only realizes code reuse, but also separates modules, which reduces the workload . For insert image description heredetails, please refer to the article [SpringBoot-3] Aspect AOP Implementation Authority Verification: Example Demonstration and Annotation Full Solution_Cloud Deep i nowhere blog - CSDN Blog

2. Ruoyi's AOP implementation

A brief introduction to Ruoyi's AOP use

import dependencies

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.1 If AOP is filtered by data

define annotations

/**
 * 数据权限过滤注解
 * 
 * @author ruoyi
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataScope
{
    /**
     * 部门表的别名
     */
    public String deptAlias() default "";

    /**
     * 用户表的别名
     */
    public String userAlias() default "";
}

@DataScope, an annotation for data filtering, there are two parameters that default to " ", these two parameters can be used for data filtering of different tables, and you can add multiple parameters according to your needs

There are three annotations to implement the configuration

@Target is used to describe the scope of use of the annotation, where the described annotation can be used, parameters

@Retention is used to define the life cycle of annotations, parameters

SOURCE Valid in source files (i.e. source files remain)
CLASS Valid in class files (ie class retention)
RUNTIME Valid at runtime (i.e. runtime preserved)

 @Document   is used to mark whether it will be recorded when generating javadoc.

Implement the aspect class

step by step

First define the aspect class

**
 * 数据过滤处理
 * 
 * @author ruoyi
 */
@Aspect
@Component
public class DataScopeAspect

@Aspect is used to indicate that the class is an aspect class @Component The component is added to the container

    @Before("@annotation(controllerDataScope)")
    public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable
    {
        clearDataScope(point);
        handleDataScope(point, controllerDataScope);
    }

@Before is used to define a method that is executed before the aspect cuts into the target method

Parameter JoinPoint: This object encapsulates the information of the aspect method in Spring AOP, and you can get the JoinPoint object that encapsulates the method information.

Parameter DataScope annotation object, including two parameters filled in by the annotation

This method first calls the clearDataScope() method

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

The function is to clear the params.DATA_SCOPE parameter to prevent injection. DATA_SCOPE is a parameter that is put into the request after the AOP aspect is used for data filtering in the sql statement, so it needs to be cleared to avoid sql injection, although it is unlikely

After calling handleDataScope() method

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());
            }
        }

It can be seen that if the request contains user information and is not an administrator, data filtering can be performed

The method dataScopeFilter() is the main part of the logic

 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 ");
                }
            }
        }

The logic is very simple. Use the dataScope value in the request (the value queried from the database) to determine which permission the user has.

Then according to the value of the @DataScope annotation, to determine which table the user is accessing, you can put a string into the sqlString

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) + ")");
            }
        }

If sqlString is not empty, and the parameters of the method defined by the annotation inherit BaseEntity (Entity base class

), put the sqlString in the parameter

Finally, put the @DataScope annotation on the method of the Servcie layer

@DataScope(deptAlias = "aaa")
    public List selectList(Params param)
    {
        return Mapper.selectList(param);
    }

Add in Mapper

${params.dataScope}

dynamic sql injection

2.2 Data Source AOP

Guess you like

Origin blog.csdn.net/xsj5211314/article/details/123794105