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

使用MyBatis攔截器實(shí)現(xiàn)sql查詢(xún)權(quán)限動(dòng)態(tài)修改代碼實(shí)例

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

動(dòng)機(jī)和具體情景

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

考慮情景,比如多部門(mén)管理系統(tǒng),員工工資和賬戶(hù)信息敏感,每個(gè)部門(mén)只能查到對(duì)應(yīng)權(quán)限的員工信息。為了實(shí)現(xiàn)sql的鑒權(quán),本來(lái)是需要將原始的sql語(yǔ)句加上某個(gè)權(quán)限字段的判斷。

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

基本原理和解析流程

Mybatis 允許在映射語(yǔ)句過(guò)程中的某一點(diǎn)進(jìn)行攔截調(diào)用,其提供了基于反射的攔截類(lèi) Interceptor 來(lái)對(duì)方法進(jìn)行攔截。

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

我們的需求是對(duì)sql語(yǔ)句進(jìn)行改寫(xiě),選擇對(duì) StatementHandler 進(jìn)行改寫(xiě)。

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

具體實(shí)現(xiàn)

原始查詢(xún)代碼

AccountInfo 賬戶(hù)信息類(lèi)

映射實(shí)體類(lèi),存放賬戶(hù)信息。要注意的是在數(shù)據(jù)庫(kù)表中還有一個(gè)字段permission表示查詢(xún)權(quán)限,在實(shí)體類(lèi)中并沒(méi)有表示。

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類(lèi)

@Permission 為自定義注解,表示該方法需要進(jìn)行鑒權(quán)操作,只能查詢(xún)當(dāng)前權(quán)限下對(duì)應(yīng)的數(shù)據(jù)信息。

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

MainController 主要控制類(lèi)

給出了查詢(xún)接口,同時(shí)因?yàn)樵O(shè)定上權(quán)限是存在session中的,給了個(gè)模擬賦予權(quán)限的接口。

@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;
    }
}

權(quán)限相關(guān)實(shí)現(xiàn)

配置文件設(shè)置

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

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

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

PermissionConfig讀取配置文件

Map形式不能直接讀取,增加對(duì)應(yīng)的讀取config類(lèi),主要其中的成員名稱(chēng)需要和配置文件中對(duì)應(yīng)(這邊為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;
    }
}

自定義注解

注解只是為了判斷是否需要鑒權(quán),不需要特殊的成員。同時(shí)設(shè)置 retention 為 runtime ,并設(shè)置作用對(duì)象為方法 method 。

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

Mybatis攔截器實(shí)現(xiàn)

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

攔截配置

主要是通過(guò) @Intercepts 注解對(duì)攔截器類(lèi)需要攔截的Handler和方法進(jìn)行設(shè)置,方便之后反射獲取對(duì)應(yīng)的類(lèi)。我們這邊是對(duì)語(yǔ)句的最終sql進(jìn)行處理,選擇StatementHandler中的 prepare 方法進(jìn)行攔截,args中為方法參數(shù)類(lèi)型來(lái)判斷重載。如果是對(duì)query進(jìn)行攔截,后續(xù)注入時(shí)其實(shí)已經(jīng)執(zhí)行了,新的sql并不會(huì)被調(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ù)類(lèi)MetaObject進(jìn)行獲取。

    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 在 對(duì)應(yīng)的Handler 的 delegate.mappedStatement 屬性對(duì)象中,包含元數(shù)據(jù)信息。 獲取類(lèi)和方法信息。

        // 獲取調(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);

注解判斷

反射獲取對(duì)應(yīng)方法的注解列表即可。

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

獲取權(quán)限信息并進(jìn)行sql修改

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

// 獲取權(quán)限信息
	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("當(dāng)前權(quán)限:%s, 可查詢(xún)范圍:%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類(lèi)中,替換原sql。

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

效果展示

數(shù)據(jù)庫(kù)簡(jiǎn)單數(shù)據(jù)

包括一個(gè)查詢(xún)權(quán)限字段

在這里插入圖片描述

給予權(quán)限信息

Session中寫(xiě)入權(quán)限。

在這里插入圖片描述

不同權(quán)限下獲取的信息結(jié)果

Finance:

在這里插入圖片描述

在這里插入圖片描述

Develop:

在這里插入圖片描述

在這里插入圖片描述

總結(jié)

一個(gè)簡(jiǎn)單的Mybatis的攔截器嘗試,用于對(duì)sql依靠查詢(xún)權(quán)限進(jìn)行動(dòng)態(tài)修改。

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

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

相關(guān)文章

  • 詳談Array和ArrayList的區(qū)別與聯(lián)系

    詳談Array和ArrayList的區(qū)別與聯(lián)系

    下面小編就為大家?guī)?lái)一篇詳談Array和ArrayList的區(qū)別與聯(lián)系。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-06-06
  • 創(chuàng)建網(wǎng)關(guān)項(xiàng)目(Spring Cloud Gateway)過(guò)程詳解

    創(chuàng)建網(wǎng)關(guān)項(xiàng)目(Spring Cloud Gateway)過(guò)程詳解

    這篇文章主要介紹了創(chuàng)建網(wǎng)關(guān)項(xiàng)目(Spring Cloud Gateway)過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09
  • JDK8配置環(huán)境變量的bat文件的詳細(xì)教程

    JDK8配置環(huán)境變量的bat文件的詳細(xì)教程

    這篇文章主要介紹了JDK8配置環(huán)境變量的bat文件,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • 解析Spring中@Controller@Service等線程安全問(wèn)題

    解析Spring中@Controller@Service等線程安全問(wèn)題

    這篇文章主要為大家介紹解析了Spring中@Controller@Service等線程的安全問(wèn)題,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-03-03
  • Java如何使用Agent和ASM在字節(jié)碼層面實(shí)現(xiàn)方法攔截

    Java如何使用Agent和ASM在字節(jié)碼層面實(shí)現(xiàn)方法攔截

    Agent是一種運(yùn)行在 Java 虛擬機(jī) (JVM) 上的特殊程序,ASM是一個(gè)輕量級(jí)的 Java 字節(jié)碼編輯和分析框架,本文為大家介紹了如何利用他們?cè)谧止?jié)碼層面實(shí)現(xiàn)方法攔截,感興趣的可以了解一下
    2023-05-05
  • SpringBoot Admin與Prometheus集成監(jiān)控

    SpringBoot Admin與Prometheus集成監(jiān)控

    本文深入探討Spring Boot Admin與Prometheus的集成監(jiān)控方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2026-01-01
  • Java多線程生產(chǎn)者消費(fèi)者模式實(shí)現(xiàn)過(guò)程解析

    Java多線程生產(chǎn)者消費(fèi)者模式實(shí)現(xiàn)過(guò)程解析

    這篇文章主要介紹了Java多線程生產(chǎn)者消費(fèi)者模式實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • maven settings.xml文件的存放及配置(包含了配置阿里云鏡像)

    maven settings.xml文件的存放及配置(包含了配置阿里云鏡像)

    本文詳細(xì)解釋了Maven中settings.xml文件的存放位置,以及用戶(hù)級(jí)別和全局級(jí)別的區(qū)別,重點(diǎn)介紹了localRepository、交互模式、離線模式、插件組、代理設(shè)置、服務(wù)器認(rèn)證、鏡像列表和激活profiles的使用方法,感興趣的可以了解一下
    2025-09-09
  • Java中的clone()和Cloneable接口實(shí)例

    Java中的clone()和Cloneable接口實(shí)例

    這篇文章主要介紹了Java中的clone()和Cloneable接口實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java數(shù)據(jù)結(jié)構(gòu)之平衡二叉樹(shù)的實(shí)現(xiàn)詳解

    Java數(shù)據(jù)結(jié)構(gòu)之平衡二叉樹(shù)的實(shí)現(xiàn)詳解

    平衡二叉樹(shù)又被稱(chēng)為AVL樹(shù)(有別于AVL算法),且具有以下性質(zhì):它是一棵空樹(shù)或它的左右兩個(gè)子樹(shù)的高度差的絕對(duì)值不超過(guò)1,并且左右兩個(gè)子樹(shù)都是一棵平衡二叉樹(shù)。本文將詳解介紹一下平衡二叉樹(shù)的原理與實(shí)現(xiàn),需要的可以參考一下
    2022-03-03

最新評(píng)論

三门县| 屏边| 六盘水市| 昂仁县| 华池县| 沅陵县| 集安市| 道真| 团风县| 龙川县| 崇文区| 庆城县| 天峻县| 海兴县| 泰来县| 五大连池市| 雷州市| 清远市| 多伦县| 静安区| 新疆| 修文县| 昭平县| 荣昌县| 弋阳县| 长葛市| 普格县| 抚顺市| 习水县| 霍林郭勒市| 阿荣旗| 申扎县| 保山市| 扬州市| 那坡县| 龙岩市| 琼结县| 新宾| 崇义县| 闵行区| 台南县|