SoFunction
Updated on 2025-03-06

Simple use of MyBatis-Plus data permission plugin

During normal development, you can only view data requirements based on the current user's role. Generally, we use an interceptor to modify statements before executing SQL in Mybatis to limit the scope of where.

Usually, the interceptor recognizes the annotation, which can better intercept and convert the required interfaces.

The general steps are as follows:

  • Create annotation class
  • Create processing classes, obtain data permissions SQL fragments, and set where conditions
  • Add interceptor to MyBatis-Plus plugin

1. Introduction to the data permission plug-in

Official Document - Data Permissions Plugin:/plugins/data-permission/

DataPermissionInterceptoris a plug-in provided by MyBatis-Plus to implement data permission control. It controls user data access by intercepting executed SQL statements and dynamically splicing permission-related SQL fragments.

How DataPermissionInterceptor works intercepts SQL statements before SQL execution and dynamically adds permission-related SQL snippets based on user permissions. In this way, only data that users have permission to access will be queried.

JSQLParser It is an open source SQL parsing library that can easily parse and modify SQL statements. It is a key tool for plug-in to implement permission logic. MyBatis-Plus' data permissions rely on the parsing capabilities of JSQLParser.

How to use:

  • Customize the implementation processor class of MultiDataPermissionHandler, which handles custom data permission logic.
  • Register data permission interceptor

2. Data permission plug-in implementation

In the project, spring security + oauth2 security authentication is used, and the role is used to define the data permission type.

The data permission type of the role:

@Getter
@RequiredArgsConstructor
public enum DataScopeEnum {

	/**
	  * All data permissions
	  */
	DATA_SCOPE_ALL("0", "All data permissions"),

	/**
	  * Custom data permissions
	  */
	DATA_SCOPE_CUSTOM("1", "Custom Data Permissions"),

	/**
	  * Data permissions for this department and child level
	  */
	DATA_SCOPE_DEPT_AND_CHILD("2", "Department and child data permissions"),

	/**
	  * Data permissions of this department
	  */
	DATA_SCOPE_DEPT("3", "Department Data Permissions"),

	/**
	  * My data permissions
	  */
	DATA_SCOPE_SELF("4", "Permissions to my data"),
	;

	/**
	  * Corresponding database dictionary value
	  */
	private final String dbValue;

	/**
	  * describe
	  */
	private final String description;

}

We have expanded the UserDetails user information. Added field information about data permissions.

public class SxdhcloudUser extends User implements OAuth2AuthenticatedPrincipal {

	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

	/**
	  * Extended attributes to facilitate storage of oauth context-related information
	  */
	private final Map<String, Object> attributes = new HashMap<>();

	/**
	  * Tenant ID
	  */
	@Getter
	@JsonSerialize(using = )
	private final Long tenantId;

	/**
	  * User ID
	  */
	@Getter
	@JsonSerialize(using = )
	private final Long id;

	/**
	  * Department ID
	  */
	@Getter
	@JsonSerialize(using = )
	private final Long deptId;

	/**
	  * Phone number
	  */
	@Getter
	private final String phone;


	/**
	  * Role data permission type, deduplication
	  */
	@Getter
	private final Set<String> dataScopeTypes;

	/**
	  * Data permission department ID collection, deduplication
	  */
	@Getter
	private final Set<Long> dataScopeDeptIds;

	/**
	  * Data permissions for your own ID
	  */
	@Getter
	private final Long dataScopeCreateId;
	
}

After the user login authentication is successful, he obtains the relevant data of the role data permissions and puts it in the UserDetails user information.

1. Custom annotations

Custom data permission annotations.

/**
  * Data permission annotations.
  * Can be used on classes or on methods.
  * - If the Mapper class is annotated, it means that the methods provided by the Mapper and the customized methods will be added with data permissions.
  * - If the method of the Mapper class is added to the annotation, it means that the method will be given data permissions.
  * - If the Mapper class and its methods are annotated at the same time, the priority is: [Class>Methods]
  * - If data permission is not required, you can do not add annotation, or use @DataScope(enabled = false)
  */
@Target({, })
@Retention()
public @interface DataScope {

	/**
	  * Whether it takes effect, default true - effective
	  */
	boolean enabled() default true;

	/**
	  * Table alias
	  */
	String tableAlias() default "";

	/**
	  * Field names for departmental restriction ranges
	  */
	String deptScopeName() default "dept_id";

	/**
	  * The field name of my restricted range
	  */
	String oneselfScopeName() default "create_id";

}

2. Custom processor

Customize the processor class and implement itMultiDataPermissionHandler interface,existgetSqlSegment() methodThe custom data permission logic is processed.

Note: mybaits-plus must be greater than version 3.5.2.

import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;

import ;
import ;
import ;
import ;

/**
  * Data permission assembly logic processing
  *
  */
public class DataScopeHandler implements MultiDataPermissionHandler {

	/**
	  * Get data permissions SQL snippets.
	  * <p>The first parameter of the old {@link MultiDataPermissionHandler#getSqlSegment(Expression, String)} method contains all where condition information. If null is returned, it will overwrite the original where data,</p>
	  * <p>The new version of {@link MultiDataPermissionHandler#getSqlSegment(Table, Expression, String)} method cannot overwrite the original where data. If return null, it means that no where conditions are added</p>
	  *
	  * @param table The database table information executed by the execution, you can obtain the table name and table alias through this parameter
	  * @param where original where condition information
	  * @param mappedStatementId Mybatis MappedStatement Id Based on this parameter, you can determine the specific execution method
	  * @return JSqlParser conditional expression, the returned conditional expression will be spliced ​​after the original expression (the original expression will not be overwritten)
	  */
	@Override
	public Expression getSqlSegment(Table table, Expression where, String mappedStatementId) {
		try {
			Class&lt;?&gt; mapperClazz = ((0, (".")));
			String methodName = ((".") + 1);

			/**
			  * DataScope annotation priority: [Class>Methods]
			  */
			// Get DataScope annotation			DataScope dataScopeAnnotationClazz = ();
			if ((dataScopeAnnotationClazz) &amp;&amp; ()) {
				return buildDataScopeByAnnotation(dataScopeAnnotationClazz);
			}
			// Get all methods in your own class, excluding inheritance.  Has nothing to do with access rights			Method[] methods = ();
			for (Method method : methods) {
				DataScope dataScopeAnnotationMethod = ();
				if ((dataScopeAnnotationMethod) || !()) {
					continue;
				}
				if (().equals(methodName) || (() + "_COUNT").equals(methodName) || (() + "_count").equals(methodName)) {
					return buildDataScopeByAnnotation(dataScopeAnnotationMethod);
				}
			}
		} catch (ClassNotFoundException e) {
			();
		}
		return null;
	}

	/**
	  * DataScope annotation method, data assembly permissions
	  *
	  * @param dataScope
	  * @return
	  */
	private Expression buildDataScopeByAnnotation(DataScope dataScope) {
		// Get UserDetails user information		Authentication authentication = ().getAuthentication();
		if (authentication == null) {
			return null;
		}
		Map&lt;String, Object&gt; userDetailsMap = (());
		Set&lt;String&gt; dataScopeTypes = (Set&lt;String&gt;) ("dataScopeTypes");
		Set&lt;Long&gt; dataScopeDeptIds = (Set&lt;Long&gt;) ("dataScopeDeptIds");
		Long dataScopeCreateId = (Long) ("dataScopeCreateId");

		// Get annotation information		String tableAlias = ();
		String deptScopeName = ();
		String oneselfScopeName = ();
		Expression expression = buildDataScopeExpression(tableAlias, deptScopeName, oneselfScopeName, dataScopeDeptIds, dataScopeCreateId);
		return expression == null ? null : new Parenthesis(expression);
	}

	/**
	  * Assemble data permissions
	  *
	  * @param tableAlias ​​table alias
	  * @param deptScopeName Field name for department restriction range
	  * @param oneselfScopeName field name of my restricted range
	  * @param dataScopeDeptIds Data permission department ID collection, deduplication
	  * @param dataScopeCreateId Data permissions I ID
	  * @return
	  */
	private Expression buildDataScopeExpression(String tableAlias, String deptScopeName, String oneselfScopeName, Set&lt;Long&gt; dataScopeDeptIds, Long dataScopeCreateId) {
		/**
		  * Construct department in expression.
		  */
		InExpression deptIdInExpression = null;
		if ((dataScopeDeptIds)) {
			deptIdInExpression = new InExpression();
			ExpressionList deptIds = new ExpressionList(().map(LongValue::new).collect(()));
			// Set the field expression on the left and the value on the right.			(buildColumn(tableAlias, deptScopeName));
			(new Parenthesis(deptIds));
		}

		/**
		  * Construct your own eq expression
		  */
		EqualsTo oneselfEqualsTo = null;
		if (dataScopeCreateId != null) {
			oneselfEqualsTo = new EqualsTo();
			(buildColumn(tableAlias, oneselfScopeName));
			(new LongValue(dataScopeCreateId));
		}

		if (deptIdInExpression != null &amp;&amp; oneselfEqualsTo != null) {
			return new OrExpression(deptIdInExpression, oneselfEqualsTo);
		} else if (deptIdInExpression != null &amp;&amp; oneselfEqualsTo == null) {
			return deptIdInExpression;
		} else if (deptIdInExpression == null &amp;&amp; oneselfEqualsTo != null) {
			return oneselfEqualsTo;
		}
		return null;
	}

	/**
	  * Build Column
	  *
	  * @param tableAlias ​​table alias
	  * @param columnName Field name
	  * @return Aliased Name Segment with Table
	  */
	public static Column buildColumn(String tableAlias, String columnName) {
		if ((tableAlias)) {
			columnName = tableAlias + "." + columnName;
		}
		return new Column(columnName);
	}

}

3. Register data permission interceptor

Register custom processors in MybatisPlusConfig configuration classDataPermissionInterceptor middle. And add the interceptor to the MyBatis-Plus plugin.

	@Bean
	public MybatisPlusInterceptor mybatisPlusInterceptor() {
		MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
		// 1. Add data permission plugin		(new DataPermissionInterceptor(new DataScopeHandler()));
		// 2. Add a paging plugin        PaginationInnerInterceptor pageInterceptor = new PaginationInnerInterceptor();
        // Set the database dialect type        ();
        // The following configurations are set according to your needs        // If the requested page is larger than the maximum page, the operation will be done. True will be called back to the home page, and false will continue to request.  Default false        (false);
        // The number of pages for single pages is limited, no limit by default        (500L);
        (pageInterceptor);
		return interceptor;
	}

4. Annotation usage

@Mapper
public interface SysUserMapper extends BaseMapper&lt;SysUser&gt; {

	/**
	  * Query user information through user name (including role information)
	  *
	  * @param username
	  * @return userVo
	  */
	UserVO getUserVoByUsername(String username);

	/**
	  *Page query of user information (including roles)
	  *
	  * @param page Pagination
	  * @param userDTO query parameters
	  * @return list
	  */
	@DataScope(tableAlias = "u")
	IPage&lt;UserVO&gt; getUserVosPage(Page page, @Param("query") UserDTO userDTO, @Param("userIds") List&lt;Long&gt; userIds);

}

The comments in the code are written very clearly, and everyone can understand them by themselves.

Reference article:

  • About JSqlparser usage strategy (efficient SQL parsing tool):https:///article/

This is the article about the simple use of the MyBatis-Plus data permission plug-in. For more related content on MyBatis-Plus data permissions, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!