真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Java中怎么利用Mybatis實(shí)現(xiàn)數(shù)據(jù)權(quán)限控制

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)Java中怎么利用Mybatis實(shí)現(xiàn)數(shù)據(jù)權(quán)限控制,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

創(chuàng)新互聯(lián)公司是一家專注網(wǎng)站建設(shè)、網(wǎng)絡(luò)營(yíng)銷策劃、微信小程序開(kāi)發(fā)、電子商務(wù)建設(shè)、網(wǎng)絡(luò)推廣、移動(dòng)互聯(lián)開(kāi)發(fā)、研究、服務(wù)為一體的技術(shù)型公司。公司成立10年以來(lái),已經(jīng)為上千余家鑿毛機(jī)各業(yè)的企業(yè)公司提供互聯(lián)網(wǎng)服務(wù)?,F(xiàn)在,服務(wù)的上千余家客戶與我們一路同行,見(jiàn)證我們的成長(zhǎng);未來(lái),我們一起分享成功的喜悅。

  1. URL訪問(wèn)資源(接口以及網(wǎng)頁(yè))  界面元素資源(增刪改查導(dǎo)入導(dǎo)出的按鈕,重要的業(yè)務(wù)數(shù)據(jù)展示與否等)  數(shù)據(jù)資源

現(xiàn)在業(yè)內(nèi)普遍的實(shí)現(xiàn)方案實(shí)際上很粗放,就是單純的“菜單控制”,通過(guò)菜單顯示與否來(lái)達(dá)到控制權(quán)限的目的。

我仔細(xì)分析過(guò),現(xiàn)在大家做的平臺(tái)分為To C和To B兩種:

  1. To C一般不會(huì)有太多的復(fù)雜權(quán)限控制,甚至大部分連菜單控制都不用,全部都可以訪問(wèn)。  To B一般都不是開(kāi)放的,只要做好認(rèn)證關(guān)口,能夠進(jìn)入系統(tǒng)的只有內(nèi)部員工。大部分企業(yè)內(nèi)部的員工互聯(lián)網(wǎng)知識(shí)有限,而且作為內(nèi)部員工不敢對(duì)系統(tǒng)進(jìn)行破壞性的嘗試。

所以針對(duì)現(xiàn)在的情況,考慮成本與產(chǎn)出,大部分設(shè)計(jì)者也不愿意在權(quán)限上進(jìn)行太多的研發(fā)力量。

菜單和界面元素一般都是由前端編碼配合存儲(chǔ)數(shù)據(jù)實(shí)現(xiàn),URL訪問(wèn)資源的控制也有一些框架比如SpringSecurity,Shiro。

目前我還沒(méi)有找到過(guò)數(shù)據(jù)權(quán)限控制的框架或者方法,所以自己整理了一份。

數(shù)據(jù)權(quán)限控制原理

數(shù)據(jù)權(quán)限控制最終的效果是會(huì)要求在同一個(gè)數(shù)據(jù)請(qǐng)求方法中,根據(jù)不同的權(quán)限返回不同的數(shù)據(jù)集,而且無(wú)需并且不能由研發(fā)編碼控制。這樣大家的第一想法應(yīng)該就是AOP,攔截所有的底層方法,加入過(guò)濾條件。這樣的方式兼容性較強(qiáng),但是復(fù)雜程度也會(huì)更高。我們這套系統(tǒng)中,采用的是利用Mybatis的plugin機(jī)制,在底層SQL解析時(shí)替換增加過(guò)濾條件。這樣一套控制機(jī)制存在很明顯的優(yōu)缺點(diǎn),首先缺點(diǎn):

  1. 適用性有限,基于底層的Mybatis。  方言有限,針對(duì)了某種數(shù)據(jù)庫(kù)(我們使用MySQL),而且由于需要在底層解析處理?xiàng)l件所以有可能造成不同的數(shù)據(jù)庫(kù)不能兼容。當(dāng)然redis和NOSQL也無(wú)法限制。

當(dāng)然,假如你現(xiàn)在就用Mybatis,而且數(shù)據(jù)庫(kù)使用的是Mysql,這方面就沒(méi)有太大影響了。

接下來(lái)說(shuō)說(shuō)優(yōu)點(diǎn):

  1. 減少了接口數(shù)量及接口復(fù)雜度。原本針對(duì)不同的角色,可能會(huì)區(qū)分不同的接口或者在接口實(shí)現(xiàn)時(shí)利用流程控制邏輯來(lái)區(qū)分不同的條件。有了數(shù)據(jù)權(quán)限控制,代碼中只用寫基本邏輯,權(quán)限過(guò)濾由底層機(jī)制自動(dòng)處理。  提高了數(shù)據(jù)權(quán)限控制的靈活性。例如原本只有主管能查本部門下組織架構(gòu)/訂單數(shù)據(jù),現(xiàn)在新增助理角色,能夠查詢本部門下組織架構(gòu),不能查詢訂單。這樣的話普通的寫法就需要調(diào)整邏輯控制,使用數(shù)據(jù)權(quán)限控制的話,直接修改配置就好。

數(shù)據(jù)權(quán)限實(shí)現(xiàn)

上一節(jié)就提及了實(shí)現(xiàn)原理,是基于Mybatis的plugins)實(shí)現(xiàn)。

MyBatis 允許你在已映射語(yǔ)句執(zhí)行過(guò)程中的某一點(diǎn)進(jìn)行攔截調(diào)用。默認(rèn)情況下,MyBatis 允許使用插件來(lái)攔截的方法調(diào)用包括:Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)ParameterHandler (getParameterObject, setParameters)ResultSetHandler (handleResultSets, handleOutputParameters)StatementHandler (prepare, parameterize, batch, update, query)

Mybatis的插件機(jī)制目前比較出名的實(shí)現(xiàn)應(yīng)該就是PageHelper項(xiàng)目了,在做這個(gè)實(shí)現(xiàn)的時(shí)候也參考了PageHelper項(xiàng)目的實(shí)現(xiàn)方式。所以權(quán)限控制插件的類命名為PermissionHelper。

機(jī)制是依托于Mybatis的plugins機(jī)制,實(shí)際SQL處理的時(shí)候基于jsqlparser這個(gè)包。

設(shè)計(jì)中包含兩個(gè)類,一個(gè)是保存角色與權(quán)限的實(shí)體類命名為PermissionRule,一個(gè)是根據(jù)實(shí)體變更底層SQL語(yǔ)句的主體方法類PermissionHelper。

首先來(lái)看下PermissionRule的結(jié)構(gòu):

public class PermissionRule {private static final Log log = LogFactory.getLog(PermissionRule.class);/*** codeName
* 適用角色列表
* 格式如: ,RoleA,RoleB,*/private String roles;/*** codeValue
* 主實(shí)體,多表聯(lián)合* 格式如: ,SystemCode,User,*/private String fromEntity;/*** codeDesc
* 過(guò)濾表達(dá)式字段,
* {uid}會(huì)自動(dòng)替換為當(dāng)前用戶的userId
* {me} main entity 主實(shí)體名稱* {me.a} main entity alias 主實(shí)體別名* 格式如:*

    *
  • userId = {uid}
  • *
  • (userId = {uid} AND authType > 3)
  • *
  • ((userId = {uid} AND authType) > 3 OR (dept in (select dept from depts where manager.id = {uid})))
  • *
*/private String exps;/*** codeShowName
* 規(guī)則說(shuō)明*/private String ruleComment;}

看完這個(gè)結(jié)構(gòu),基本能夠理解設(shè)計(jì)的思路了。數(shù)據(jù)結(jié)構(gòu)中保存如下幾個(gè)字段:

角色列表:需要使用此規(guī)則的角色,可以多個(gè),使用英文逗號(hào)隔開(kāi)。  實(shí)體列表:對(duì)應(yīng)的規(guī)則應(yīng)用的實(shí)體(這里指的是表結(jié)構(gòu)中的表名,可能你的實(shí)體是駝峰而數(shù)據(jù)庫(kù)是蛇形,所以這里要放蛇形那個(gè)),可以多個(gè),使用英文逗號(hào)隔開(kāi)。  表達(dá)式:表達(dá)式就是數(shù)據(jù)權(quán)限控制的核心了。簡(jiǎn)單的說(shuō)這里的表達(dá)式就是一段SQL語(yǔ)句,其中設(shè)置了一些可替換值,底層會(huì)用對(duì)應(yīng)運(yùn)行時(shí)的變量替換對(duì)應(yīng)內(nèi)容,從而達(dá)到增加條件的效果。  規(guī)則說(shuō)明:?jiǎn)渭兊囊粋€(gè)說(shuō)明字段。

核心流程

系統(tǒng)啟動(dòng)時(shí),首先從數(shù)據(jù)庫(kù)加載出所有的規(guī)則。底層利用插件機(jī)制來(lái)攔截所有的查詢語(yǔ)句,進(jìn)入查詢攔截方法后,首先根據(jù)當(dāng)前用戶的權(quán)限列表篩選出PermissionRule列表,然后循環(huán)列表中的規(guī)則,對(duì)語(yǔ)句中符合實(shí)體列表的表進(jìn)行條件增加,最終生成處理后的SQL語(yǔ)句,退出攔截器,Mybatis執(zhí)行處理后SQL并返回結(jié)果。

講完P(guān)ermissionRule,再來(lái)看看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 {}

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

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

private String processSelectSql(String sql, List 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("當(dāng)前sql中, " + mainTable + " 沒(méi)有設(shè)置別名");}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) {// 若主表匹配規(guī)則主體,則直接使用本規(guī)則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) {// 當(dāng)主表不能匹配時(shí),匹配所有join,使用符合條件的第一個(gè)表的規(guī)則。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("當(dāng)前sql沒(méi)有join的部分!");}}}}if (realRuls == null) return sql; // 沒(méi)有合適規(guī)則直接退出。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;}

重點(diǎn)思路

重點(diǎn)其實(shí)就在于Sql的解析和條件注入,使用開(kāi)源項(xiàng)目JSqlParser。

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

上述就是小編為大家分享的Java中怎么利用Mybatis實(shí)現(xiàn)數(shù)據(jù)權(quán)限控制了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。


分享文章:Java中怎么利用Mybatis實(shí)現(xiàn)數(shù)據(jù)權(quán)限控制
當(dāng)前鏈接:http://weahome.cn/article/jgspii.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部