ava識堂,一個高原創,高收藏,有干貨的微信公眾號,一起成長,一起進步,歡迎關注
關系型數據庫最難的地方,就是建模(model)。
錯綜復雜的數據,需要建立模型,才能儲存在數據庫。所謂"模型"就是兩樣東西:實體(entity)+ 關系(relationship)。
實體指的是那些實際的對象,帶有自己的屬性,可以理解成一組相關屬性的容器。關系就是實體之間的聯系,通常可以分成"一對一"、"一對多"和"多對多"等類型。
在關系型數據庫里面,每個實體有自己的一張表(table),所有屬性都是這張表的字段(field),表與表之間根據關聯字段"連接"(join)在一起。所以,表的連接是關系型數據庫的核心問題。
表的連接分成好幾種類型。
內連接(inner join)外連接(outer join)左連接(left join)右連接(right join)全連接(full join)
以前,很多文章采用維恩圖(兩個圓的集合運算),解釋不同連接的差異。
上周,我讀到一篇文章,認為還有比維恩圖更好的解釋方式。我發現確實如此,換一個角度解釋,更容易懂。
所謂"連接",就是兩張表根據關聯字段,組合成一個數據集。問題是,兩張表的關聯字段的值往往是不一致的,如果關聯字段不匹配,怎么處理?比如,表 A 包含張三和李四,表 B 包含李四和王五,匹配的只有李四這一條記錄。
很容易看出,一共有四種處理方法。
只返回兩張表匹配的記錄,這叫內連接(inner join)。返回匹配的記錄,以及表 A 多余的記錄,這叫左連接(left join)。返回匹配的記錄,以及表 B 多余的記錄,這叫右連接(right join)。返回匹配的記錄,以及表 A 和表 B 各自的多余記錄,這叫全連接(full join)。
下圖就是四種連接的圖示。我覺得,這張圖比維恩圖更易懂,也更準確。
上圖中,表 A 的記錄是 123,表 B 的記錄是 ABC,顏色表示匹配關系。返回結果中,如果另一張表沒有匹配的記錄,則用 null 填充。
這四種連接,又可以分成兩大類:內連接(inner join)表示只包含匹配的記錄,外連接(outer join)表示還包含不匹配的記錄。所以,左連接、右連接、全連接都屬于外連接。
這四種連接的 SQL 語句如下。
SELECT * FROM A INNER JOIN B ON A.book_id=B.book_id; SELECT * FROM A LEFT JOIN B ON A.book_id=B.book_id; SELECT * FROM A RIGHT JOIN B ON A.book_id=B.book_id; SELECT * FROM A FULL JOIN B ON A.book_id=B.book_id;
上面的 SQL 語句還可以加上where條件從句,對記錄進行篩選,比如只返回表 A 里面不匹配表 B 的記錄。
SELECT * FROM A LEFT JOIN B ON A.book_id=B.book_id WHERE B.id IS null;
另一個例子,返回表 A 或表 B 所有不匹配的記錄。
SELECT * FROM A FULL JOIN B ON A.book_id=B.book_id WHERE A.id IS null OR B.id IS null;
此外,還存在一種特殊的連接,叫做"交叉連接"(cross join),指的是表 A 和表 B 不存在關聯字段,這時表 A(共有 n 條記錄)與表 B (共有 m 條記錄)連接后,會產生一張包含 n x m 條記錄的新表(見下圖)。
作者:阮一峰
鏈接:http://www.ruanyifeng.com/blog/2019/01/table-join.html
京東SRC(Security Response Center)收錄大量外部白帽子提交的sql注入漏洞,漏洞發生的原因多為sql語句拼接和Mybatis使用不當導致。
mysql5.0以上版本中存在一個重要的系統數據庫information_schema,通過此數據庫可訪問mysql中存在的數據庫名、表名、字段名等元數據。information_schema中有三個表成為了sql注入構造的關鍵。
SQL注入常用SQL函數
// sqli vuln code
Statement statement = con.createStatement();
String sql = "select * from users where username = '" + username + "'";
logger.info(sql);
ResultSet rs = statement.executeQuery(sql);
// fix code 如果要使用原始jdbc,請采用預編譯執行
String sql = "select * from users where username = ?";
PreparedStatement st = con.prepareStatement(sql);
使用未預編譯原始jdbc作為demo,注意此demo中sql語句參數采用單引號閉合。
對于字符類型注入,通常先嘗試單引號,判斷單引號是否被拼接到SQL語句中。推薦使用瀏覽器擴展harkbar作為手工測試工具。https://chrome.google.com/webstore/detail/hackbar/ginpbkfigcoaokgflihfhhmglmbchinc
正常頁面應該顯示如下:
admin后加單引號導致無信息回顯,原因是后端sql執行報錯,說明引號被拼接至SQL語句中
select * from users where username = 'admin' #正常sql
select * from users where username = 'admin'' #admin'被帶入sql執行導致報錯無法顯示信息
mysql中使用order by 進行排序,不僅可以是字段名也可以是字段序號。所以可以用來判斷表中字段數,order by 超過字段個數的數字就會報錯。
判斷字段數
當order by 超過4時會報錯,所以此表共四個字段。
后端所執行的sql語句
select * from users where username = 'admin' order by 1-- '
此處我們將原本username的值admin替換為admin’ order by 1 —+,其中admin后的單引號用于閉合原本sql語句中的前引號,—+用于注釋sql語句中的后引號。—后的+號主要作用是提供一個空格,sql語句單行注釋后需有空格,+會被解碼為空格。
主要用于定位后端sql字段在前端顯示的位置,采用聯合查詢的方式確定。注意聯合查詢前后字段需一致,這也就是我們為什么做第二步的原因。
通過下圖可知,后端查詢并回顯的字段位置為2,3位。
聯合查詢后的字段可以隨意,本次采用的是數字1到4直觀方便。
group_concat()函數用于將查詢結果拼接為字符串。
sqlmap兼容python2和python3,可以自動化檢測各類注入和幾乎所有數據庫類型。
-u 可能存在注入的url鏈接
-r讀取http數據包
--data 指定post數據
--cookie 指定cookie
--headers 指定http頭 如采用token認證的情況下
--threads 指定線程數
--dbms 指定后端的數據庫
--os 指定后端的操作系統類型
--current-user 當前用戶
--users 所有用戶
--is-dba 是否是dba
--sql-shell 交互式的sqlshell
-p指定可能存在注入點的參數
--dbs 窮舉系統存在的數據庫
-D指定數據庫
--tables 窮舉存在的表
-T指定表
--column 窮舉字段
-C指定字段
--dump dump數據
直接檢測
其中—cookie用于指定cookie,—batch 自動化執行,—dbms指定數據庫類型
檢測結果
讀取系統中存在數據庫
—dbs讀取當前用戶下的數據庫
讀取指定庫下的表
-D java_sec_code —tables
dump users表數據
-D java_sec_code -T users —dump
//采用#不會導致sql注入,mybatis會使用預編譯執行
@Select("select * from users where username = #{username}")
User findByUserName(@Param("username") String username);
//采用$作為入參可導致sql注入
@Select("select * from users where username = '${username}'")
List<User> findByUserNameVuln01(@Param("username") String username);
//錯誤寫法
<select id="findByUserNameVuln02" parameterType="String" resultMap="User">
select * from users where username like '%${_parameter}%'
</select>
//正確寫法
<select id="findByUserNameVuln02" parameterType="String" resultMap="User">
select * from users where username like concat(‘%’,#{_parameter}, ‘%’)
</select>
order by 后若使用#{}會導致報錯,因為#{}默認添加引號會導致找不到字段從而報錯。
//錯誤寫法
<select id="findByUserNameVuln03" parameterType="String" resultMap="User">
select * from users
<if test="order != null">
order by ${order} asc
</if>
</select>
//正確寫法 id指字段id 此表字段共四個 所以id為1-4
<select id="OrderByUsername" resultMap="User">
select * from users order by id asc limit 1
</select>
slqmap手冊:https://octobug.gitbooks.io/sqlmap-wiki-zhcn/content/Users-manual/Introduction.html
sql注入詳解:http://sqlwiki.radare.cn/#/
之前的文章中介紹了使用ASP.NET MVC來開發一個博客系統,并且已將初具雛形,可以查看文章列表頁面,也可以點擊文章列表的其中一篇文章查看詳情,這已經完成了最開始需求分析的讀者的查看列表和查看文章兩個需求,但是現在最大的問題是文章數據仍然是“靜態”的。
所有數據被保存在內存中,系統在初始化時會自動添加被硬編碼在代碼文件中的數據,服務器每次重新啟動都只會保留這些數據,并且沒有提供“作者”的管理接口,“作者”沒法來管理這些數據,哪怕是可以進行管理也沒用,因為服務器重啟后數據就不存在了,這才是最糟糕的。最初使用HTML文件保存的文章雖然不易修改,但至少數據以文件的形式被保存在硬盤上,只要硬盤不壞,那么數據永遠不會丟失,但是現在不一樣了,要如何對數據進行持久化呢?
數據庫是最好的選擇(有的時候也會需要使用文件來管理數據,需要根據實際的應用場景來選擇,如配置文件),當然也可以使用文件的方式來管理數據,其實數據庫也是一種特殊的文件,只不過數據庫文件可以特有的訪問方式來對數據進行管理,如插入、刪除、更新和快速查找等等,這些功能都不需要自己編寫,它們是數據庫管理系統提供的(DBMS,Database Management System),數據庫又存在很多種類型,在一般企業級應用中常用的是關系型數據庫,主要有SQL Server、MySQL、Oracle、PostgreSQL等,它們都能夠為應用程序的數據存儲提供可靠保證。
對于ASP.NET來說最常用且提供支持最多的當然是SQL Server,但是由于MySQL和PostgreSQL都是開源的,一定程度上可以對其免費使用,所以也經常被.NET開發人員使用,本系列文章將使用SQL Server和MySQL兩種數據庫來實現數據存儲功能,同時也可以看出不同數據庫對一個應用程序會有哪些影響。
本文通過以下兩點來介紹如何在ASP.NET中使用SQL Server數據庫:
●使用SQL Server存儲數據
●在ASP.NET中訪問SQL Server
使用SQL Server存儲數據
1. 使用SQL Server創建Blog數據庫,以及Posts表,表字段對應Post類(如何使用SQL Server不在本系列文章范圍內,所以會忽略很多細節):
2. 在表中添加數據:
在ASP.NET中訪問SQL Server
文章前面說過數據庫其實也是一種特殊的文件,然后通過特有的訪問方式來使用它,那么ASP.NET 要如何連接并使用SQL Server呢?ADO.NET。(ADO.NET更多信息參考:http://blog.csdn.net/dreamcatchergo/article/details/9729525)
現在修改原有通過靜態數組獲取數據的BlogRepository,使用ADO.NET來獲取數據庫中的數據,ADO.NET相關的類在System.Data.dll程序集中,而ADO.NET對于不同的數據源提供了不同的數據提供器,它們用于連接不同的數據源:
SQL Server:System.Data.SqlClient
OleDb: System.Data.OleDb
Odbc: System.Data.Odbc
Oracle: System.Data.OracleClient
在本例中使用的是SQL Server,所以需要System.Data以及System.Data.SqlClient的支持,System.Data在創建項目時以及默認存在。
1. 通過Nuget包管理器安裝System.Data.Sqlclient庫:
2. 修改BlogRepository,讓其從數據庫中獲取數據:
上面代碼中有三個重要的對象分別是SqlConnection、SqlCommand、SqlDataReader,它們的作用分別是通過連接字符串連接數據庫、通過SQL語句及參數執行SQL(也可以執行存儲過程)、讀取執行SQL返回的結果。
3. 程序執行結果:
小結
本章介紹了如何使用數據庫來管理數據,然后通過ADO.NET來連接數據庫,從數據庫中獲取文章數據,然后顯示到頁面上,現在這個應用程序已經“動”起來了,僅需要更新數據庫的內容,頁面內容也將隨之而變。但存在一個問題就是每一次從數據庫獲取數據都要寫SQL語句然后從返回的數據集中獲取相應字段的數據用來創建實體對象。有沒有更好的方法來解決?
參考:
https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/
http://blog.csdn.net/dreamcatchergo/article/details/9729525
歡迎添加個人微信號:Like若所思。
歡迎關注我的公眾號,不僅為你推薦最新的博文,還有更多驚喜和資源在等著你!一起學習共同進步!
*請認真填寫需求信息,我們會在24小時內與您取得聯系。