91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

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

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

Java中怎么利用Mybatis實現數據權限控制

發布時間:2021-07-24 15:00:50 來源:億速云 閱讀:234 作者:Leah 欄目:編程語言

這期內容當中小編將會給大家帶來有關Java中怎么利用Mybatis實現數據權限控制,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

  1. URL訪問資源(接口以及網頁)  界面元素資源(增刪改查導入導出的按鈕,重要的業務數據展示與否等)  數據資源

現在業內普遍的實現方案實際上很粗放,就是單純的“菜單控制”,通過菜單顯示與否來達到控制權限的目的。

我仔細分析過,現在大家做的平臺分為To C和To B兩種:

  1. To C一般不會有太多的復雜權限控制,甚至大部分連菜單控制都不用,全部都可以訪問。  To B一般都不是開放的,只要做好認證關口,能夠進入系統的只有內部員工。大部分企業內部的員工互聯網知識有限,而且作為內部員工不敢對系統進行破壞性的嘗試。

所以針對現在的情況,考慮成本與產出,大部分設計者也不愿意在權限上進行太多的研發力量。

菜單和界面元素一般都是由前端編碼配合存儲數據實現,URL訪問資源的控制也有一些框架比如SpringSecurity,Shiro。

目前我還沒有找到過數據權限控制的框架或者方法,所以自己整理了一份。

數據權限控制原理

數據權限控制最終的效果是會要求在同一個數據請求方法中,根據不同的權限返回不同的數據集,而且無需并且不能由研發編碼控制。這樣大家的第一想法應該就是AOP,攔截所有的底層方法,加入過濾條件。這樣的方式兼容性較強,但是復雜程度也會更高。我們這套系統中,采用的是利用Mybatis的plugin機制,在底層SQL解析時替換增加過濾條件。這樣一套控制機制存在很明顯的優缺點,首先缺點:

  1. 適用性有限,基于底層的Mybatis。  方言有限,針對了某種數據庫(我們使用Mysql),而且由于需要在底層解析處理條件所以有可能造成不同的數據庫不能兼容。當然RedisNoSQL也無法限制。

當然,假如你現在就用Mybatis,而且數據庫使用的是Mysql,這方面就沒有太大影響了。

接下來說說優點:

  1. 減少了接口數量及接口復雜度。原本針對不同的角色,可能會區分不同的接口或者在接口實現時利用流程控制邏輯來區分不同的條件。有了數據權限控制,代碼中只用寫基本邏輯,權限過濾由底層機制自動處理。  提高了數據權限控制的靈活性。例如原本只有主管能查本部門下組織架構/訂單數據,現在新增助理角色,能夠查詢本部門下組織架構,不能查詢訂單。這樣的話普通的寫法就需要調整邏輯控制,使用數據權限控制的話,直接修改配置就好。

數據權限實現

上一節就提及了實現原理,是基于Mybatis的plugins)實現。

MyBatis 允許你在已映射語句執行過程中的某一點進行攔截調用。默認情況下,MyBatis 允許使用插件來攔截的方法調用包括:Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)ParameterHandler (getParameterObject, setParameters)ResultSetHandler (handleResultSets, handleOutputParameters)StatementHandler (prepare, parameterize, batch, update, query)

Mybatis的插件機制目前比較出名的實現應該就是PageHelper項目了,在做這個實現的時候也參考了PageHelper項目的實現方式。所以權限控制插件的類命名為PermissionHelper。

機制是依托于Mybatis的plugins機制,實際SQL處理的時候基于jsqlparser這個包。

設計中包含兩個類,一個是保存角色與權限的實體類命名為PermissionRule,一個是根據實體變更底層SQL語句的主體方法類PermissionHelper。

首先來看下PermissionRule的結構:

public class PermissionRule {private static final Log log = LogFactory.getLog(PermissionRule.class);/*** codeName<br>* 適用角色列表<br>* 格式如: ,RoleA,RoleB,*/private String roles;/*** codeValue<br>* 主實體,多表聯合* 格式如: ,SystemCode,User,*/private String fromEntity;/*** codeDesc<br>* 過濾表達式字段, <br>* <code>{uid}</code>會自動替換為當前用戶的userId<br>* <code>{me}</code> main entity 主實體名稱* <code>{me.a}</code> main entity alias 主實體別名* 格式如:* <ul>* <li>userId = {uid}</li>* <li>(userId = {uid} AND authType > 3)</li>* <li>((userId = {uid} AND authType) > 3 OR (dept in (select dept from depts where manager.id = {uid})))</li>* </ul>*/private String exps;/*** codeShowName<br>* 規則說明*/private String ruleComment;}

看完這個結構,基本能夠理解設計的思路了。數據結構中保存如下幾個字段:

角色列表:需要使用此規則的角色,可以多個,使用英文逗號隔開。  實體列表:對應的規則應用的實體(這里指的是表結構中的表名,可能你的實體是駝峰而數據庫是蛇形,所以這里要放蛇形那個),可以多個,使用英文逗號隔開。  表達式:表達式就是數據權限控制的核心了。簡單的說這里的表達式就是一段SQL語句,其中設置了一些可替換值,底層會用對應運行時的變量替換對應內容,從而達到增加條件的效果。  規則說明:單純的一個說明字段。

核心流程

系統啟動時,首先從數據庫加載出所有的規則。底層利用插件機制來攔截所有的查詢語句,進入查詢攔截方法后,首先根據當前用戶的權限列表篩選出PermissionRule列表,然后循環列表中的規則,對語句中符合實體列表的表進行條件增加,最終生成處理后的SQL語句,退出攔截器,Mybatis執行處理后SQL并返回結果。

講完PermissionRule,再來看看PermissionHelper,首先是頭:

@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})public class PermissionHelper implements Interceptor {}

頭部只是標準的Mybatis攔截器寫法,注解中的Signature決定了你的代碼對哪些方法攔截,update實際上針對修改(Update)、刪除(Delete)生效,query是對查詢(Select)生效。

下面給出針對Select注入查詢條件限制的完整代碼:

private String processSelectSql(String sql, List<PermissionRule> rules, UserDefaultZimpl principal) {try {String replaceSql = null;Select select = (Select) CCJSqlParserUtil.parse(sql);PlainSelect selectBody = (PlainSelect) select.getSelectBody();String mainTable = null;if (selectBody.getFromItem() instanceof Table) {mainTable = ((Table) selectBody.getFromItem()).getName().replace("`", "");} else if (selectBody.getFromItem() instanceof SubSelect) {replaceSql = processSelectSql(((SubSelect) selectBody.getFromItem()).getSelectBody().toString(), rules, principal);}if (!ValidUtil.isEmpty(replaceSql)) {sql = sql.replace(((SubSelect) selectBody.getFromItem()).getSelectBody().toString(), replaceSql);}String mainTableAlias = mainTable;try {mainTableAlias = selectBody.getFromItem().getAlias().getName();} catch (Exception e) {log.debug("當前sql中, " + mainTable + " 沒有設置別名");}String condExpr = null;PermissionRule realRuls = null;for (PermissionRule rule :rules) {for (Object roleStr :principal.getRoles()) {if (rule.getRoles().indexOf("," + roleStr + ",") != -1) {if (rule.getFromEntity().indexOf("," + mainTable + ",") != -1) {// 若主表匹配規則主體,則直接使用本規則realRuls = rule;condExpr = rule.getExps().replace("{uid}", UserDefaultUtil.getUserId().toString()).replace("{bid}", UserDefaultUtil.getBusinessId().toString()).replace("{me}", mainTable).replace("{me.a}", mainTableAlias);if (selectBody.getWhere() == null) {selectBody.setWhere(CCJSqlParserUtil.parseCondExpression(condExpr));} else {AndExpression and = new AndExpression(selectBody.getWhere(), CCJSqlParserUtil.parseCondExpression(condExpr));selectBody.setWhere(and);}}try {String joinTable = null;String joinTableAlias = null;for (Join j :selectBody.getJoins()) {if (rule.getFromEntity().indexOf("," + ((Table) j.getRightItem()).getName() + ",") != -1) {// 當主表不能匹配時,匹配所有join,使用符合條件的第一個表的規則。realRuls = rule;joinTable = ((Table) j.getRightItem()).getName();joinTableAlias = j.getRightItem().getAlias().getName();condExpr = rule.getExps().replace("{uid}", UserDefaultUtil.getUserId().toString()).replace("{bid}", UserDefaultUtil.getBusinessId().toString()).replace("{me}", joinTable).replace("{me.a}", joinTableAlias);if (j.getOnExpression() == null) {j.setOnExpression(CCJSqlParserUtil.parseCondExpression(condExpr));} else {AndExpression and = new AndExpression(j.getOnExpression(), CCJSqlParserUtil.parseCondExpression(condExpr));j.setOnExpression(and);}}}} catch (Exception e) {log.debug("當前sql沒有join的部分!");}}}}if (realRuls == null) return sql; // 沒有合適規則直接退出。if (sql.indexOf("limit ?,?") != -1 && select.toString().indexOf("LIMIT ? OFFSET ?") != -1) {sql = select.toString().replace("LIMIT ? OFFSET ?", "limit ?,?");} else {sql = select.toString();}} catch (JSQLParserException e) {log.error("change sql error .", e);}return sql;}

重點思路

重點其實就在于Sql的解析和條件注入,使用開源項目JSqlParser。

解析出MainTable和JoinTable。from之后跟著的稱為MainTable,join之后跟著的稱為JoinTable。這兩個就是我們PermissionRule需要匹配的表名,PermissionRule::fromEntity字段。  解析出MainTable的where和JoinTable的on后面的條件。使用and連接原本的條件和待注入的條件,PermissionRule::exps字段。  使用當前登錄的用戶信息(放在緩存中),替換條件表達式中的值。  某些情況需要忽略權限,可以考慮使用ThreadLocal(單機)/Redis(集群)來控制。

上述就是小編為大家分享的Java中怎么利用Mybatis實現數據權限控制了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

新田县| 贡觉县| 留坝县| 安吉县| 三门峡市| 来宾市| 临澧县| 泗阳县| 临海市| 达尔| 安化县| 三河市| 灌阳县| 夏津县| 贵定县| 尤溪县| 佛冈县| 新沂市| 新田县| 洛川县| 东光县| 屏南县| 西华县| 出国| 平遥县| 巴彦淖尔市| 介休市| 成安县| 商都县| 金坛市| 余姚市| 巴林左旗| 阜阳市| 长治市| 平安县| 隆子县| 内乡县| 宁波市| 阿瓦提县| 蓝山县| 平定县|