您好,登錄后才能下訂單哦!
對于題目中提到的動態 SQL,顯然應該有一種靜態 SQL 與之相對,那么兩者應該怎么理解?我們先來個基本的認識。
靜態 SQL 之“靜態”,意味著在執行之前就已經明確了該 sql 在數據庫執行后的業務含義,也就是對于做啥事兒我們是清楚的,只不過需要知道這事兒的具體內容有哪些。比如“select userID,username from users where deptName=’銷售部’”,意思就是查銷售部的所有用戶 ID 及用戶名。 再稍微靈活一點,可以寫作“select userID,username from users where deptName=?”,此時語句中的“?”傳遞哪個部門名稱,相對應的就查哪個部門的用戶信息。但,sql 本身所要做的“查詢某部門下的用戶信息”這個事情我們是完全明確的,不確定的只是用戶有多少,各自的信息是什么。
而動態 SQL 之所以稱為“動態”,就是在是否“明確業務含義”這一點上是“不明確”的,也就是說 sql 應該執行出啥結果,我們本身并沒有明確的預期,包括查什么、用哪些條件以及怎么組合這些條件等——原則上,這些都可以隨意選擇組合。
比如:“select userID,userName from users where ${condition}”,此處的 condition 可以隨意指定合理的查詢條件,當傳入“deptName=’xx 部門’”時,執行結果是查詢某部門的用戶信息;當傳入“roleName=’技術主管’”時,則是按照角色篩選復合條件的用戶信息。盡管都是返回用戶信息,但不同條件下,我們可以認為是兩個不同的業務,這里的 sql 我們就稱之為是動態的,顯然,這樣的 sql 執行后得到的結果的業務含義也是完全不確定的。
上面動態 sql 的例子是條件不固定,實際上動態 sql 還允許返回字段不確定,如“select userID,${fields} from users where …”, 需要查詢哪些字段,完全由動態指定的”fields”來確定,可以查 deptName,也可以查 userName 和 deptName,非常自由、靈活。
同理,使用的物理表也可能不固定,例如定義為“select … from ${table} where …”時,”table”給定啥值就從哪個表查數據。
結合兩者的具體含義,可以分析得到各自的優劣所在:
l 靜態 SQL :功能固定,比較傳統,但相對比較安全。
l 動態 SQL :自由靈活,但同時不得不提到 sql 植入風險,一旦被攻擊者利用可能的 sql 漏洞,會有相當嚴重的安全問題,如竊取用戶信息、篡改數據等等。
而在常用的報表工具使用場景中,報表開發人員一般都沒有 DBA 的功力,因此難以對 sql 的安全性問題考慮周全,動態 SQL 可能帶來的風險也就很難排除。而要規避動態 SQL 風險,無非是下面兩種方法:
1、 讓 dba 配合工作,盡可能協助提供安全性高的 sql ;
2、 希望報表工具可以提供規避 sql 注入風險的方法。
方法 1 需要依賴外部 DBA 的配合,不是總能滿足,所以,比較可行的,還是在考察報表產品時,考慮報表工具是否提供防 sql 植入風險的功能支持。不然,在安全第一的前提下,就只能不用動態 sql ,退而選擇靜態 sql 了,畢竟安全還是最重要的,一旦造成信息泄露,責任很難承擔。
下面,我們就對比一下常見的兩種報表工具, Birt 和潤乾報表,看看它們各自在動態 SQL 以及安全性方面的表現如何:
Birt 動態 sql**** 的做法有兩種:
1、 數據集中通過 script 拼接 ****sql
2、 應用程序來實現:這個太麻煩,需要有 Java 開發能力,咱們就不介紹了,感興趣的可以到網上搜索自行研究。
下面我們通過實例來講解一下在數據集中通過 script 腳本拼接 sql 的方式。
注:例子以“員工表”為例,員工表內存放有“工資”及“應發工資”兩個字段,我們通過動態 SQL 實現由終端用戶選擇應該查詢“工資”列還是“應發工資”列
1、 新建報表并新增數據源(hsql)
2、 新增數據集
如,數據集名稱為“ dt ” ,query text 為“select * from 員工”。實際這里可以定義為空,但為了保存等操作不報錯,這里需要隨便寫個 sql。
3、 利用 script ,在 data set 的 before open 事件中實現動態 sql
點擊 dt 數據集,選中 script 頁,選擇 beforeOpen 并編輯如下
腳本如下(含義為當 qField 傳進來是“工資”時,拼入工資字段查數據,為“應發工資”時則對應查詢,默認查詢工資列):
var query = this .queryText; // 獲取 query text 內定義的 sql
query = "";
query =“SELECT 編號, 部門, 姓名 , 性別, 出生日期, 入職日期, 籍貫,”;
var queryField = params[“qField”].value;
if (queryField!= null &&queryField!=""){
query += queryField ;
}
else {
query +="工資" ; // 如果不給值或默認狀態,查工資列
}
query += "as salary from 員工表";
this .queryText = query;
4、 報表模板設計
前面 6 列為常規列,拖拽字段到 dataRow 即可,第 7 列位動態列,這里當根據傳入參數的不同,顯示不同的標題(header row)及數值(data row),其中
Header row 表達式為:params[“qField”]
Data row 表達式為:dataSetRow[params[“qField”]]
5、 結果展現
(1) 當輸入 qField 參數為“應發工資”時
結果
(2) 當輸入 qField 參數為“工資”時
結果
暫且不考慮上述操作涉及的復雜的制作過程,即便辛苦做出了效果,我們也不難發現其中留有很大漏洞,那就是這種拼串的方法很容易出現 sql 植入問題。
比如,當攻擊者嘗試給 qFiled 參數傳入“身份證號”,而恰好又存在該字段的話,用戶的身份證號信息就全部暴露了,同樣,用類似的方法還可以猜到“電話號碼”、“家庭住址”,等等等等,這些敏感信息的全面泄露,意味著安全保障的不堪一擊。
而針對腳本這種做法,想要全面規避風險非常麻煩,可能的方式是在腳本里加入大量的判斷,盡力排除所有 “身份證”、“電話號碼”、“手機號碼”等各種情況。對于一兩個報表這么搞還行,多了肯定就費勁了,并且后期維護也得蒙圈。
同樣以“員工表”為例,員工表內存放有“工資”及“應發工資”,我們來實現由終端用戶選擇應該查詢“工資”列還是“應發工資”列。
潤乾報表采用動態參數實現:
(1) 連接 demo 數據源
(2) 用向導生成報表
通過向導,一步生成如下網格式報表模板
(3) 修改數據集,增加動態列
A、 增加報表參數“ qFiled ”
B、 數據集 sql 中增加動態列
采用宏的方式引入, sql 改為如下
(4) 修改報表模板,增加動態列
G1:=@qFiled
G2:=ds1.${qFiled}
(5) 效果展示
A、 qFiled 傳入“應發工資”
結果
B、 qFiled 傳入“工資”
結果
很顯然,僅從做法而言,潤乾報表就比 birt 要簡單的多,不需要寫任何腳本即可實現,對開發人員的技術要求也不高,對應后期維護也就很輕松了。
當然這里也存在 sql 植入的風險,但是潤乾報表作為一款商業軟件,廠商已經為用戶考慮到了,可以通過專門的配置規避風險,具體使用也很簡單,但功能很強大。
關于 sql 植入及報表規避的有專門的文章做詳細的介紹,可參考《報表的 SQL 植入風險及規避方法》 url : 報表的 SQL 植入風險及規避方法
總結
–
不管是開源還是商業報表,可能對于某功能的實現都沒什么問題,但制作方法的簡便性、考慮問題是否周全(比如安全性問題)等方面的差距就可能會非常明晰。
開源報表固然有它的優點——不用花錢!!! 但工作量相對要大很多,服務或支持也沒有保障,只能靠用戶自己埋頭苦干去研究了。而商業報表則是一條捷徑,雖然要花一點點 Money,但考慮到效率及安全性方面,那就真是完全可忽略的成本了。
詳情鏈接: http://c.raqsoft.com.cn/article/1564367236761?r=gxy
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。