亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

使用MyBatis攔截器實現(xiàn)sql查詢權限動態(tài)修改代碼實例

 更新時間:2023年08月14日 11:42:25   作者:回爐重造P  
這篇文章主要介紹了使用MyBatis攔截器實現(xiàn)sql查詢權限動態(tài)修改代碼實例,為了不耦合,現(xiàn)在的方案是在需要鑒權的Mybatis?Mapper方法上增加一個注解,在運行過程中判斷該注解存在即對sql進行修改,需要的朋友可以參考下

動機和具體情景

最近考慮怎么在Mybatis自動創(chuàng)建sql執(zhí)行過程中進行介入,來不對原有代碼耦合的情況下,實現(xiàn)對sql的修改。

考慮情景,比如多部門管理系統(tǒng),員工工資和賬戶信息敏感,每個部門只能查到對應權限的員工信息。為了實現(xiàn)sql的鑒權,本來是需要將原始的sql語句加上某個權限字段的判斷。

為了不耦合,現(xiàn)在的方案是在需要鑒權的Mybatis Mapper方法上增加一個注解,在運行過程中判斷該注解存在即對sql進行修改,形成新的帶權限字段判斷的sql,這樣對原始代碼的修改就少很多(加個注解就行)。

基本原理和解析流程

Mybatis 允許在映射語句過程中的某一點進行攔截調(diào)用,其提供了基于反射的攔截類 Interceptor 來對方法進行攔截。

這些可攔截的方法存在的原始執(zhí)行類包括: Executor (執(zhí)行器相關), ParameterHandler (參數(shù)處理相關), ResultSetHandler (結果集相關), StatementHandler (sql語法和會話創(chuàng)建相關)四種。

我們的需求是對sql語句進行改寫,選擇對 StatementHandler 進行改寫。

通過反射獲取到此次Mybatis執(zhí)行的原始Mapper接口和方法名,通過判斷我們的自定義注解 @permission 是否存在來選擇鑒權行為,之后從session拿到當前查詢權限,從配置文件中拿到權限可查詢的數(shù)據(jù)范圍,即可對sql進行修改。最后將修改后的sql反射注入回Mybatis的對應執(zhí)行類即可。

具體實現(xiàn)

原始查詢代碼

AccountInfo 賬戶信息類

映射實體類,存放賬戶信息。要注意的是在數(shù)據(jù)庫表中還有一個字段permission表示查詢權限,在實體類中并沒有表示。

public class AccountInfo implements Serializable {
    int id;
    String account;
    String name;
    BigDecimal money;
    public AccountInfo(){}
    public AccountInfo(int id, String account, String name, BigDecimal money){
        this.id = id;
        this.account = account;
        this.name = name;
        this.money = money;
}

AccountMapper 操作Mapper類

@Permission 為自定義注解,表示該方法需要進行鑒權操作,只能查詢當前權限下對應的數(shù)據(jù)信息。

@Mapper
public interface AccountMapper {
    @Permission
    @Select("select * from account")
    public List<AccountInfo> getAccountInfoList();
}

MainController 主要控制類

給出了查詢接口,同時因為設定上權限是存在session中的,給了個模擬賦予權限的接口。

@Controller
public class MainController {
    @Autowired
    AccountMapper accountMapper;
    @RequestMapping("/getAccountInfo")
    @ResponseBody
    public String getAccountInfo() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        List<AccountInfo> accounts  = accountMapper.getAccountInfoList();
        return objectMapper.writeValueAsString(accounts);
    }
    @RequestMapping("/setPermission")
    @ResponseBody
    public String setPermission(HttpServletRequest request, String permission){
        request.getSession().setAttribute("permission", permission);
        return permission;
    }
}

權限相關實現(xiàn)

配置文件設置

增加權限級聯(lián)的設置,為值鍵對形式,表示key能查詢的數(shù)據(jù)范圍。

permission:
  permissionMap:
    develop: "\"develop\""
    advertise: "\"advertise\""
    finance: "\"develop\", \"advertise\", \"finance\""

需要注意的是sql字符串查詢需要使用引號,所以配置文件中需要增加引號轉義,方便后續(xù)sql的使用。

PermissionConfig讀取配置文件

Map形式不能直接讀取,增加對應的讀取config類,主要其中的成員名稱需要和配置文件中對應(這邊為permissionMap)。

// 從配置文件中讀取permission層次范圍
@Configuration
@ConfigurationProperties(prefix = "permission")
@EnableConfigurationProperties(PermissionConfig.class)
public class PermissionConfig {
    private Map<String, String> permissionMap = new HashMap<>();
    public Map<String, String> getPermissionMap() {
        return permissionMap;
    }
    public void setPermissionMap(Map<String, String> permissionMap) {
        this.permissionMap = permissionMap;
    }
}

自定義注解

注解只是為了判斷是否需要鑒權,不需要特殊的成員。同時設置 retention 為 runtime ,并設置作用對象為方法 method 。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
// 指名數(shù)據(jù)庫查詢方法需要和權限掛鉤
public @interface Permission {}

Mybatis攔截器實現(xiàn)

主要的實現(xiàn)集中在 PermissionInterceptor 中,主要可分為幾個部分:攔截配置,元數(shù)據(jù)獲取,自定義注解判斷,權限獲取與sql修改,反射注入。主要邏輯集中在 intercept 方法中。

攔截配置

主要是通過 @Intercepts 注解對攔截器類需要攔截的Handler和方法進行設置,方便之后反射獲取對應的類。我們這邊是對語句的最終sql進行處理,選擇StatementHandler中的 prepare 方法進行攔截,args中為方法參數(shù)類型來判斷重載。如果是對query進行攔截,后續(xù)注入時其實已經(jīng)執(zhí)行了,新的sql并不會被調(diào)用。

@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
@Component
public class PermissionInterceptor implements Interceptor {

元數(shù)據(jù)獲取

用的Mybatis給的元數(shù)據(jù)類MetaObject進行獲取。

    private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
    private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
    private static final ReflectorFactory REFLECTOR_FACTORY = new DefaultReflectorFactory();
    @Autowired
    private PermissionConfig permissionConfig;
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 獲取sql信息
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        BoundSql boundSql = statementHandler.getBoundSql();
        String sql = boundSql.getSql();
        System.out.println("原sql為: " + sql);
        // 獲取元數(shù)據(jù)
        MetaObject metaResultSetHandler = MetaObject.forObject(statementHandler, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, REFLECTOR_FACTORY);
        MappedStatement mappedStatement = (MappedStatement) metaResultSetHandler.getValue("delegate.mappedStatement");

MappedStatement 在 對應的Handler 的 delegate.mappedStatement 屬性對象中,包含元數(shù)據(jù)信息。 獲取類和方法信息。

        // 獲取調(diào)用方法
        String id = mappedStatement.getId();
        String className = id.substring(0, id.lastIndexOf("."));
        String methodName = id.substring(id.lastIndexOf(".") + 1);
        System.out.println("調(diào)用方法為: " + id);

注解判斷

反射獲取對應方法的注解列表即可。

        // 注解查詢
        Class clazz = Class.forName(className);
        Method method = clazz.getDeclaredMethod(methodName);
        boolean needPermission = method.isAnnotationPresent(Permission.class);
        // 對注解方法進行權限處理
        if(needPermission){
            System.out.println("需要進行sql權限變化");

獲取權限信息并進行sql修改

配置文件中獲取權限范圍信息,增加到原sql的條件判斷中。

// 獲取權限信息
	HttpSession session = HttpUtil.getSession();
	String permission = (String) session.getAttribute("permission");
	Map<String, String> map = permissionConfig.getPermissionMap();
	for(String key:map.keySet()){
		System.out.println(key);
	}
	String canSelectPermission = null;
	if(map.containsKey(permission)){
		canSelectPermission = map.get(permission);
	}
	System.out.printf("當前權限:%s, 可查詢范圍:%s%n", permission, canSelectPermission);
// 修改sql
	String newSql = String.format("select * from (%s) `range` where `range`.permission in (%s)", sql, canSelectPermission);
	// String newSql = "select * from account where permission in (\"advertise\")";
	System.out.println("修改后的sql為: " + newSql);

反射注入并執(zhí)行

注入到BoundSql類中,替換原sql。

  // 反射修改handler中的sql以執(zhí)行
  Class boundClass = boundSql.getClass();
  Field field = boundClass.getDeclaredField("sql");
  field.setAccessible(true);
  field.set(boundSql, newSql);

效果展示

數(shù)據(jù)庫簡單數(shù)據(jù)

包括一個查詢權限字段

在這里插入圖片描述

給予權限信息

Session中寫入權限。

在這里插入圖片描述

不同權限下獲取的信息結果

Finance:

在這里插入圖片描述

在這里插入圖片描述

Develop:

在這里插入圖片描述

在這里插入圖片描述

總結

一個簡單的Mybatis的攔截器嘗試,用于對sql依靠查詢權限進行動態(tài)修改。

主要就是Mybatis這個MetaObject需要知道對應的statement的value才能反射拿到,查了好久才發(fā)現(xiàn)是delegate.mappedStatement,很神秘。

到此這篇關于使用MyBatis攔截器實現(xiàn)sql查詢權限動態(tài)修改代碼實例的文章就介紹到這了,更多相關MyBatis攔截器動態(tài)修改sql內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 利用session實現(xiàn)簡單購物車功能

    利用session實現(xiàn)簡單購物車功能

    這篇文章主要為大家詳細介紹了利用session實現(xiàn)簡單購物車功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • 淺談Java自動裝箱與拆箱及其陷阱

    淺談Java自動裝箱與拆箱及其陷阱

    下面小編就為大家?guī)硪黄獪\談Java自動裝箱與拆箱及其陷阱。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-06-06
  • springboot+springJdbc+postgresql 實現(xiàn)多數(shù)據(jù)源的配置

    springboot+springJdbc+postgresql 實現(xiàn)多數(shù)據(jù)源的配置

    本文主要介紹了springboot+springJdbc+postgresql 實現(xiàn)多數(shù)據(jù)源的配置,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • JavaWeb會話技術詳解與案例

    JavaWeb會話技術詳解與案例

    會話技術:在Web開發(fā)中,服務器跟蹤用戶信息的奇數(shù)稱為會話技術。會話:指的是一個客戶端與服務器發(fā)生的一系列請求和響應的過程。由于請求包含的信息,在請求被銷毀后也就不存在,多次讓用戶輸入賬號密碼,會影響用戶的使用體驗感,基于此,產(chǎn)生了cookie和session技術
    2021-11-11
  • Java虛擬機堆內(nèi)存溢出的原因和解決方法

    Java虛擬機堆內(nèi)存溢出的原因和解決方法

    在Java開發(fā)中,內(nèi)存溢出(OutOfMemoryError)是一個常見的問題,尤其是在處理大量數(shù)據(jù)或長時間運行的應用時,本文將通過一個簡單的示例,展示如何通過JVM參數(shù)和代碼分析來理解和解決內(nèi)存溢出問題,需要的朋友可以參考下
    2024-10-10
  • 在Java的Struts框架下進行web編程的入門教程

    在Java的Struts框架下進行web編程的入門教程

    這篇文章主要介紹了在Java的Struts框架下進行web編程的入門教程,需要的朋友可以參考下
    2015-11-11
  • spring在service層的方法報錯事務不會回滾的解決

    spring在service層的方法報錯事務不會回滾的解決

    這篇文章主要介紹了spring在service層的方法報錯事務不會回滾的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Java 判斷實體對象及所有屬性是否為空的操作

    Java 判斷實體對象及所有屬性是否為空的操作

    這篇文章主要介紹了Java 判斷實體對象及所有屬性是否為空的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • springboot 通過代碼自動生成pid的方法

    springboot 通過代碼自動生成pid的方法

    這篇文章主要介紹了springboot 通過代碼自動生成pid的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07
  • 最長公共子序列問題的深度分析與Java實現(xiàn)方式

    最長公共子序列問題的深度分析與Java實現(xiàn)方式

    本文詳細介紹了最長公共子序列(LCS)問題,包括其概念、暴力解法、動態(tài)規(guī)劃解法,并提供了Java代碼實現(xiàn),暴力解法雖然簡單,但在大數(shù)據(jù)處理中效率較低,動態(tài)規(guī)劃解法通過構建DP表,顯著提高了計算效率,適用于大規(guī)模數(shù)據(jù)處理
    2025-02-02

最新評論