最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MyBatis-Plus數(shù)據(jù)權(quán)限插件的簡單使用

 更新時間:2024年10月31日 11:17:16   作者:Charge8  
在MyBatis-Plus中,通過DataPermissionInterceptor插件實現(xiàn)數(shù)據(jù)權(quán)限控制,首先需要創(chuàng)建自定義注解和處理類,利用JSQLParser庫動態(tài)修改SQL,實現(xiàn)按角色權(quán)限過濾數(shù)據(jù),配置類中注冊攔截器,確保只有授權(quán)用戶能訪問指定數(shù)據(jù),感興趣的可以了解一下

平時開發(fā)中遇到根據(jù)當(dāng)前用戶的角色,只能查看數(shù)據(jù)權(quán)限范圍的數(shù)據(jù)需求。一般我們采用攔截器在mybatis執(zhí)行sql前修改語句,限定where范圍。

通常攔截器針對注解進(jìn)行識別,可以更好的對需要的接口進(jìn)行攔截和轉(zhuǎn)化。

一般步驟如下:

  • 創(chuàng)建注解類
  • 創(chuàng)建處理類,獲取數(shù)據(jù)權(quán)限 SQL 片段,設(shè)置 where條件
  • 將攔截器加到MyBatis-Plus插件中

一、數(shù)據(jù)權(quán)限插件簡介

官方文檔-數(shù)據(jù)權(quán)限插件:https://baomidou.com/plugins/data-permission/

DataPermissionInterceptor 是 MyBatis-Plus 提供的一個插件,用于實現(xiàn)數(shù)據(jù)權(quán)限控制。它通過攔截執(zhí)行的 SQL 語句,并動態(tài)拼接權(quán)限相關(guān)的 SQL 片段,來實現(xiàn)對用戶數(shù)據(jù)訪問的控制。

DataPermissionInterceptor 的工作原理會在 SQL 執(zhí)行前攔截 SQL 語句,并根據(jù)用戶權(quán)限動態(tài)添加權(quán)限相關(guān)的 SQL 片段。這樣,只有用戶有權(quán)限訪問的數(shù)據(jù)才會被查詢出來。

JSQLParser 是一個開源的 SQL 解析庫,可方便地解析和修改 SQL 語句。它是插件實現(xiàn)權(quán)限邏輯的關(guān)鍵工具,MyBatis-Plus 的數(shù)據(jù)權(quán)限依托于 JSQLParser 的解析能力。

使用方法:

  • 自定義 MultiDataPermissionHandler的實現(xiàn)處理器類,處理自定義數(shù)據(jù)權(quán)限邏輯。
  • 注冊數(shù)據(jù)權(quán)限攔截器

二、數(shù)據(jù)權(quán)限插件實現(xiàn)

在項目中,使用的是 spring security + oauth2安全認(rèn)證,角色來定義數(shù)據(jù)權(quán)限類型。

角色的數(shù)據(jù)權(quán)限類型:

@Getter
@RequiredArgsConstructor
public enum DataScopeEnum {

	/**
	 * 全部數(shù)據(jù)權(quán)限
	 */
	DATA_SCOPE_ALL("0", "全部數(shù)據(jù)權(quán)限"),

	/**
	 * 自定義數(shù)據(jù)權(quán)限
	 */
	DATA_SCOPE_CUSTOM("1", "自定義數(shù)據(jù)權(quán)限"),

	/**
	 * 本部門及子級數(shù)據(jù)權(quán)限
	 */
	DATA_SCOPE_DEPT_AND_CHILD("2", "部門及子級數(shù)據(jù)權(quán)限"),

	/**
	 * 本部門數(shù)據(jù)權(quán)限
	 */
	DATA_SCOPE_DEPT("3", "部門數(shù)據(jù)權(quán)限"),

	/**
	 * 本人數(shù)據(jù)權(quán)限
	 */
	DATA_SCOPE_SELF("4", "本人數(shù)據(jù)權(quán)限"),
	;

	/**
	 * 對應(yīng)數(shù)據(jù)庫字典值
	 */
	private final String dbValue;

	/**
	 * 描述
	 */
	private final String description;

}

對于 UserDetails用戶信息我們做了擴(kuò)展。添加了關(guān)于數(shù)據(jù)權(quán)限的字段信息。

public class SxdhcloudUser extends User implements OAuth2AuthenticatedPrincipal {

	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

	/**
	 * 擴(kuò)展屬性,方便存放oauth 上下文相關(guān)信息
	 */
	private final Map<String, Object> attributes = new HashMap<>();

	/**
	 * 租戶ID
	 */
	@Getter
	@JsonSerialize(using = ToStringSerializer.class)
	private final Long tenantId;

	/**
	 * 用戶ID
	 */
	@Getter
	@JsonSerialize(using = ToStringSerializer.class)
	private final Long id;

	/**
	 * 部門ID
	 */
	@Getter
	@JsonSerialize(using = ToStringSerializer.class)
	private final Long deptId;

	/**
	 * 手機號
	 */
	@Getter
	private final String phone;


	/**
	 * 角色數(shù)據(jù)權(quán)限類型,去重
	 */
	@Getter
	private final Set<String> dataScopeTypes;

	/**
	 * 數(shù)據(jù)權(quán)限部門ID集合,去重
	 */
	@Getter
	private final Set<Long> dataScopeDeptIds;

	/**
	 * 數(shù)據(jù)權(quán)限本人ID
	 */
	@Getter
	private final Long dataScopeCreateId;
	
}

用戶在登錄認(rèn)證成功之后,獲取到角色數(shù)據(jù)權(quán)限的相關(guān)數(shù)據(jù),并放到 UserDetails用戶信息中。

1、自定義注解

自定義數(shù)據(jù)權(quán)限注解。

/**
 * 數(shù)據(jù)權(quán)限注解。
 * 可以使用在類上,也可以使用在方法上。
 * - 如果 Mapper類加上注解,表示 Mapper提供的方法以及自定義的方法都會被加上數(shù)據(jù)權(quán)限
 * - 如果 Mapper類的方法加在上注解,表示該方法會被加上數(shù)據(jù)權(quán)限
 * - 如果 Mapper類和其方法同時加上注解,優(yōu)先級為:【類上 > 方法上】
 * - 如果不需要數(shù)據(jù)權(quán)限,可以不加注解,也可以使用 @DataScope(enabled = false)
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataScope {

	/**
	 * 是否生效,默認(rèn)true-生效
	 */
	boolean enabled() default true;

	/**
	 * 表別名
	 */
	String tableAlias() default "";

	/**
	 * 部門限制范圍的字段名稱
	 */
	String deptScopeName() default "dept_id";

	/**
	 * 本人限制范圍的字段名稱
	 */
	String oneselfScopeName() default "create_id";

}

2、自定義處理器

自定義處理器類并實現(xiàn) MultiDataPermissionHandler接口,在 getSqlSegment()方法中處理自定義數(shù)據(jù)權(quán)限邏輯。

注意:mybaits-plus 必須大于 3.5.2版本。

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler;
import com.sxdh.sxdhcloud.common.mybatis.annotation.DataScope;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 數(shù)據(jù)權(quán)限拼裝邏輯處理
 *
 */
public class DataScopeHandler implements MultiDataPermissionHandler {

	/**
	 * 獲取數(shù)據(jù)權(quán)限 SQL 片段。
	 * <p>舊的 {@link MultiDataPermissionHandler#getSqlSegment(Expression, String)} 方法第一個參數(shù)包含所有的 where 條件信息,如果 return 了 null 會覆蓋原有的 where 數(shù)據(jù),</p>
	 * <p>新版的 {@link MultiDataPermissionHandler#getSqlSegment(Table, Expression, String)} 方法不能覆蓋原有的 where 數(shù)據(jù),如果 return 了 null 則表示不追加任何 where 條件</p>
	 *
	 * @param table             所執(zhí)行的數(shù)據(jù)庫表信息,可以通過此參數(shù)獲取表名和表別名
	 * @param where             原有的 where 條件信息
	 * @param mappedStatementId Mybatis MappedStatement Id 根據(jù)該參數(shù)可以判斷具體執(zhí)行方法
	 * @return JSqlParser 條件表達(dá)式,返回的條件表達(dá)式會拼接在原有的表達(dá)式后面(不會覆蓋原有的表達(dá)式)
	 */
	@Override
	public Expression getSqlSegment(Table table, Expression where, String mappedStatementId) {
		try {
			Class<?> mapperClazz = Class.forName(mappedStatementId.substring(0, mappedStatementId.lastIndexOf(".")));
			String methodName = mappedStatementId.substring(mappedStatementId.lastIndexOf(".") + 1);

			/**
			 * DataScope注解優(yōu)先級:【類上 > 方法上】
			 */
			// 獲取 DataScope注解
			DataScope dataScopeAnnotationClazz = mapperClazz.getAnnotation(DataScope.class);
			if (ObjectUtils.isNotEmpty(dataScopeAnnotationClazz) && dataScopeAnnotationClazz.enabled()) {
				return buildDataScopeByAnnotation(dataScopeAnnotationClazz);
			}
			// 獲取自身類中的所有方法,不包括繼承。與訪問權(quán)限無關(guān)
			Method[] methods = mapperClazz.getDeclaredMethods();
			for (Method method : methods) {
				DataScope dataScopeAnnotationMethod = method.getAnnotation(DataScope.class);
				if (ObjectUtils.isEmpty(dataScopeAnnotationMethod) || !dataScopeAnnotationMethod.enabled()) {
					continue;
				}
				if (method.getName().equals(methodName) || (method.getName() + "_COUNT").equals(methodName) || (method.getName() + "_count").equals(methodName)) {
					return buildDataScopeByAnnotation(dataScopeAnnotationMethod);
				}
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * DataScope注解方式,拼裝數(shù)據(jù)權(quán)限
	 *
	 * @param dataScope
	 * @return
	 */
	private Expression buildDataScopeByAnnotation(DataScope dataScope) {
		// 獲取 UserDetails用戶信息
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		if (authentication == null) {
			return null;
		}
		Map<String, Object> userDetailsMap = BeanUtil.beanToMap(authentication.getPrincipal());
		Set<String> dataScopeTypes = (Set<String>) userDetailsMap.get("dataScopeTypes");
		Set<Long> dataScopeDeptIds = (Set<Long>) userDetailsMap.get("dataScopeDeptIds");
		Long dataScopeCreateId = (Long) userDetailsMap.get("dataScopeCreateId");

		// 獲取注解信息
		String tableAlias = dataScope.tableAlias();
		String deptScopeName = dataScope.deptScopeName();
		String oneselfScopeName = dataScope.oneselfScopeName();
		Expression expression = buildDataScopeExpression(tableAlias, deptScopeName, oneselfScopeName, dataScopeDeptIds, dataScopeCreateId);
		return expression == null ? null : new Parenthesis(expression);
	}

	/**
	 * 拼裝數(shù)據(jù)權(quán)限
	 *
	 * @param tableAlias        表別名
	 * @param deptScopeName     部門限制范圍的字段名稱
	 * @param oneselfScopeName  本人限制范圍的字段名稱
	 * @param dataScopeDeptIds  數(shù)據(jù)權(quán)限部門ID集合,去重
	 * @param dataScopeCreateId 數(shù)據(jù)權(quán)限本人ID
	 * @return
	 */
	private Expression buildDataScopeExpression(String tableAlias, String deptScopeName, String oneselfScopeName, Set<Long> dataScopeDeptIds, Long dataScopeCreateId) {
		/**
		 * 構(gòu)造部門in表達(dá)式。
		 */
		InExpression deptIdInExpression = null;
		if (CollectionUtils.isNotEmpty(dataScopeDeptIds)) {
			deptIdInExpression = new InExpression();
			ExpressionList deptIds = new ExpressionList(dataScopeDeptIds.stream().map(LongValue::new).collect(Collectors.toList()));
			// 設(shè)置左邊的字段表達(dá)式,右邊設(shè)置值。
			deptIdInExpression.setLeftExpression(buildColumn(tableAlias, deptScopeName));
			deptIdInExpression.setRightExpression(new Parenthesis(deptIds));
		}

		/**
		 * 構(gòu)造本人eq表達(dá)式
		 */
		EqualsTo oneselfEqualsTo = null;
		if (dataScopeCreateId != null) {
			oneselfEqualsTo = new EqualsTo();
			oneselfEqualsTo.withLeftExpression(buildColumn(tableAlias, oneselfScopeName));
			oneselfEqualsTo.setRightExpression(new LongValue(dataScopeCreateId));
		}

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

	/**
	 * 構(gòu)建Column
	 *
	 * @param tableAlias 表別名
	 * @param columnName 字段名稱
	 * @return 帶表別名字段
	 */
	public static Column buildColumn(String tableAlias, String columnName) {
		if (StringUtils.isNotEmpty(tableAlias)) {
			columnName = tableAlias + "." + columnName;
		}
		return new Column(columnName);
	}

}

3、注冊數(shù)據(jù)權(quán)限攔截器

MybatisPlusConfig配置類中將自定義的處理器注冊到 DataPermissionInterceptor 中。并將攔截器加到MyBatis-Plus插件中。

	@Bean
	public MybatisPlusInterceptor mybatisPlusInterceptor() {
		MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
		// 1.添加數(shù)據(jù)權(quán)限插件
		interceptor.addInnerInterceptor(new DataPermissionInterceptor(new DataScopeHandler()));
		// 2.添加分頁插件
        PaginationInnerInterceptor pageInterceptor = new PaginationInnerInterceptor();
        // 設(shè)置數(shù)據(jù)庫方言類型
        pageInterceptor.setDbType(DbType.MYSQL);
        // 下面配置根據(jù)需求自行設(shè)置
        // 設(shè)置請求的頁面大于最大頁后操作,true調(diào)回到首頁,false繼續(xù)請求。默認(rèn)false
        pageInterceptor.setOverflow(false);
        // 單頁分頁條數(shù)限制,默認(rèn)無限制
        pageInterceptor.setMaxLimit(500L);
        interceptor.addInnerInterceptor(pageInterceptor);
		return interceptor;
	}

4、注解使用

@Mapper
public interface SysUserMapper extends BaseMapper<SysUser> {

	/**
	 * 通過用戶名查詢用戶信息(含有角色信息)
	 *
	 * @param username 用戶名
	 * @return userVo
	 */
	UserVO getUserVoByUsername(String username);

	/**
	 * 分頁查詢用戶信息(含角色)
	 *
	 * @param page      分頁
	 * @param userDTO   查詢參數(shù)
	 * @return list
	 */
	@DataScope(tableAlias = "u")
	IPage<UserVO> getUserVosPage(Page page, @Param("query") UserDTO userDTO, @Param("userIds") List<Long> userIds);

}

代碼中的注釋寫的挺清楚,大家自行理解。

參考文章:

到此這篇關(guān)于MyBatis-Plus數(shù)據(jù)權(quán)限插件的簡單使用的文章就介紹到這了,更多相關(guān)MyBatis-Plus數(shù)據(jù)權(quán)限內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

  • 一文帶你掌握java8中的reduce操作

    一文帶你掌握java8中的reduce操作

    reduce?操作是一種通用的歸約操作,它可以實現(xiàn)從?Stream?中生成一個值,其生成的值不是隨意的,而是根據(jù)指定的計算模型,下面我們就來深入了解下java8中的reduce操作吧
    2023-12-12
  • Java設(shè)計模式之java原型模式詳解

    Java設(shè)計模式之java原型模式詳解

    這篇文章主要介紹了Java設(shè)計模式之原型模式詳解,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-09-09
  • 基于SpringBoot+JWT 實現(xiàn)Token登錄認(rèn)證與登錄人信息查詢功能

    基于SpringBoot+JWT 實現(xiàn)Token登錄認(rèn)證與登錄人信息查詢功能

    本文給大家介紹基于SpringBoot+JWT實現(xiàn)Token登錄認(rèn)證與登錄人信息查詢功能,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值
    2026-03-03
  • 使用SpringAI整合Ollama實現(xiàn)工具鏈調(diào)用功能

    使用SpringAI整合Ollama實現(xiàn)工具鏈調(diào)用功能

    文章介紹了SpringAI的ToolCalling能力,允許大模型自主調(diào)用外部工具以完成多步驟任務(wù),具體而言,通過@Tool注解標(biāo)記方法,SpringAI自動解析生成JSONSchema,@Tools注解注冊多個工具,模型可自主選擇調(diào)用順序和組合,需要的朋友可以參考下
    2026-05-05
  • java向es中寫入數(shù)據(jù)報錯org.elasticsearch.action.ActionReque問題

    java向es中寫入數(shù)據(jù)報錯org.elasticsearch.action.ActionReque問題

    這篇文章主要介紹了java向es中寫入數(shù)據(jù)報錯org.elasticsearch.action.ActionReque問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • Mybatis控制臺打印SQL執(zhí)行信息的方法詳解

    Mybatis控制臺打印SQL執(zhí)行信息的方法詳解

    SQL性能監(jiān)控是一個程序必要的功能,通常我們可以使用數(shù)據(jù)庫自帶的客戶端工具進(jìn)行SQL性能分析,本章節(jié)只實現(xiàn)Mybatis執(zhí)行時對執(zhí)行SQL進(jìn)行攔截,控制臺打印執(zhí)行SQL包括參數(shù)、執(zhí)行方法以及執(zhí)行時間,需要的朋友可以參考下
    2024-11-11
  • 徹底解決IDEA中SpringBoot熱部署無效的問題(推薦)

    徹底解決IDEA中SpringBoot熱部署無效的問題(推薦)

    這篇文章主要介紹了徹底解決IDEA中SpringBoot熱部署無效的問題,本文給大家?guī)韱栴}原因分析通過圖文實例相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2020-09-09
  • 淺談springboot之JoinPoint的getSignature方法

    淺談springboot之JoinPoint的getSignature方法

    這篇文章主要介紹了springboot之JoinPoint的getSignature方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • 詳解spring中使用Elasticsearch的代碼實現(xiàn)

    詳解spring中使用Elasticsearch的代碼實現(xiàn)

    本篇文章主要介紹了詳解spring中使用Elasticsearch的代碼實現(xiàn),具有一定的參考價值,有興趣的可以了解一下
    2017-05-05
  • spring boot中使用http請求的示例代碼

    spring boot中使用http請求的示例代碼

    本篇文章主要介紹了spring boot中 使用http請求的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-12-12

最新評論

曲水县| 米易县| 永福县| 原阳县| 开平市| 拜城县| 洪雅县| 河津市| 蓝田县| 高阳县| 通城县| 土默特右旗| 许昌市| 运城市| 周宁县| 沙河市| 张掖市| 布拖县| 紫金县| 花莲县| 府谷县| 溧阳市| 夏河县| 芷江| 广汉市| 沽源县| 麻江县| 鄂托克旗| 台南市| 特克斯县| 竹北市| 清水河县| 兖州市| 宁津县| 广南县| 宁强县| 奉化市| 苏州市| 二手房| 南昌县| 乌兰察布市|