修改request的parameter的幾種方式
原文地址:http://blog.csdn.net/xieyuooo/article/details/8447301
這篇文章僅僅用來參考,本身不想寫,request之所以不想讓你修改parameter的值,就是因為這個東西一般不然改,有人問我為什麼不讓改,表面上說我只能說這屬於篡改資料,因為這個使使用者的請求資料,如果被篡改就相當於篡改訊息,如果你一天給別人發訊息發的是:你好,而對方收到的是:fuck you!,你會怎麼想,呵呵!當然它主要是怕不安全把引數資料該亂了,因為程式設計師畢竟是自己寫程式,尤其是在公共程式裡面寫,後臺程式設計師發現自己的資料不對,也找不到原因;一般WEB應用會提供一個attribute來提供自己的引數設定,這樣就OK了,但是有些人就是那麼變態說為啥就不能改呢,面向物件不是相互的麼,有get應該有set的呀,我只能說,面向物件來自於生活現實,生活現實中每天逛大街,街上有很多形形色色如花似玉的,但是又可能你只能看,不能摸,更不能XX,呵呵,否則一個異常就出來了:臭流氓!
呵呵,不過就技術的角度來講,能實現嗎,當然可以,沒有不可以實現的,原始碼之下,了無祕密,這是一個大牛說的,那麼我們先來思考下有那些實現的方式:
1、我自己new一個request,然後放到容器裡頭,放那呢?等會來說,先記錄下。
2、如果我能改掉request裡面的值,那就好了唄,好的,先記錄下,等會來想怎麼改。
先說第一種方式,我自己new一個,呵呵,怎麼new,怎麼讓其他的程式知道。
new的兩種方式之一(開始思考的起源):
先說new的方式,在不知道具體的容器怎麼實現HttpSevletRequest的時候,很簡單,我自己寫個類,implements HttpServletRequest呵呵,這個貌似很簡單,OK,繼承下試一試:
- publicclass HttpServletRequestExtend implements HttpServletRequest {
- .......實現程式碼
- }
此時提示需要有N多方法需要被實現,例如:
getParameter、getAttribute、getAttributeNames、getCharacterEncoding、getContentLength、getContentType。。。。。。
等等幾十個方法,呵呵;
當然,你可以再構造方法裡面將實際的request物件傳遞進來,如果是相同的方法,就這個request來實現,如果需要自己處理的方法,就按照自己的方式來處理,這種包裝貌似簡單
自己定義parameter,就用一個
- private Map<String , String[]>paramterMap = new HashMap<String , String[]>();
就可以簡單搞定,自己再搞個addParameter方法等等,就可以實現自己的功能。
不過寫起來挺費勁的,因為意味著你所有的方法都要去實現下,除非你其他的方法都不用,只用其中幾個方法而已,這就體現出一些介面的不足了。
但是這種方式是可行的,至少可以這樣說,只是很費勁而已,因為感覺冗餘很厲害,也體現出介面的不足,和抽象類的價值,我們想要的只是過載那些我們想要過載的,原有的還是按照它原有的處理思路,此時,有一個叫HttpServletRequestWrapper的出現了;
new方式2:
繼承HttpServletRequestWrapper,其實就是上面那種方法多了一層繼承,將你的重複工作交予了它,你也可以這樣做,
全名為:javax.servlet.http.HttpServletRequestWrapper,看來也是一個擴充套件的通用介面,也就是會對request做一次包裝,OK;跟著進去發現它可以處理類似request一樣的差不多的內容,在這個基礎上做了一次包裝,你可以認為他就是對你自己new的那個,多了一層簡單擴充套件實現,而你再這個基礎上,可以繼續繼承和重寫。
OK,此時你要重寫如何重寫呢,比如我們要重寫一個getParameter方法和getParameterValues方法,其餘的方法保持和原來一致,我們在子類中,自己定義一個Map用來放參數,結合request本身的引數,加上外部其他自定義的引數,做成一個新的引數表。
如下所示:
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletRequestWrapper;
- import java.util.HashMap;
- import java.util.Map;
- publicclass ParameterRequestWrapper extends HttpServletRequestWrapper {
- private Map<String , String[]> params = new HashMap<String, String[]>();
- @SuppressWarnings("unchecked")
- public ParameterRequestWrapper(HttpServletRequest request) {
- // 將request交給父類,以便於呼叫對應方法的時候,將其輸出,其實父親類的實現方式和第一種new的方式類似
- super(request);
- //將引數表,賦予給當前的Map以便於持有request中的引數
- this.params.putAll(request.getParameterMap());
- }
- //過載一個構造方法
- public ParameterRequestWrapper(HttpServletRequest request , Map<String , Object> extendParams) {
- this(request);
- addAllParameters(extendObject);//這裡將擴充套件引數寫入引數表
- }
- @Override
- public String getParameter(String name) {//重寫getParameter,代表引數從當前類中的map獲取
- String[]values = params.get(name);
- if(values == null || values.length == 0) {
- returnnull;
- }
- return values[0];
- }
- public String[] getParameterValues(String name) {//同上
- return params.get(name);
- }
- publicvoid addAllParameters(Map<String , Object>otherParams) {//增加多個引數
- for(Map.Entry<String , Object>entry : otherParams.entrySet()) {
- addParameter(entry.getKey() , entry.getValue());
- }
- }
- publicvoid addParameter(String name , Object value) {//增加引數
- if(value != null) {
- if(value instanceof String[]) {
- params.put(name , (String[])value);
- }elseif(value instanceof String) {
- params.put(name , new String[] {(String)value});
- }else {
- params.put(name , new String[] {String.valueOf(value)});
- }
- }
- }
- }
好了,兩種new的方式都有了,我們推薦那種?一般來說推薦第二種方式,至少他給你提供好了一些東西,不過怎麼說呢,你要明白是怎麼回事,第一種方式到第二種方式的演變是需要知道的,至少你要知道,效果是一樣的就是了,第一種方式裡面有大量的方法需要重寫,第二種不需要,這屬於設計模式的知識,我們這不詳細探討了。
接下來我們說下將new出來的request如何使用,以及【讓業務層使用到】,以及我們要說的,這種方式的【缺陷是什麼】,如何做沒有這種缺陷。
讓業務層知道的方式很簡單,最簡單的方式是:
你寫一個過濾器,在filter這個地方new了這個自己定義的request後,然後將在doFilter的時候,給的request就不是傳入的request,而是你自己new出來的,接下來所有的request都是你new出來的了,如下所示:
- ParameterRequestWrapper requestWrapper = new ParameterRequestWrapper((HttpServletRequest)request);
- requestWrapper.addParameter("fff" , "我靠");
- filterChain.doFilter(requestWrapper, servletResponse);
接下來,應用使用到的request物件,通過getParameter方法就能得到一個字串叫:“我靠”,呵呵;注意,這個Fiter一定要在類似struts或者spring MVC之前處理。
還有什麼方式呢,在傳入業務層之前你還可以做AOP,如果業務層的入口方法是傳入request的;還有些特殊自理,如struts2裡面的request物件是通過:ServletActionContext.getRequest()來獲取的,而不是直接入參的,你只需要,在業務程式碼呼叫前,呼叫程式碼:
ServletActionContext.setRequest(HttpServletRequest request),引數是你自己new出來的這個request就可以了,簡單吧。方法多多,任意你選。
好,開心了一會,回到正題,有缺陷沒有,有的,肯定有的。是什麼,是什麼,是什麼?
剛才過載方法的時候,Map是自己寫的,getParameter方法、getParameterValues方法是重寫了,但是,其他的方法呢?回答是其他方法還是用request以前的值,是的,是以前的值,但是子類的Map資料有增加,request實際沒增加,當你獲取getParameterMap、getParameterNames這些方法的時候,引數就又有問題了,會不一致,這個可以自己測試,當然,最直接的解決方法是將這些方法也給換掉,也沒問題,只要你願意寫,呵呵!
接下來,我們介紹第二種方法,我不推薦使用,但是從技術角度,不得不說是一種方法,只是這種方法是讓java的安全機制在你面前裸奔,變得一絲不掛。
可能說到這裡,很多人已經知道我要說啥了,因為可以讓他變得一絲不掛的東西,沒幾樣,在這個層面,一般說的就是“反射”,是的,request既然不讓我改,那麼我又想修改,那麼我就用反射。
那麼用反射的條件是什麼?熟悉原始碼,是的,你必須看懂request怎麼獲取引數的,看原始碼容易走入誤區,雖然是錯誤的,但是我還是先說下我走入的那些個誤區,然後再來說怎麼實際的改東西。
我走入的誤區,但是也跟蹤了原始碼,因禍得福:
首先通過以下方式找到request的例項來自於哪裡,是那個類(因為HttpServletRequest是一個介面),那個jar包:
request.getClass() 就獲取到是那個類,在tomcat下,看到是:org.apache.catalina.connector.RequestFacade這個類,其實看package就基本知道jar包的名稱是啥了
不過可以通過程式看下是啥:
- request.getClass().getResource("").getPath()
可以得到request所在的jar包的原始檔檔案路徑。
或者這樣也可以:
- request.getClass().getResource("/org/apache/catalina/connector/RequestFacade.class").getPath()
一樣可以獲取到,主要要加第一個反斜槓哦,否則會認為是當前class的相對路徑的,第一個為長度為0的字串""就是指當前路徑了。
可以得到是tomcat下面的lib目錄下的catalina.jar這個包。
這些可以反編譯,也可以到官方下載原始碼,我們下面來看看原始碼:
我當時第一理解是getParameterMap獲取的map和getParameter時獲取引數的位置是一樣的,然後,我就想嘗試去修改這個Map,可惜當然獲取到這個map的時候,發生put、remove這些操作的時候,直接丟擲異常:
IllegalStateException內容裡面會提示:parameterMap.locked這樣的字樣在裡面,為啥呢,我們進去看看:
先看看getParameterMap這個方法:
那麼這個request是什麼呢?看到定義:
protected Request request = null;
發現上面沒有import,那就應該是同一層包下面的Request類(我沒有直接跟蹤進去就是想要讓大家知道雖然簡單,但是容易混淆,在tomcat原始碼中,不止有一個類叫Request,而且存在相互呼叫)
這個類的全名就是:
- org.apache.catalina.connector.Request
跟蹤進去看看他的getParameterMap方法:
可以看到如果map被lock,直接返回,若沒有,則將裡面做了一個填充的操作,然後再設定為Lock,很簡單吧。這個Lock貌似就和上面的異常有點關係了。
我們到這個parameterMap看看是什麼型別,裡面發生了什麼:
- protected ParameterMap parameterMap = new ParameterMap();
那麼ParameterMap 是什麼定義的呢:
- publicfinalclass ParameterMap extends HashMap {
- .....
- }
有點意思了,貌似找到組織了,竟然是HashMap的兒子,還有搞不定的嘛,眼看就要一切撥開雲霧見青天了。
在看看裡面的lock到底做了啥,找個put方法:
乖乖,終於找到凶手了,再看看其他的clear方法都做了類似操作,要修改這個怎麼辦?簡單想辦法把這個Map拿到,然後setLock(false)然後就可以操作了,然後操作完再setLock(true)呵呵,怎麼獲取到這個Map呢?
getParameterMap其實就是返回了他,將他強制型別轉換為ParameterMap,貌似不靠譜,因為這個Class不在你的應用記憶體裡面,引用不到,不過可以做的是什麼反射?
呵呵!簡單來說,獲取到這個Map後,假如被命名為map
- Filed lockedField = map.getClass().getDeclaredField("locked");
- lockedField.setAccessible(true);//開啟訪問許可權,讓他裸奔,private型別照樣玩他
- lockedField.setBoolean(map, false);//將lock引數設定為false了,就是可以修改了
- 這下子爽了,可以呼叫map.put了
- map.put("newNode" , new String[] {"阿拉拉拉"});
- ....
- 呼叫完了,記得:
- lockedField.setBoolean(map, true);
否則看上述程式碼,發現lock是false,會重新初始化,你的設定就悲劇了。
OK,這個時候發現,request.getParameterMap對了,可是其他的貌似不對,getParameter、getParameterValues、getParameterNames這幾個都不對;
最後我發現我走錯了,下面開始糾正錯誤了:
跟蹤另外幾個方法進去:
發現也是在這個request裡面,進去看看:
發現又出來一個coyoteRequest,又是哪裡冒出來的:看定義:
protected org.apache.coyote.Request coyoteRequest;
這個類竟然也叫Request,而且是包裝在現在Request類裡面的,這就是為什麼開始我要說全名(org.apache.catalina.connector.Request)了繼續跟蹤到後面這個Reuqest(org.apache.coyote.Request)裡面去過後,看這個裡面的getParameters方法,因為可以看出Parameters獲取到,後面就是鍵值對了。
進去看下:
原來是一個屬性,看下屬性定義:
- private Parameters parameters = new Parameters();
這個Parameters到底是啥東西,我能修改麼?
- publicfinalclass Parameters extends MultiMap {
- ....
- }
沒開始那麼興奮,貌似沒見過MultiMap 是什麼。
跟蹤進去,盡然沒發現父類,正在我納悶的時候,翻看這個Parameters的原始碼的時候,發現沒用整合,用了下組合,呵呵:
-
相關推薦
Java 修改編碼格式的幾種方式
格式 text cnblogs 修改 .com pac 方式 src -1 1、工作空間 workspase Window→Preferences→General→Workspace→Text file encoding→other→UTF-8 2、項目編碼格式 右鍵項目
MySQL 修改 root 密碼 的幾種方式
當前 修改 col color pass table date nbsp password 方法1: 用SET PASSWORD命令 mysql -u root mysql> SET PASSWORD FOR ‘root‘@‘localhost‘ = PASS
如何修改request的parameter的幾種方式
《Java特種兵 (上冊)》 關於本書的一些資料: 如果對此書有興趣的小夥伴,可以通過以下連結購買: 關於本書,小胖只針對特定的人群寫書,只希望適合此書的人在此書得到合適的內容,小胖接受建設性意見,但不是服務員,在寫作手法上不會去照顧一些人的品味問題,而且小胖僅代表個人寫
oralce查詢表修改記錄的幾種方式
(1)SELECT ID,NAME,state,VERSIONS_ENDTIME,VERSIONS_OPERATION FROM table_name VERSIONS BETWEEN TIMES
修改request的parameter的幾種方式
原文地址:http://blog.csdn.net/xieyuooo/article/details/8447301這篇文章僅僅用來參考,本身不想寫,request之所以不想讓你修改parameter的值,就是因為這個東西一般不然改,有人問我為什麼不讓改,表面上說我只能說這屬
Eclipse安裝svn插件的幾種方式 轉帖....
如果 version name feature help sin 鏈接 exe 文件 Eclipse安裝svn插件的幾種方式 1.在線安裝: (1).點擊 Help --> Install New Software... (2).在彈出的窗口中點擊add按鈕,輸
解決瀏覽器跨域的幾種方式
doc cor 求和 對象 跨域 http onf 從服務器 console 1、什麽是跨域問題 在頁面中使用js訪問其他網站的數據時,就會出現跨域問題,比如在網站中使用ajax請求其他網站的天氣、快遞或者其他數據接口時,以及hybrid app中請求數據,
前端跨域幾種方式
div ner dev 修改 ati hash 標簽 nbsp 端口 跨域問題的直接原因是瀏覽器存在同源策略,瀏覽器同源指的是:兩個頁面的協議、端口和主機相同,則兩個頁面具有相同的源。IE下滿足協議、主機相同,就認為是同源。 想象一下,如果沒有同源策略,誰都可以修改你站點
Python 與 C/C++ 交互的幾種方式
pythonpython作為一門腳本語言,其好處是語法簡單,很多東西都已經封裝好了,直接拿過來用就行,所以實現同樣一個功能,用Python寫要比用C/C++代碼量會少得多。但是優點也必然也伴隨著缺點(這是肯定的,不然還要其他語言幹嘛),python最被人詬病的一個地方可能就是其運行速度了。這這是大部分腳本語言
php中實現頁面跳轉的幾種方式
腳本 timeout location clas replace asc idt lee 實現 親測,not復制粘貼 PHP中實現頁面跳轉有一下幾種方式,看了幾個人寫的不是很條理,自己整理一下 在PHP腳本代碼中實現 <?php header("locati
Oracle數據庫遷移的幾種方式
備份與恢復 行遷移 target span spf 位置 server create 設備 面試: 一、exp/imp邏輯備份與恢復: 二、Storage存儲遷移: 將數據文件、控制文件、日誌文件、spfile掛到新機器上,然後在新機器上啟動數據庫。 三、利用data gu
C#打開SDE數據庫的幾種方式總結
tex 用戶 ops 總結 param word editor conn tor 轉自謝燦軟件原文 C#打開SDE數據庫的幾種方式總結 1.通過指定連接屬性參數打開數據庫 /// <param name="server">數據庫服務器名&
數組去重的幾種方式
strong class 一個 spl spa cnblogs 不變 數字 {} 一、利用indexOf查找,ie9以下不兼容 function noRepeat(ary) { if (ary instanceof Array) { var new
即時通信常見的幾種方式,此處只做學習記錄
維護 時間 最簡 安裝 記錄 htm websocket 雙向 new 1. 輪詢 利用ajax每隔一段時間就請求一次服務器,服務器返回數據。 優點:最簡單的解決方案 缺點:對服務器壓力很大,浪費帶寬 2. 長輪詢 利用ajax請求服務器,當有數據變化
IOC創建對象的幾種方式
pri clas ati div 參數 system 實例方法 tex 通過 接上一篇IOC入門 IOC創建對象的幾種方式 1)調用無參數構造器 2)帶參數構造器 3)工廠創建對象 工廠類:靜態方法創建對象 工廠類:非靜態方法創建對象 1、對之前的User類
遍歷Map集合的幾種方式
set password stat class ati put 獲取 map hashmap 1 import java.util.HashMap; 2 import java.util.Iterator; 3 import java.util.Map; 4 im
C++多態有哪幾種方式?
cti 早綁定 時間 對象 區別 父類 不同的 版本 內幕 C++多態方式: (1)靜態多態(重載,模板) 是在編譯的時候,就確定調用函數的類型。 (2)動態多態(覆蓋,虛函數實現) 在運行的時候,才確定調用的是哪個函數,動態綁定。運行基類指針指向派生類的對象,並調用派生類
JS創建對象的幾種方式詳解
演員 sta say object ron 操作 tar obj 構造 Js是一門面向對象的語言,裏面沒有類的思想,所以直接是創建對象,下面介紹幾種創建對象的方法: 1.對象字面量的方法:記住鍵值對格式:{key:value,key :value} 實例: Var
使用 Hive裝載數據的幾種方式
rom art lec install 查詢語句 如果 mode lena 重寫 裝載數據 1、以LOAD的方式裝載數據 LOAD DATA [LOCAL] INPATH ‘filepath‘ [OVERWRITE] INTO TABLE tablename [PARTIT
生成PDF的幾種方式
pdf、php、web、js、wkhtmltox1、後臺生成PDFthinkphp利用MPDF插件示例代碼:public function pdf(){ //引入類庫 Vendor(‘mpdf.mpdf‘); //設置中文編碼 $mpdf=new \mPDF(‘zh-cn‘,‘A4‘,