第四章
細(xì)粒度數(shù)據(jù)查詢權(quán)限
(上)
通過基于角色訪問控制,我們可以控制哪些人具有某種權(quán)限。比如總公司員工柴其貴、分公司員工李朵朵和營業(yè)部員工賈志宏,三個(gè)人都具有訪問“查詢員工”頁面權(quán)限。
但,由于他們?nèi)怂诠炯?jí)別不同(總公司、分公司和營業(yè)部),進(jìn)入查詢員工頁面,系統(tǒng)展示出來的員工數(shù)據(jù)應(yīng)該是不同的。
此類數(shù)據(jù)查詢權(quán)限,在業(yè)務(wù)系統(tǒng)里面隨處可見。畢竟 N 多操作,都起源于查詢。連增加數(shù)據(jù)操作都有,比如增加表單的某個(gè)下拉框內(nèi)容,可能來自數(shù)據(jù)庫查詢。
數(shù)據(jù)查詢權(quán)限,包括 2 個(gè)緯度:行級(jí)和列級(jí)。
現(xiàn)有方案
常見拼湊 SQL
常見做法是,采用 if else 做判斷,決定執(zhí)行那條程序分支,也就是 SQL 。
if( user.getCompanyLevel()==Constants.總公司 ) {
sql=”select * from demouser u, company c where u.companyId=c.id”; //查詢所有員工
} else if( user.getCompanyLevel()==Constants.分公司 ) {
String currentUserCompanyId=user.getCompanyId();
sql=” select * from demouser u, company c where u.companyId=c.id and c.id”= currentUserCompanyId + “ or c.pid=”+currentUserCompanyId; //查詢本分公司及下屬營業(yè)部員工
} else if( user.getCompanyLevel()==Constants.營業(yè)部 ) {
String currentUserCompanyId=user.getCompanyId();
sql=” select * from demouser u, company c where u.companyId=c.id and c.id”= currentUserCompanyId; //查詢本營業(yè)部員工
}
變通方法
也有不少開發(fā)者對上述方法做了改進(jìn),我了解到如下改進(jìn)方法:
1. 采用 AOP 技術(shù),向 find/select 模型注入 where 條件;
2. 將 SQL 語句全部提取出來,集中保存在某個(gè) SQL 配置文件里面,類似 HIBERNATE 那樣。
上述方法,都不能減少 if else 判斷,只是把 SQL 語句做了轉(zhuǎn)移。 AOP 注入方式,將 if else 判斷從模型層轉(zhuǎn)移到注入層。集中提取 SQL 方式,只是將 SQL 轉(zhuǎn)移到統(tǒng)一的保存文件, if else 依然轉(zhuǎn)移不掉。
關(guān)于列級(jí)控制、分頁查詢和自定義條件查詢,那就更麻煩了,在此不做敘述。
如果使用 Metadmin
在設(shè)計(jì) Metadmin 之初,我們確定了這些目標(biāo):
1. 將行列級(jí)授權(quán)邏輯、 if else 判斷全部從業(yè)務(wù)代碼中剝離出去,達(dá)到權(quán)限與業(yè)務(wù)完全解開耦合;
2. 提供 API 供業(yè)務(wù)方法調(diào)用,通過該方法獲取該用戶具有權(quán)限查詢的數(shù)據(jù);
3. 整個(gè)過程不要編碼,也不要 XML ,通過界面設(shè)計(jì)出來,并且每個(gè)查詢邏輯設(shè)計(jì)完畢,可以立即在線測試,保證查詢邏輯無誤。
為此, Metadmin 提供如下服務(wù):
1. Metadmin 提供數(shù)據(jù)查詢 API :告訴 metadmin ,當(dāng)前是誰想要查什么數(shù)據(jù), metadmin 就能返回該用戶具有權(quán)限查詢的數(shù)據(jù);
2. Metadmin 提供的 API 支持分頁和自定義條件查詢,當(dāng)然這一切都是在該用戶的授權(quán)范圍內(nèi);
3. 權(quán)限設(shè)計(jì)器,通過設(shè)計(jì)器展現(xiàn)出業(yè)務(wù)數(shù)據(jù)庫表,運(yùn)用鼠標(biāo)拖拽等操作把查詢邏輯設(shè)計(jì)出來,并可以在線測試;
4. 支持復(fù)雜、特定數(shù)據(jù)庫邏輯手工輸入 SQL ,調(diào)優(yōu)性能。
以下演示來自 metadmin 下載包里面包含的演示示例,可以在 www.metadmin.com 下載 Metadmin 安裝程序包。
API
MetadminService 類:
static QueryResult |
query
(int privilegeId, User
user,
java.util.Map context) |
static QueryResult |
query
(int privilegeId, User
user,
java.util.Map context, CustomizedWhere
where) |
static QueryResult |
query
(int privilegeId, User
user,
java.util.Map context, CustomizedWhere
where,
int first, int max) |
static QueryResult |
query
(int privilegeId, User
user,
java.util.Map context, int first, int max) |
static int |
queryCount
(int privilegeId, User
user,
java.util.Map context) |
static int |
queryCount
(int privilegeId, User
user,
java.util.Map context, CustomizedWhere
where) |
WebMetadminService 類,為 WEB 程序定制的類,從 HttpRequest 自動(dòng)讀取當(dāng)前用戶:
static java.util.Collection |
query
(HttpServletRequest req, int privilegeId) |
static java.util.Collection |
query
(HttpServletRequest req,
int privilegeId, CustomizedWhere
where) |
static java.util.Collection |
query
(HttpServletRequest req,
int privilegeId, CustomizedWhere
where,
int first, int max) |
static java.util.Collection |
query
(HttpServletRequest req, int privilegeId,
int first, int max) |
static java.util.Collection |
query
(HttpServletRequest req, int privilegeId,
java.util.Map context) |
static java.util.Collection |
query
(HttpServletRequest req, int privilegeId,
java.util.Map context,CustomizedWhere
where) |
static java.util.Collection |
query
(HttpServletRequest req, int privilegeId,
java.util.Map context,CustomizedWhere
where,
int first, int max) |
static java.util.Collection |
query
(HttpServletRequest req, int privilegeId,
java.util.Map context, int first, int max) |
static int |
queryCount
(HttpServletRequest req,
int privilegeId) |
static int |
queryCount
(HttpServletRequest req,
int privilegeId, CustomizedWhere
where) |
static int |
queryCount
(HttpServletRequest req, int privilegeId,
java.util.Map context) |
static int |
queryCount
(HttpServletRequest req, int privilegeId,
java.util.Map context,CustomizedWhere
where) |
業(yè)務(wù)數(shù)據(jù)庫
第一章講解了 Metadmin 對于數(shù)據(jù)庫的分類:權(quán)限數(shù)據(jù)和業(yè)務(wù)數(shù)據(jù),兩者保存在不同 schema 里面。
WEB-INF/metadmin/datasources.xml :
<datasources>
<datasource name="metadmin" configFile="metadmin.properties"/>
<datasource name="mydemo" configFile="mysql.properties" schemas="mydemo, metadmin"/>
</datasources>
name=”mydemo” 的數(shù)據(jù)源表示業(yè)務(wù)數(shù)據(jù)源,具體配置信息在 mysql.properties 文件里面,打開設(shè)計(jì)器時(shí),只展示該數(shù)據(jù)庫的 mydemo 和 metadmin 兩個(gè) schema 數(shù)據(jù)庫表和視圖。
具體數(shù)據(jù)源配置信息,參閱: http://www.metadmin.com/doc/main.html#數(shù)據(jù)源 2.6
數(shù)據(jù)查詢設(shè)計(jì)器
在打開數(shù)據(jù)查收設(shè)計(jì)器之前,開發(fā)者先準(zhǔn)備好 JavaBean ,也就是打開把查詢出來的數(shù)據(jù)保存到哪個(gè) Java 值對象。演示程序提供了 Employee ,將查詢出來的數(shù)據(jù)保存到該 JavaBean 里面。
Employee.java
import java.util.Date;
public class Employee {
private int id;
private int companyId;
private int departmentId;
private String loginName;
private String name;
private String password;
private String companyName;
private String departmentName;
private int isManager;
private Date hireDate;
// … get/set methods…
}
啟動(dòng) web 服務(wù)器,在瀏覽器輸入: http://localhost:8080/mydemo/metadmin/designer
(假定您發(fā)布的 web context 是 mydemo ,且服務(wù)器端口是 8080 )
打開左邊條形框里面的“數(shù)據(jù)查詢”,在樹上右擊,選擇“新增數(shù)據(jù)查詢”。如圖示:
在彈出的框里面輸入相關(guān)信息,如圖示:
我們先新建總公司用戶查看數(shù)據(jù)的 SQL ,分公司和營業(yè)部用戶查詢 SQL 以及怎樣與業(yè)務(wù)系統(tǒng)集成,由于篇幅關(guān)系,下章講述。
在數(shù)據(jù)查詢樹上,單擊“查詢所有員工”,系統(tǒng)自動(dòng)展現(xiàn)數(shù)據(jù)查詢設(shè)計(jì)器。
然后按照如下步驟操作:
1. 展開 mydemo schema ,展開表;
2. 雙擊 company, department, demouser 表,因?yàn)橐樵冞@三張表;
3. 勾選 company 表 name 字段, department 表 name 字段,勾選 demouser 表所有字段;
4. 在映射類里面,輸入 org.back.demo.Employee ;
5. 檢查下面的字段映射是否有誤,修改 company 表的 name 映射屬性為 companyName ,修改 department 表的 name 映射屬性為 departmentName 。
如圖示:
到此,設(shè)計(jì)還差一個(gè)步驟,設(shè)置 where 條件。本查詢 where 條件是 3 張表關(guān)聯(lián)。
按照如下步驟操作:
1. 點(diǎn)擊設(shè)計(jì)器下方的“ WHERE ”標(biāo)簽頁
2. 右擊條件組,選擇“新增二元條件”;
3. 點(diǎn)擊第一個(gè)字段節(jié)點(diǎn),在右邊選擇“ company.id ”也就是 company 表的 id 字段;
4. 然后,設(shè)置第二個(gè)字段為“ demouser.companyId ”字段;
5. 右擊條件組,選擇“新增二元條件”;
6. 將第一個(gè)字段選擇為“ department.id ”,第二個(gè)字段選擇為“ demouser.departmentId ”。
至此,三表關(guān)聯(lián)完畢。也就是完成 SQL : company.id=demouser.companyId and department.id=demouser.departmentId 。
如圖示:
現(xiàn)在,我們可以測試了!選擇設(shè)計(jì)器下方“測試”標(biāo)簽頁,點(diǎn)擊控制臺(tái)執(zhí)行小圖標(biāo)。 Metadmin 將顯示該 sql 語句能查詢的數(shù)據(jù),行列級(jí)!
如圖示:
至此,我們完全放心該 SQL 語句沒有任何問題。點(diǎn)擊“保存”圖標(biāo),保存設(shè)計(jì)結(jié)果。
下章,講解其他 SQL 還有怎樣與業(yè)務(wù)系統(tǒng)集成。