您好,登錄后才能下訂單哦!
這篇文章主要講解了“怎么通過MyBatis自定義插件實現(xiàn)簡易數(shù)據(jù)權(quán)限”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“怎么通過MyBatis自定義插件實現(xiàn)簡易數(shù)據(jù)權(quán)限”吧!
一說到mybatis插件,都會想到mybatis的分頁插件。的確現(xiàn)在開發(fā)中用mybatis的話,一般都會用到它。它能在我們的sql語句后面添加分頁查詢條件,達(dá)到分頁查詢的效果。
由于mybatis是基于xml插件配置的所以,我們要在xml中配置自己的插件
<plugins> <plugin interceptor="com.wj.Interceptor.DataScopeInterceptor" /> //自定義插件的全類名 </plugins>
先看看一張圖 ,mybatis主要提供了下面4個接口支持?jǐn)r截
編寫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í)行。
項目開發(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)。(這塊本來就沒有同一的處理方案)。
我這里將數(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)
數(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; } }
下面在自定義插件中編寫業(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
大概的簡易數(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)注!
免責(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)容。