溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么通過MyBatis自定義插件實現(xiàn)簡易數(shù)據(jù)權(quán)限

發(fā)布時間:2021-09-07 10:46:28 來源:億速云 閱讀:200 作者:chen 欄目:大數(shù)據(jù)

這篇文章主要講解了“怎么通過MyBatis自定義插件實現(xiàn)簡易數(shù)據(jù)權(quán)限”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“怎么通過MyBatis自定義插件實現(xiàn)簡易數(shù)據(jù)權(quán)限”吧!

1.mybatis自定義插件

一說到mybatis插件,都會想到mybatis的分頁插件。的確現(xiàn)在開發(fā)中用mybatis的話,一般都會用到它。它能在我們的sql語句后面添加分頁查詢條件,達(dá)到分頁查詢的效果。

①配置

由于mybatis是基于xml插件配置的所以,我們要在xml中配置自己的插件

<plugins>
    <plugin interceptor="com.wj.Interceptor.DataScopeInterceptor" /> //自定義插件的全類名
</plugins>
②接口的攔截

先看看一張圖 ,mybatis主要提供了下面4個接口支持?jǐn)r截 怎么通過MyBatis自定義插件實現(xiàn)簡易數(shù)據(jù)權(quán)限
編寫DataScopeInterceptor(需要實現(xiàn)Interceptor接口)

@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class DataScopeInterceptor implements Interceptor {
    // 這里是每次執(zhí)行操作的時候,都會進(jìn)行這個攔截器的方法內(nèi)
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //TODO:自已的業(yè)務(wù)處理
        return invocation.proceed();
    }
    // 主要是為了把這個攔截器生成一個代理放到攔截器鏈中
    @Override
    public Object plugin(Object o) {

        return Plugin.wrap(o, this);
    }

    // 插件初始化的時候調(diào)用,也只調(diào)用一次,插件配置的屬性從這里設(shè)置進(jìn)來
    @Override
    public void setProperties(Properties properties) {

    }
}

看看這個注解

@Signature(
    type = StatementHandler.class, //這是指攔截哪個接口
    method = "prepare", //這個是攔截接口中的什么方法,可以在StatementHandler中找到此方法
    args = {Connection.class, Integer.class}//攔截方法的參數(shù)列表)

下面我們啟動項目,執(zhí)行sql查詢的時候會進(jìn)入到我們的插件中。現(xiàn)在算是自己定義好了一個空的插件了。

ps:mybatis可以同時定義多個插件,這些插件采用責(zé)任鏈模式,通過代理對象一個一個調(diào)用下一個插件,進(jìn)行執(zhí)行。

2.數(shù)據(jù)權(quán)限(僅以查詢?yōu)槔?/h4>

項目開發(fā)中,大多項目都有權(quán)限相關(guān)的考慮,說到權(quán)限,大體分為兩類,數(shù)據(jù)權(quán)限與功能權(quán)限。數(shù)據(jù)權(quán)限一般是針對不同的角色或者用戶,查詢到不同的數(shù)據(jù),對同一數(shù)據(jù)表或者數(shù)據(jù)源的不同條件篩選。而功能權(quán)限更多是對功能菜單的訪問權(quán)限。在不同項目中,權(quán)限設(shè)計也不同,跟系統(tǒng)業(yè)務(wù),架構(gòu)設(shè)計息息相關(guān),做好權(quán)限,是非常困難的。我這里僅僅是對數(shù)據(jù)權(quán)限的一個模擬。不涉及具體實現(xiàn)。(這塊本來就沒有同一的處理方案)。

①授權(quán)的分類

我這里將數(shù)據(jù)權(quán)限分為兩個類別,明細(xì)授權(quán),條件授權(quán)
明細(xì)授權(quán):對一個表數(shù)據(jù),通過id進(jìn)行授權(quán),比如有個商品表,可以把指定商品的id授權(quán)給指定用戶或角色,達(dá)到該用戶或角色可以查詢到這一個指定商品。

select * from product where id = 1

條件授權(quán):可以通過指定范圍查詢條件為某一用戶或角色進(jìn)行授權(quán)

select * from product where name like '%iphone%'

上面的兩個sql很明顯的表現(xiàn)了這兩種授權(quán)方式的區(qū)別與聯(lián)系。我們不免會想,明細(xì)授權(quán)也可以用條件授權(quán)來實現(xiàn),達(dá)到相同的效果,的確是這樣,我們這里僅僅說明兩天不同的授權(quán)方式,其實在真實的環(huán)境中,可能還會有其他的授權(quán)形式。

上面我們通過對mybatis插件的自定義,我們就可以對sql進(jìn)行處理了。所以,數(shù)據(jù)權(quán)限無非就是在自定義插件中獲取到sql。在sql語句后面拼接不同的過濾條件來達(dá)到數(shù)據(jù)過濾。真實業(yè)務(wù)場景更為復(fù)雜。很多時候,不僅僅是拼接這么簡單。對sql的處理也是一件廢功夫的事情。下面看具體實現(xiàn)

②不同授權(quán)類型實現(xiàn)

數(shù)據(jù)權(quán)限接口,這里只寫了一個getsql的方法定義,參數(shù)為原始sql,返回值為經(jīng)過處理后的sql

public interface DataScopeInterface {

    /**
     * 獲取sql
     * @return
     */
    String getSql(String sql);

}

DataScopeInterface有兩個不同的實現(xiàn)類,明細(xì)與條件

@Component("grantDataScope")
@Slf4j
public class GrantDataScope implements DataScopeInterface {
    //明細(xì)授權(quán)實現(xiàn)
    @Override
    public String getSql(String sql) {
        //這里是獲取當(dāng)前登錄用戶的id,我定義了一個登錄攔截器,通過ThreadLocal保存當(dāng)前登錄的用戶。代碼就不寫出來了。比較簡單
        String userId = LoginHandlerInterceptor.userLoginThreadLocal.get();
        //這里模擬用戶id為1的用戶在查詢語句后面拼接id為1的過濾條件
        if("1".equals(userId)){
            if(sql.toLowerCase().contains("where")){
                sql += ">

條件授權(quán)

@Component("ruleDataScope")
@Slf4j
public class RuleDataScope implements DataScopeInterface {
    // 條件授權(quán)實現(xiàn)
    public String getSql(String sql) {
        String userId = LoginHandlerInterceptor.userLoginThreadLocal.get();
        //模擬用戶為id為2的用戶加name中有ipone的數(shù)據(jù)
        if("2".equals(userId)){
            if(sql.toLowerCase().contains("where")){
                sql += " and name like '%iphone%'";
            }else {
                sql += " where name like '%iphone%'";
            }
        }
        log.info("rule: "+ sql);
        return sql;
    }
}
③插件邏輯實現(xiàn)

下面在自定義插件中編寫業(yè)務(wù)具體實現(xiàn)邏輯。由于我們不同的授權(quán)類型都是交給spring管理的bean,我們可以借此通過策略模式來實現(xiàn)授權(quán)規(guī)則的可擴展性。有不同的授權(quán)規(guī)則只需要實現(xiàn)DataScopeInterface接口即可。

public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
        String userName = LoginHandlerInterceptor.userLoginThreadLocal.get();
        //  sql語句類型
        Object sqlCommandType = metaStatementHandler.getValue("delegate.mappedStatement.sqlCommandType");
        //只考慮了查詢
        if (SqlCommandType.SELECT.equals(sqlCommandType)) {
            //獲取sql
            String sql = String.valueOf(metaStatementHandler.getValue("delegate.boundSql.sql"));
            //獲取DataScopeInterface的實現(xiàn)類集合,并循環(huán)執(zhí)行sql的格式化
            Map<String, DataScopeInterface> dataScopeInterfaceMap = SpringContextUtils.getBeanOfType(DataScopeInterface.class);
            Collection<DataScopeInterface> dataScopeInterfaces = dataScopeInterfaceMap.values();
            for (DataScopeInterface dataScopeInterface : dataScopeInterfaces) {
                sql = dataScopeInterface.getSql(sql);
            }
            log.info("sql --> " + sql);
            //重新設(shè)置sql
            metaStatementHandler.setValue("delegate.boundSql.sql", sql);
        }
        return invocation.proceed();
}
⑤驗證

啟動項目,模擬用戶id為1的用戶登錄,返回了一條記錄,達(dá)到了過濾的效果。

2019-08-24 14:07:05,496 DEBUG (BaseJdbcLogger.java:145)- ==>  Preparing: select * from product where id = 1 
2019-08-24 14:07:05,496 DEBUG (BaseJdbcLogger.java:145)- ==> Parameters: 
2019-08-24 14:07:05,498 DEBUG (BaseJdbcLogger.java:145)- <==      Total: 1

同理,模擬用戶id為2的用戶登錄。返回兩條記錄

2019-08-24 14:09:59,006 DEBUG (BaseJdbcLogger.java:145)- ==>  Preparing: select * from product where where name like '%iphone%' 
2019-08-24 14:09:59,017 DEBUG (BaseJdbcLogger.java:145)- ==> Parameters: 
2019-08-24 14:09:59,020 DEBUG (BaseJdbcLogger.java:145)- <==      Total: 2
⑥真實業(yè)務(wù)場景下的考慮的問題

大概的簡易數(shù)據(jù)權(quán)限就這樣了,下面多談?wù)勗谡鎸崢I(yè)務(wù)場景下的一些情況:
1,不同的授權(quán)類型都是在數(shù)據(jù)庫中進(jìn)行維護(hù),在不同的實現(xiàn)中,讀取數(shù)據(jù)庫的權(quán)限配置信息,進(jìn)而封裝sql
2,存在多表關(guān)聯(lián)的時候,或非驅(qū)動表控制了數(shù)據(jù)權(quán)限,驅(qū)動表沒有控制,根據(jù)不同的業(yè)務(wù)場景具體分析,對sql的處理也就更加困難了。
3,在分布式中存在跨系統(tǒng)問題的數(shù)據(jù)權(quán)限控制。得看情況,做方案。

感謝各位的閱讀,以上就是“怎么通過MyBatis自定義插件實現(xiàn)簡易數(shù)據(jù)權(quán)限”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對怎么通過MyBatis自定義插件實現(xiàn)簡易數(shù)據(jù)權(quán)限這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI