1. 程式人生 > >修改request的parameter的幾種方式

修改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,繼承下試一試:

  1. publicclass HttpServletRequestExtend implements HttpServletRequest {  
  2.  .......實現程式碼  
  3. }  

此時提示需要有N多方法需要被實現,例如:

getParameter、getAttribute、getAttributeNames、getCharacterEncoding、getContentLength、getContentType。。。。。。

等等幾十個方法,呵呵;

當然,你可以再構造方法裡面將實際的request物件傳遞進來,如果是相同的方法,就這個request來實現,如果需要自己處理的方法,就按照自己的方式來處理,這種包裝貌似簡單

自己定義parameter,就用一個

  1. 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本身的引數,加上外部其他自定義的引數,做成一個新的引數表。

如下所示:

  1. import javax.servlet.http.HttpServletRequest;  
  2. import javax.servlet.http.HttpServletRequestWrapper;  
  3. import java.util.HashMap;  
  4. import java.util.Map;  
  5. publicclass ParameterRequestWrapper extends HttpServletRequestWrapper {  
  6.     private Map<String , String[]> params = new HashMap<String, String[]>();  
  7.     @SuppressWarnings("unchecked")  
  8.     public ParameterRequestWrapper(HttpServletRequest request) {  
  9.         // 將request交給父類,以便於呼叫對應方法的時候,將其輸出,其實父親類的實現方式和第一種new的方式類似
  10.         super(request);  
  11.         //將引數表,賦予給當前的Map以便於持有request中的引數
  12.         this.params.putAll(request.getParameterMap());  
  13.     }  
  14.     //過載一個構造方法
  15.     public ParameterRequestWrapper(HttpServletRequest request , Map<String , Object> extendParams) {  
  16.         this(request);  
  17.         addAllParameters(extendObject);//這裡將擴充套件引數寫入引數表
  18.     }  
  19.     @Override
  20.     public String getParameter(String name) {//重寫getParameter,代表引數從當前類中的map獲取
  21.         String[]values = params.get(name);  
  22.         if(values == null || values.length == 0) {  
  23.             returnnull;  
  24.         }  
  25.         return values[0];  
  26.     }  
  27.     public String[] getParameterValues(String name) {//同上
  28.          return params.get(name);  
  29.     }  
  30.    publicvoid addAllParameters(Map<String , Object>otherParams) {//增加多個引數
  31.         for(Map.Entry<String , Object>entry : otherParams.entrySet()) {  
  32.             addParameter(entry.getKey() , entry.getValue());  
  33.         }  
  34.     }  
  35.     publicvoid addParameter(String name , Object value) {//增加引數
  36.         if(value != null) {  
  37.             if(value instanceof String[]) {  
  38.                 params.put(name , (String[])value);  
  39.             }elseif(value instanceof String) {  
  40.                 params.put(name , new String[] {(String)value});  
  41.             }else {  
  42.                 params.put(name , new String[] {String.valueOf(value)});  
  43.             }  
  44.         }  
  45.     }  
  46. }  


好了,兩種new的方式都有了,我們推薦那種?一般來說推薦第二種方式,至少他給你提供好了一些東西,不過怎麼說呢,你要明白是怎麼回事,第一種方式到第二種方式的演變是需要知道的,至少你要知道,效果是一樣的就是了,第一種方式裡面有大量的方法需要重寫,第二種不需要,這屬於設計模式的知識,我們這不詳細探討了。

接下來我們說下將new出來的request如何使用,以及【讓業務層使用到】,以及我們要說的,這種方式的【缺陷是什麼】,如何做沒有這種缺陷。

讓業務層知道的方式很簡單,最簡單的方式是:

你寫一個過濾器,在filter這個地方new了這個自己定義的request後,然後將在doFilter的時候,給的request就不是傳入的request,而是你自己new出來的,接下來所有的request都是你new出來的了,如下所示:

  1. ParameterRequestWrapper requestWrapper = new ParameterRequestWrapper((HttpServletRequest)request);  
  2. requestWrapper.addParameter("fff" , "我靠");  
  3. 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包的名稱是啥了

不過可以通過程式看下是啥:

  1. request.getClass().getResource("").getPath()   

可以得到request所在的jar包的原始檔檔案路徑。

或者這樣也可以:

  1. 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,而且存在相互呼叫)

這個類的全名就是:

  1. org.apache.catalina.connector.Request  

跟蹤進去看看他的getParameterMap方法:


可以看到如果map被lock,直接返回,若沒有,則將裡面做了一個填充的操作,然後再設定為Lock,很簡單吧。這個Lock貌似就和上面的異常有點關係了。

我們到這個parameterMap看看是什麼型別,裡面發生了什麼:

  1. protected ParameterMap parameterMap = new ParameterMap();  

那麼ParameterMap 是什麼定義的呢:

  1. publicfinalclass ParameterMap extends HashMap {  
  2.   .....  
  3. }  

有點意思了,貌似找到組織了,竟然是HashMap的兒子,還有搞不定的嘛,眼看就要一切撥開雲霧見青天了。

在看看裡面的lock到底做了啥,找個put方法:


乖乖,終於找到凶手了,再看看其他的clear方法都做了類似操作,要修改這個怎麼辦?簡單想辦法把這個Map拿到,然後setLock(false)然後就可以操作了,然後操作完再setLock(true)呵呵,怎麼獲取到這個Map呢?

getParameterMap其實就是返回了他,將他強制型別轉換為ParameterMap,貌似不靠譜,因為這個Class不在你的應用記憶體裡面,引用不到,不過可以做的是什麼反射?

呵呵!簡單來說,獲取到這個Map後,假如被命名為map

  1. Filed  lockedField = map.getClass().getDeclaredField("locked");  
  2. lockedField.setAccessible(true);//開啟訪問許可權,讓他裸奔,private型別照樣玩他
  3. lockedField.setBoolean(map, false);//將lock引數設定為false了,就是可以修改了
  4. 這下子爽了,可以呼叫map.put了  
  5. map.put("newNode" , new String[] {"阿拉拉拉"});  
  6. ....  
  7. 呼叫完了,記得:  
  8. 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)了繼續跟蹤到後面這個Reuqestorg.apache.coyote.Request)裡面去過後,看這個裡面的getParameters方法,因為可以看出Parameters獲取到,後面就是鍵值對了。

進去看下:


原來是一個屬性,看下屬性定義:

  1. private Parameters parameters = new Parameters();  

這個Parameters到底是啥東西,我能修改麼?

  1. publicfinalclass Parameters extends MultiMap {  
  2. ....  
  3. }  

沒開始那麼興奮,貌似沒見過MultiMap 是什麼。

跟蹤進去,盡然沒發現父類,正在我納悶的時候,翻看這個Parameters的原始碼的時候,發現沒用整合,用了下組合,呵呵:

  1. 相關推薦

    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‘,