如何利用緩存機制實現JAVA類反射性能提升30倍

1 SSM框架簡介

SSM框架,即SpringMVC+Spring+Mybatis三個開源框架整合在一起的縮寫。

在SSM框架之前生產環境中SSH框架佔據多數,即Struts2+Spring+Hibernate三個開源框架整合而成。後因Struts2爆出衆多高危漏洞,導致目前SSM逐漸代替SSH成爲主流開發框架的選擇。

審計SSM框架首先就要對MVC設計模式和,web三層架構有一定程度的瞭解,限於篇幅原因這裏就簡單介紹一下

1.1 SpringMVC

是一種基於Java的實現MVC設計模式的請求驅動類型的輕量級Web框架,使用了MVC架構模式的思想,將web層進行職責解耦,基於請求驅動指的就是使用請求-響應模型,框架的目的就是幫助我們簡化開發。

1.2 Spring

是分層的 Java SE/EE full-stack 輕量級開源框架,以 IOC(Inverse of Control,控制反轉)和 AOP(Aspect Oriented Programming,面向切面編程)爲內核,使用基本的 JavaBean 完成以前只可能由 EJB 完成的工作,取代了 EJB 臃腫和低效的開發模式,Spring的用途不僅僅限於服務器端的開發。從簡單性、可測試性和鬆耦合性角度而言,絕大部分Java應用都可以從Spring中受益

1.3 Mybatis

是支持定製化 SQL、存儲過程以及高級映射的優秀的持久層框架。MyBatis 避免了幾乎所有的 JDBC 代碼和手動設置參數以及獲取結果集。MyBatis 可以對配置和原生Map使用簡單的 XML 或註解,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對象)映射成數據庫中的記錄。

1.4 Servlet

還有一項技術雖然名稱沒有出現在這三個開源框架中但是SpringMVC的底層就是以此技術進行構建的,這項技術就是Servlet

Servlet是基於Java技術的Web組件,由容器管理併產生動態的內容。Servlet與客戶端通過Servlet容器實現的請求/響應模型進行交互。

相對以SSM框架搭建的java web項目進行審計,上述這些都是要有一定程度的瞭解的。

2 SSM框架代碼執行流程和審計思路

2.1 審計的出發點web.xml

其實代碼審計的核心思想就是追蹤參數,而追蹤參數的步驟就是程序執行的步驟,說白了代碼審計就是一個跟蹤程序執行步驟的一個過程,當我們知道了SSM框架的一個執行流程,自然就知道了如何如跟蹤一個參數,剩下的就是去觀察在參數傳遞的過程中有沒有一些常見的漏洞點。

我們這裏通過一個簡單的Demo來描述一下SSM框架搭建的項目是如何完成一次用戶請求,它的流程是怎麼樣的,而參數又是怎樣被傳遞怎樣被過濾的,當我們明白了這些,就可以嘗試自己上手一些SSM的項目審計。

首先我把Demo的全部文件和文件結構粘貼出來
如何利用緩存機制實現JAVA類反射性能提升30倍
這是一個簡單的圖書管理Demo目錄,功能是對圖書名稱,數量和簡介簡單的增刪改查

首先不管我們是審計一個項目還是包括Tomcat加載一個項目一般都是由web.xml這個文件開始的

當然一個項目中沒有web.xml也是可以的,可以通過servlet3.0開始提供的一些新註解來達到和配置web.xml一樣的效果,但是這樣的項目很少會碰到,所以我們以主流的配置web.xml的項目來作爲講解。

Tomcat會加載web.xml文件讀取文件中的內容

如何利用緩存機制實現JAVA類反射性能提升30倍
web.xml文件主要的工作包括兩部分:1、web.xml啓動spring容器;2、DispathcheServlet的聲明;3、其餘工作是session過期,字符串編碼等

首先是生成DispatcherServlet類,DispatcherServlet是前端控制器設計模式的實現,提供Spring Web MVC的集中訪問點(也就是把前端請求分發到目標controller),而且與Spring IoC容器無縫集成,從而可以獲得Spring的所有好處。

簡單理解就是將我們的請求轉發至我們的SpringMVC中去,交由我們SpringMVC的Controller來進行接下來的處理

然後下面有一個 子標籤,是生成DispatcherServlet時的初始化參數contextConfigLocation參數,Spring會根據這個參數去加載所有逗號分隔的xml文件,如果沒有這個參數,Spring默認加載WEB-INF/DispatcherServlet-servlet.xml文件

下面的 標籤中還有一個子標籤 裏面的value是“/”代表攔截所有請求。

下面的 標籤放在後面講

2.2 Spring核心配置文件applicationContext.xml

然後我們根據加載順序去看applicationContext.xml
如何利用緩存機制實現JAVA類反射性能提升30倍如何利用緩存機制實現JAVA類反射性能提升30倍
applicationContext.xml中包含了三個配置文件,這三個配置文件就是我們用Spring來整合SpringMVC和Mybaits的配置文件,其實這三個配置文件中的內容都可以直接寫applicationContext.xml中因爲applicationContext.xml是Spring的核心配置文件,例如生成Bean,配置連接池,生成sqlSessionFactory。但是爲了便於理解,我們這些配置分別寫在三個配置文件中,由applicationContext.xml將這三個xml進行關聯,由下面這張截圖我們可以清晰的看到applicationContext.xml將這三個配置文件關聯了起來。
如何利用緩存機制實現JAVA類反射性能提升30倍
首先數據經由DispatcherServlet派發至Spring-mvc的controller層所以我們先看Spring-mvc.xml這個配置文件
如何利用緩存機制實現JAVA類反射性能提升30倍
標籤

如果在web.xml中servlet-mapping的url-pattern設置的是/,而不是如.do。表示將所有的文件,包含靜態資源文件都交給spring mvc處理。就需要用到了。如果不加,DispatcherServlet則無法區分請求是資源文件還是mvc的註解,而導致controller的請求報404錯誤

在Spring-mvc.xml中配置後,會在Spring MVC上下文中定義一個org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler,它會像一個檢查員,對進入DispatcherServlet的URL進行篩查,如果發現是靜態資源的請求,就將該請求轉由Web應用服務器默認的Servlet處理,如果不是靜態資源的請求,才由DispatcherServlet繼續處理,這麼做是爲了保證Spring-mvc優雅REST風格的資源URL。

剩下兩項一個是指定了返回的view所在的路徑,另一個是指定SpringMVC註解的掃描路徑

不難看出這個配置文件中都是和Spring-mvc相關的配置。

2.3 SSM之SpringMVC執行流程

接下來就是我們SpringMVC controller層接受前臺傳入的數據,這裏我們讓demo跑起來以方便演示和講解

如何利用緩存機制實現JAVA類反射性能提升30倍
這是我們的index首頁,我們看下頁面源碼
如何利用緩存機制實現JAVA類反射性能提升30倍
可以看到a標籤的超鏈接是http://localhost:8080/SSMFrameWorkTest_war/book/allbook

${pageContext.request.contextPath}

是JSP取得絕對路徑的方法, 也就是取出部署的應用程序名或者是當前的項目名稱,這樣當我們把項目部署到生產環境中時不容易出錯

後臺此時收到的請求路徑爲/book/allbook首先SpringMVC在項目啓動時會去掃描我們指定的要掃描的路徑也就是com.kuang.controller這個路徑下的所有類,我們看下BookController這個類的代碼
如何利用緩存機制實現JAVA類反射性能提升30倍
SpringMVC會掃描這個類中的所有註解,當看到@Controller是會生成該controller的Bean,掃描到@RequestMappting註解的時候會將@RequestMappting中的URI和下面的方法形成映射。所以我們請求的URI是“/book/allBool” SpringMVC就會將數據交由BookController類的list方法來處理

我們仔細觀察list方法,裏面調用了bookService參數的queryAllBook方法,這裏使用到了兩個註解@Autowired,@Qualifier,簡單介紹下兩個註解的作用

@Autowrite

作用:自動按照類型注入,只要有唯一的類型匹配就能注入成功,當傳入的類型不唯一時,則會報錯。

@Qualiier

作用:在自動按照類型注入的基礎上,在按照bean的id注入。它在給類成員注入數據時,不能獨立使用。但是再給方法的形參注入數據的時候,可以獨立使用。

由此可以看到bookService參數的類型是BookService類型,通過註解自動注入的Bean的id叫做BookServiceImpl

2.4 SSM之Spring執行流程

這裏我們就要從SpringMVC的部分過渡到Spring的部分了,所謂的過渡就是我們從SpringMVC的Controller層去調用Service層而這Service就是我們使用Spring進行IOC控制和AOP編程的地方。

首先我們需要先要去看spring-service.xml這個配置文件,
如何利用緩存機制實現JAVA類反射性能提升30倍
這裏我們看到了一個很重要的東西 id爲BookServiceImpl的bean,我們可以看到這個bean的class路徑是com.kuang.service.BookServiceImpl,

這個標籤就牽扯到了Spring一大核心功能點 就是IOC(Inversion of Control)控制反轉,名字聽起來特別唬人,其實特別容易理解,就是本來寫一個項目需要我們自己手動去new一個實例出來,用了Spring以後我們至於要把我們需要生成實例的那個類的路徑,以及我們在new 一個實例時需要傳入的的參數,傳入參數的方法可以是通過構造方法,也可以通過set方法,我們還可以給這個bean起一個名稱來方便我們調用(如果不用id參數之名的話那麼這個bean的名稱默認爲 類名開頭字母小寫,比如BookServiceImpl,如不特別指定,那麼生成的bean的名稱就是bookServiceImpl)。Spring就會在啓動時將這些我們指定好的類生成的實例放入IOC容器中供我們使用,通俗點說就是本來由我們手動生成實例的過程交由Spring來做了,這就是所謂的控制反轉。

接下來我們去看BookServiceImpl這個類的詳細信息
如何利用緩存機制實現JAVA類反射性能提升30倍
首先看到該類是實現了BookService這個接口,ok我們先去看BookService這個接口
如何利用緩存機制實現JAVA類反射性能提升30倍
可以看到接口中定義了四種方法,爲了方便理解,這些方法的名字是對應着日常項目中最長用的操作數據庫的四個方法即,增刪改查。

好了,看完了接口我們來看接口的實現類也就是BookServiceImpl。

如何利用緩存機制實現JAVA類反射性能提升30倍
由於實現了BookService這個接口,自然也就需要實現該接口下的所有方法,我們找到queryAllBook方法,發現queryAllBook調用了bookMapper參數的queryAllBook方法,而bookMapper是BookMapper類型的參數。

我們回過頭來看spring-service.xml中的這一項配置,之前說了這一配置是將BookServiceImpl這個類生成一個bean並放入Spring 的IOC容器中, 標籤的意思是通過該類提供的set方法在bean生成時向指定的參數注入value,name屬性就是指定的參數的名稱,可以看到我們BookServiceImpl中確實有一個私有參數名叫bookMapper

並且提供了該屬性的set方法, ref屬性是指要注入的value是其他的Bean類型,如果傳入的是一些基本類型或者String類型就不需要用ref 將ref改成value就可以
如何利用緩存機制實現JAVA類反射性能提升30倍
這裏我們可以看到我們通過ref屬性向BookServiceImpl這個類中的bookMapper參數注入了一個value,這個value是一個其他的bean類型,這個bean的id叫做bookMapper。此時由於我們Service層的BookServiceImpl的queryAllBook方法的實現方式其實就是調用了id爲bookMapper的bean的queryAllBook方法。所以這個id爲bookMapper的bean就是程序執行的下一步。

2.5 SSM之Mybatis執行流程

接下來就是是我們的web三層架構的數據訪問層也就是我們Mybaits負責的部分,通常這一部分的包名會叫做xxxdao,也就是開發中常說的dao層,該包下面的類和接口通常都叫做xxxDao或者xxxMapper,當然不遵守這個規範也可以但是不推薦。此時我們的請求要從Spring負責的業務層過渡到Mybatis負責的數據層了,但是Mybaits和Spring的關係不像SpringMVC和Spring的關係一樣可以無縫銜接,所以我們需要通過配置文件將Mybatis和Spring關聯起來,這裏我們看一下pom.xml
如何利用緩存機制實現JAVA類反射性能提升30倍
可以看到我們導入的包除了Mybatis本身,還倒入了一個mybatis-spring的包,目的就是爲了將Mybatis和Spring做結合,spring-dao.xml也就是用來整合Spring和Mybatis的配置文件。

剛纔我們看到Spring啓動加載bean時會注入一個id爲bookMapper的bean但是我們並未在之前的任何配置文件包括註解中看到有這個bean的相關信息,所以我們接下來要看spring-dao.xml中有沒有和這個bean有關的信息
如何利用緩存機制實現JAVA類反射性能提升30倍
每項配置的作用基本都用註釋的方式標明瞭

這裏關聯了一個properties文件
如何利用緩存機制實現JAVA類反射性能提升30倍
裏面是連接數據庫和配置連接池時需要的信息,沒什麼好說的。

我們着重看這個配置
如何利用緩存機制實現JAVA類反射性能提升30倍
這個配置通過生成MapperScannerConfigurer的bean來實現自動掃描com.kuang.dao下面的接口包,然後動態注入到Spring IOC容器中,同樣動態注入的bean的id默認爲類名(開頭字母小寫),我們看下到目錄下有哪些文件。
如何利用緩存機制實現JAVA類反射性能提升30倍
我們看到有一個叫BookMapper的接口文件,這樣就明白了之前生成BookServiceImpl這個bean是通過 也就是通過BookServiceImpl類中的

public void setBookMapper(BookMapper bookMapper) {

? this.bookMapper = bookMapper;

}

方法注入的這個bookMapper是哪裏來的了,是由我們配置了MapperScannerConfigurer這個bean後這個bean幫我們掃描dao包下的藉口文件並生成bean然後再幫我們注入到Spring的IOC容器中,所以我們纔可以在BookServiceImpl這個bean中通過 標籤注入bookmapper這個bean

然後我們來看這項配置
如何利用緩存機制實現JAVA類反射性能提升30倍
這裏是生成一個id爲sqlSessionFactory的bean,這裏就要引出Mybatis中的兩個關鍵對象即sqlSessionFactory和sqlSession。

簡單介紹下這兩個對象

SqlSessionFactory

SqlSessionFactory是MyBatis的關鍵對象,它是單個數據庫映射關係經過編譯後的內存鏡像。SqlSessionFactory對象的實例可以通過SqlSessionBuilder對象獲得,而SqlSessionBuilder則可以從XML配置文件或一個預先定製的Configuration的實例構建出SqlSessionFactory的實例。SqlSessionFactory是創建SqlSession的工廠。

SqlSession

SqlSession是執行持久化操作的對象,類似於JDBC中的Connection。它是應用程序與持久存儲層之間執行交互操作的一個單線程對象。SqlSession對象完全包括以數據庫爲背景的所有執行SQL操作的方法,它的底層封裝了JDBC連接,可以用SqlSession實例來直接執行已映射的SQL語句。

SqlSessionFactory和SqlSession的實現過程:

mybatis框架主要是圍繞着SqlSessionFactory進行的,創建過程大概如下:

(1)、定義一個Configuration對象,其中包含數據源、事務、mapper文件資源以及影響數據庫行爲屬性設置settings

(2)、通過配置對象,則可以創建一個SqlSessionFactoryBuilder對象

(3)、通過 SqlSessionFactoryBuilder 獲得SqlSessionFactory 的實例。

(4)、SqlSessionFactory 的實例可以獲得操作數據的SqlSession實例,通過這個實例對數據庫進行

如果是spring和mybaits整合之後的配置文件,一般以這種方式實現,SqlSessionFactory的創建:

SqlSessionFactoryBean是一個工廠Bean,根據配置來創建SqlSessionFactory

如果是單獨的使用手動創建SqlSessionFactory和SqlSession話流程如下
如何利用緩存機制實現JAVA類反射性能提升30倍
看完了SqlSessionFactory和SqlSession的基礎知識我們同時注意到下面這個 標籤的value屬性,“classpath:mybatis-config.xml”
如何利用緩存機制實現JAVA類反射性能提升30倍
這裏又引入了一個xml配置文件,還記得我上面剛說過spring-dao.xml是用來整合Spring和Mybatis的麼?這個mybatis-config.xml就是我們Mybatis的配置文件。

好了spring-dao.xml這個用來整合Spring和Mybatis的配置文件我們已經瞭解了,程序按着剛纔的請求接着向下走。

我們剛在走到了BookServiceImpl類的queryAllBook方法,然後該方法又是調用了bookMapper的queryAllBook方法。現在我們清楚了bookMapper的類型是BookMapper

又從sping-dao.xml的配置文件中看到了該文件的位置位於com.kuang.dao路徑下,我們現在就打開BookMapper.java文件看一看
如何利用緩存機制實現JAVA類反射性能提升30倍
我們注意到這只是個接口,我們都知道接口是不能實例化的接口只是提供一個規範,這是我們就有疑問了,那我們調用的bookMapper的queryAllBook是怎麼執行的?

我們在仔細看下dao目錄下的文件,
如何利用緩存機制實現JAVA類反射性能提升30倍
發現還有一個名字和BookMapper.java名字一樣的xml文件,我們打開看一下內容。
如何利用緩存機制實現JAVA類反射性能提升30倍
看到這個文件,雖然我們對mybatis的瞭解不多,但是我們應該大概明白了,爲什麼我們BookMapper明明只是接口,我們卻可以實例化生成BookMapper的bean並且可以調用他的方法了。

但是隻有BookMapper.java和BookMapper.xml顯然不能就是Mybatis的全部了,兩個文件之間此時除了名字相同以外還沒有什麼直接聯繫,所以我們還需要關聯起來,我們來看看mybatis-config.xml這個Mybatis的配置文件
如何利用緩存機制實現JAVA類反射性能提升30倍
我們看到了 這個標籤的resource屬性的value就是我們BookMapper.xml的路徑MyBatis 是基於 sql 映射配置的框架,sql 語句都寫在 Mapper 配置文件中,當構建 SqlSession 類之後,就需要去讀取 Mapper 配置文件中的 sql 配置。而 標籤就是用來配置需要加載的 sql 映射配置文件路徑的。

也就是說最終由我們的Spring幫我生成BookMapper的代理對象然後由Mybaits通過 標籤將BookMapper代理對象中的方法和BookMapper.xml中的配置進行一一的映射,並最終執行其中的Sql語句。

我們看到我們此次請求最終調用的BookMapper的queryAllBook方法,這時我們就需要去BookMapper.xml去尋找與之對應的Sql語句了
如何利用緩存機制實現JAVA類反射性能提升30倍
容易就找到了

我們看到最後執行的sql語句是

SELECT * from ssmbuild.books

至此我們的請求已經完成從一開始的由DispatcherServlet這個前端控制器派發給SpringMVC並最終通過Mybatis 執行我們需要對數據庫進行的操作。

生產環境的業務代碼,會比這個Demo複雜,但是整體的執行流程和思路並不會有什麼太大的變化,所以審計思路也是如此。

SSM框架有三種配置方式,即全局採用xml配置文件的形式,全局採取註解的配置方式,或者註解和xml配置文件配合使用的方式,區別只是在於寫法不一樣,執行流程不會因此發生太多改變。

2.6 審計的重點filter過濾器

此時在將web.xml時還有一個標籤說放在後面講,就是web.xml的 標籤

SpringMVC時構建於Servlet之上的,所以Servlet中的過濾器自然也是可以使用,只不過不能配置在spring-mvc.xml中,而是要直接配置在web.xml中,因爲是屬於Servlet的技術嘛。

我們重回web.xml
如何利用緩存機制實現JAVA類反射性能提升30倍
爲了方便之前的講解,我將這兩個filter註釋掉了。也就是說這兩個filter並沒有生效。我們以下面的filter-name爲XSSEscape的filter來進行講解。

首先我們此時程序是沒有XSS防護的,所以存在存儲型XSS漏洞,我們來嘗試存儲型XSS
如何利用緩存機制實現JAVA類反射性能提升30倍
我們點擊新增功能
如何利用緩存機制實現JAVA類反射性能提升30倍
看一下提交路徑
如何利用緩存機制實現JAVA類反射性能提升30倍
去後臺找與之對應的方法
如何利用緩存機制實現JAVA類反射性能提升30倍
找到後在這裏下斷點看傳入參數的詳細信息
如何利用緩存機制實現JAVA類反射性能提升30倍
看到沒有任何過濾XSS語句就這麼直接傳了進來
如何利用緩存機制實現JAVA類反射性能提升30倍
如果我們此時想要防禦這個XSS就可以在web.xml中配置上我們的
如何利用緩存機制實現JAVA類反射性能提升30倍
這裏聲明瞭了我在com.kuang.filter的包路徑下又一個類叫XssFilter是一個過濾器

下面的 屬性中的REQUEST的意思是

只要發起的操作是一次HTTP請求,比如請求某個URL、發起了一個GET請求、表單提交方式爲POST的POST請求、表單提交方式爲GET的GET請求。一次重定向則前後相當於發起了兩次請求,這些情況下有幾次請求就會走幾次指定過濾器。

屬性2.4版本的servlet中添加的新的屬性標籤,總共有四個值REQUEST,FORWARD,INCLUDE和ERROR,以下把這四個值簡單說明一下

1、REQUEST

只要發起的操作是一次HTTP請求,比如請求某個URL、發起了一個GET請求、表單提交方式爲POST的POST請求、表單提交方式爲GET的GET請求。一次重定向則前後相當於發起了兩次請求,這些情況下有幾次請求就會走幾次指定過濾器。

2、FOWARD

只有噹噹前頁面是通過請求轉發轉發過來的情形時,纔會走指定的過濾器

3、INCLUDE

只要是通過,嵌入進來的頁面,每嵌入的一個頁面,都會走一次指定的過濾器。

4、ERROR

假如web.xml裏面配置了 <error-page></error-page> :

例如

<error-page>

 <error-code>400</error-code>

 <location>/filter/error.jsp</location>

</error-page>

意思是HTTP請求響應的狀態碼只要是400、404、500三種狀態碼之一,容器就會將請求轉發到error.jsp下,這就觸發了一次error,走進了配置的DispatchFilter。需要注意的是注意一點的是,雖然把請求轉發到error.jsp是一次forward的過程,但是配置成 FORWARD 並不會走DispatchFilter這個過濾器。

這四種dispatcher方式可以單獨使用,也可以組合使用,配置多個 <dispatcher></dispatcher>即可。

在審計的時候的過濾器 <dispatcher> 屬性中使用的值也是我們關注的一個點,

<url-pattern> 屬性是指明我們要過濾訪問哪些資源的請求,“/*”的意思就是攔截所有對後臺的請求, 包括對一個簡單的對jsp頁面的GET請求,同時我們可以具體的指定攔截對某一資源的請求,同時也可以設置對某些資源的請求不過濾單獨進行放過,

舉例說明

<filter>

 <filter-name>XSSEscape</filter-name>

 <filter-class>com.springtest.filter.XssFilter</filter-class>

 </filter>

 <filter-mapping>

 <filter-name>XSSEscape</filter-name>

 <url-pattern>/com/app/UserControl</url-pattern>

 <dispatcher>REQUEST</dispatcher>

 </filter-mapping>

既然等指定單獨過濾特定資源,自然也就可以指定對特定資源的放行。

如果設置全局的資源請求過濾的話肯定是不合理的,生產環境中又很多靜態資源是不需要進行過濾的,所以我們可以指定將這些資源進行放行,

例如

<filter>

 <filter-name> XSSEscape </filter-name>

 <filter-class> com.springtest.filter.XssFilter </filter-class>

 <init-param>

 <!-- 配置不需要被登錄過濾器攔截的鏈接,只支持配後綴、前綴 及全路徑,多個配置用逗號分隔 -->

 <param-name>excludedPaths</param-name>

 <param-value>/pages/*,*.html,*.js,*.ico</param-value>

 </init-param>

</filter>

<filter-mapping>

 <filter-name> XSSEscape </filter-name>

 <url-pattern>/*</url-pattern>

</filter-mapping>

這樣我們的serlvet在路徑選擇時當有對 html js 和ico資源發起的請求就不回在將改請求轉發至XssFilter類。

我們在審計代碼時 這裏也是需要注意的一個點,因爲有可能開發人員的錯誤配置導致本應該經過過濾器的請求,錯誤的給直接放行了,這樣即使項目中有過濾器,也是不會生效的。

明白了 <filter> 標籤的作用我們就去看XssFilter這個類的內容
如何利用緩存機制實現JAVA類反射性能提升30倍
可以看到filter包下有兩個java類,我們先看XssFilter這個類
如何利用緩存機制實現JAVA類反射性能提升30倍
可以看到我們的XssFilter這個類實現了一個叫Filter的接口

我們去看一下Filter接口的源碼
如何利用緩存機制實現JAVA類反射性能提升30倍
可以看到Filter所屬的包是javax.servlet

Filter是Servlet的三大組件之一

javax.servlet.Filter 是一個接口,過濾請求,實現請求的攔截或者放行,並且添加新的功能

衆所周知接口其實就是一個標準,所以我們想要編寫自己的過濾器自然也要遵守這個標準即實現Filter這個接口。

Filter接口中有三個方法,這裏簡單介紹一下

init方法:

在創建完過濾器對象之後被調用。只執行一次

doFilter方法:

執行過濾任務方法。執行多次。

destroy方法:

Web服務器停止或者Web應用重新加載,銷燬過濾器對象。

當 Servlet 容器開始調用某個 Servlet 程序時,如果發現已經註冊了一個 Filter 程序來對該 Servlet 進行攔截,那麼容器不再直接調用 Servlet 的 service 方法,而是調用 Filter 的 doFilter 方法,再由 doFilter 方法決定是否去激活 service 方法

不難看出需要我們重點關注的方法是doFilter方法
如何利用緩存機制實現JAVA類反射性能提升30倍
這裏的request的參數和response參數可以理解成封裝了請求數據和相應數據的對象,我們需要過濾的數據就是存放在這兩個對象中,

最後一個參數FilterChain,通過名字我們猜這個參數是一個過濾鏈,查看一下FilterChain的源碼
如何利用緩存機制實現JAVA類反射性能提升30倍
看到FilterChain是一個接口,而且這個接口只有一個方法,那就是doFilter方法,FilterChain參數存在的意義就在於,在一個 Web 應用程序中可以註冊多個 Filter 程序,每個 Filter 程序都可以對一個或一組 Servlet 程序進行攔截。如果有多個 Filter 程序都可以對某個 Servlet 程序的訪問過程進行攔截,當針對該 Servlet 的訪問請求到達時,Web 容器將把這多個 Filter 程序組合成一個 Filter 鏈(也叫過濾器鏈),

Filter 鏈中的各個 Filter 的攔截順序與它們在 web.xml 文件中的映射順序一致,上一個 Filter.doFilter 方法中調用 FilterChain.doFilter 方法將激活下一個 Filter的doFilter 方法,最後一個 Filter.doFilter 方法中調用的 FilterChain.doFilter 方法將激活目標 Servlet的service 方法

只要 Filter 鏈中任意一個 Filter 沒有調用 FilterChain.doFilter 方法,則目標 Servlet 的 service 方法都不會被執行

介紹完FilterChain接下來大家應該發現,雖然名字叫過濾器

但是調用chain.dofilter方法似乎並沒有執行任何類似過濾的工作,沒有看到任何類似黑名單或者白名單的過濾規則

在調用chain.dofilter方法時我們傳遞了兩個參數進去

new X***equestWrapper((HttpServletRequest) request)和response

這就是說我們傳遞了一個X***equestWrapper對象和ServletRespons對象,我們關心的當然是這個X***equestWrapper

在傳遞參數的過程中我們通過調用X***equestWrapper的構造器,傳遞了HttpServletRequest對象,這裏簡單從繼承關係讓大家看一下HttpServletRequest和ServletRequest的關係
如何利用緩存機制實現JAVA類反射性能提升30倍
既然這裏生成了一個X***equestWrapper對象並傳入的參數那我們自然要跟進一探究竟
如何利用緩存機制實現JAVA類反射性能提升30倍
正好filter下面有一個叫X***equestWrapper的類,我們看一下代碼
如何利用緩存機制實現JAVA類反射性能提升30倍
看到這裏大家應該恍然大悟,原來過濾的行爲是在這裏進行了,而XssFilter的存在只是在鏈式執行過濾器並最終將值傳給Servlet時調用X***equestWrapper來進行過濾並獲取過濾結果而已。

這裏對過濾規則就不過多贅述,網上有很多好的過濾規則,這裏就不多提了。

這裏肯定有很多人並明白問什麼不將過濾的邏輯代碼寫在XssFilter中而是又新寫了一個類,不是多此一舉麼?

這麼做當然不是多此一舉,首先解耦這個理由就已經足夠充分了,其次我們看到X***equestWrapper繼承了一個類 HttpServletRequestWrapper

這裏我們看一下HttpServletRequestWrapper類的繼承關係
如何利用緩存機制實現JAVA類反射性能提升30倍
我們可以看到HttpServletRequestWrapper是實現了HttpServletRequest接口的,我們這裏提一下過濾這個概念,我們的想法是儘可能的把請求中的有危害的數據或者特殊符號過濾掉,然後將過濾後的數據轉發向後面的業務代碼並繼續執行,而不是說發現請求數據中有特殊字符就直接停止執行,拋出異常,返回給用戶一個400頁面,所以既然要繼續執行,那我們就要去修改或者轉義HttpServletRequest對象中的惡意數據或者特殊字符。然而HttpServletRequest對象中的數據是不允許被修改的,也就是說HttpServletRequest對象沒有提供給我們直接修改請求數據的方法。

此時矛盾就來了,我們想要修改但是HttpServletRequest對象又不給提供,所以HttpServletRequestWrapper這個類就出現了,這裏用到了常見的23中設計模式之一的裝飾者模式,限於篇幅原因不可能對裝飾者模式在進行講解了,感興趣的同學可以自己去研究。也就是說HttpServletRequestWrapper這個類的出現就是爲了給我們提供修改request請求數據的方法的,到這裏大家應該就明白了爲什麼需要單寫一個類來進行過濾的行爲,不是我們想着麼寫,而是框架就這麼設計的,爲的就是解耦。

此時當HttpServletRequestWrapper將請求中的數據過濾完,並修改完成後返回然後作爲chain.doFilter方法的形參進行傳遞。

結合之前說的,最後一個 Filter.doFilter 方法中調用的 FilterChain.doFilter 方法將激活目標 Servlet的service 方法

由於我們沒有配置第二個Filter所以XssFilter中的chain.doFilter將會激活我們Servlet的service方法即DispatcherServlet的service方法,然後數據將傳入我們的SpringMVC的Controller層交由我們的BookController來處理。

我們這次使用filter來演示一下效果
如何利用緩存機制實現JAVA類反射性能提升30倍
老地方下斷
如何利用緩存機制實現JAVA類反射性能提升30倍
然後再次執行到這裏時XSS語句中的特殊字符已經被Filter轉義。
如何利用緩存機制實現JAVA類反射性能提升30倍
自然也就不會存在Xss的問題了。

3 SSM框架審計思路總結

3.1 思路總結

最後總結一下SSM框架的審計思路,審計思路其實就是我們代碼的執行思路

和審計非SSM框架代碼的主要區別就是在於SSM框架的各種XML配置,和註解配置,需要我們根據XML中的配置和註解來查看代碼的執行路徑,SSM框架中常見的註解和註解中的屬性,以及常見的標籤和標籤的各個屬性。

審計漏洞的方式同正常的java代碼審計沒有區別,網上有很多非常優秀的java代碼審計文章,關於每個漏洞的審計方式寫的都非常全面,我們需要的就只是將其移植到SSM框架的審計中來,我們明白SSM的執行流程了,自然就明白了該怎麼在SSM框架中跟蹤參數,例如剛剛講的XSS漏洞,我們根據XML中的配置和註解中的配置一路跟到了Mybatis的mapper.xml這個映射文件,找到了最中執行的

insert into ssmbuild.books(bookName,bookCounts,detail)

 values (#{bookName}, #{bookCounts}, #{detail})

這個sql語句,發現我們傳入的books參數直到sql語句執行的前一刻都沒有經過任何的過濾處理,所以此處插入數據庫的參數自然是不可信的髒數據。

當我們再次查詢這條數據並返回到前端時就非常可能造成存儲型XSS

我們在審計這類漏洞時,最簡單的方法就是先去web.xml中去查看有沒有配置相關的過濾器,如果有哪我們就去看看過濾器的規則是否嚴格,如果沒有那就很有可能存在漏洞。

3.2 補充知識

最後還要提一個必要重要的Mybaits知識點就是Mybatis的預編譯,關於java的預編譯簡單介紹一下

非預編譯的情況下我們每次執行sql都需要將slq和參數拼接在一起然後傳給數據庫編譯執行,這樣採用拼接的方式非常容易產生SQL注入漏洞,當然可以使用filter對參數進行過濾來避免產生SQL注入,

而在預編譯的情況下,程序會提前將我們的sql語句編譯好,程序執行的時候只需要將傳遞進來的參數交由數據庫進行操作就可以了,此時不論傳來的參數是什麼,都不會被當成時SQL語句的一部分,因爲真正的SQL語句已經提前編譯好了,所以即使不過濾也不會產生SQL注入這類漏洞,

以下面這個mapper.xml中的SQL語句舉例

insert into ssmbuild.books(bookName,bookCounts,detail)

 values (#{bookName}, #{bookCounts}, #{detail})

#{bookName}這種形式的就是採用了預編譯的形式傳參,而以下這種形式

 insert into ssmbuild.books(bookName,bookCounts,detail)

 values ('${bookName}','${bookCounts}', '${detail}')

'${bookName}'這種寫法就是沒有使用預編譯的形式進行傳參數,此時如果不對傳入的參數進行過濾和校驗的話就會產生SQL注入漏洞

'${xxxx}'和#{xxxx}其實就是jdbc的Statement和PreparedStatement對象。

3.3 學習建議

整篇文章對SSM框架的整個執行流程和審計流程進行了簡單的講解,後續想要增強SSM框架的審計水平,推薦大家自己上手一些簡單SSM框架搭建的項目,實戰永遠是最快的學習方式,大家在審計SSM框架可能遇到的最大的困難就是有很多新的之前沒有碰到過的註解,和XML中一些SSM獨有的標籤,這些註解和標籤數量很多,沒有辦法在一篇文章中講完,大家碰到不懂的註解和標籤都可以通過官方提供的文檔和搜索引擎來尋找答案。

最後感謝大家的耐心觀看。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章