亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

修改request的parameter的幾種方式總結(jié)

 更新時(shí)間:2021年08月12日 10:03:08   作者:xieyu_zy  
這篇文章主要介紹了修改request的parameter的幾種方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

修改request的parameter的幾種方式總結(jié)

這篇文章僅僅用來參考,本身不想寫,request之所以不想讓你修改parameter的值,就是因?yàn)檫@個(gè)東西一般不然改,有人問我為什么不讓改,表面上說我只能說這屬于篡改數(shù)據(jù),因?yàn)檫@個(gè)使用戶的請(qǐng)求數(shù)據(jù),如果被篡改就相當(dāng)于篡改消息,如果你一天給別人發(fā)消息發(fā)的是:你好,而對(duì)方收到的是:fuck you!,你會(huì)怎么想,呵呵!當(dāng)然它主要是怕不安全把參數(shù)數(shù)據(jù)該亂了,因?yàn)槌绦騿T畢竟是自己寫程序,尤其是在公共程序里面寫,后臺(tái)程序員發(fā)現(xiàn)自己的數(shù)據(jù)不對(duì),也找不到原因;一般WEB應(yīng)用會(huì)提供一個(gè)attribute來提供自己的參數(shù)設(shè)置,這樣就OK了,但是有些人就是那么變態(tài)說為啥就不能改呢,面向?qū)ο蟛皇窍嗷サ拿?,有g(shù)et應(yīng)該有set的呀,我只能說,面向?qū)ο髞碜杂谏瞵F(xiàn)實(shí),生活現(xiàn)實(shí)中每天逛大街,街上有很多形形色色如花似玉的,但是又可能你只能看,不能摸,更不能XX,呵呵,否則一個(gè)異常就出來了:臭流氓!

呵呵,不過就技術(shù)的角度來講,能實(shí)現(xiàn)嗎,當(dāng)然可以,沒有不可以實(shí)現(xiàn)的,源碼之下,了無秘密,這是一個(gè)大牛說的,

那么我們先來思考下有那些實(shí)現(xiàn)的方式:

1、我自己new一個(gè)request,然后放到容器里頭,放那呢?等會(huì)來說,先記錄下。

2、如果我能改掉request里面的值,那就好了唄,好的,先記錄下,等會(huì)來想怎么改。

先說第一種方式,我自己new一個(gè),呵呵,怎么new,怎么讓其他的程序知道。

new的兩種方式之一(開始思考的起源):

先說new的方式,在不知道具體的容器怎么實(shí)現(xiàn)HttpSevletRequest的時(shí)候,很簡(jiǎn)單,我自己寫個(gè)類,implements HttpServletRequest呵呵,這個(gè)貌似很簡(jiǎn)單,OK,繼承下試一試:

public class HttpServletRequestExtend implements HttpServletRequest {
 .......實(shí)現(xiàn)代碼
}

此時(shí)提示需要有N多方法需要被實(shí)現(xiàn),例如:

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

等等幾十個(gè)方法,呵呵;

當(dāng)然,你可以再構(gòu)造方法里面將實(shí)際的request對(duì)象傳遞進(jìn)來,如果是相同的方法,就這個(gè)request來實(shí)現(xiàn),如果需要自己處理的方法,就按照自己的方式來處理,這種包裝貌似簡(jiǎn)單

自己定義parameter,就用一個(gè)

private Map<String , String[]>paramterMap = new HashMap<String , String[]>();

就可以簡(jiǎn)單搞定,自己再搞個(gè)addParameter方法等等,就可以實(shí)現(xiàn)自己的功能。

不過寫起來挺費(fèi)勁的,因?yàn)橐馕吨闼械姆椒ǘ家?shí)現(xiàn)下,除非你其他的方法都不用,只用其中幾個(gè)方法而已,這就體現(xiàn)出一些接口的不足了。

但是這種方式是可行的,至少可以這樣說,只是很費(fèi)勁而已,因?yàn)楦杏X冗余很厲害,也體現(xiàn)出接口的不足,和抽象類的價(jià)值,我們想要的只是重載那些我們想要重載的,原有的還是按照它原有的處理思路,此時(shí),有一個(gè)叫HttpServletRequestWrapper的出現(xiàn)了;

new方式2:

繼承HttpServletRequestWrapper,其實(shí)就是上面那種方法多了一層繼承,將你的重復(fù)工作交予了它,你也可以這樣做,

全名為:javax.servlet.http.HttpServletRequestWrapper,看來也是一個(gè)擴(kuò)展的通用接口,也就是會(huì)對(duì)request做一次包裝,OK;跟著進(jìn)去發(fā)現(xiàn)它可以處理類似request一樣的差不多的內(nèi)容,在這個(gè)基礎(chǔ)上做了一次包裝,你可以認(rèn)為他就是對(duì)你自己new的那個(gè),多了一層簡(jiǎn)單擴(kuò)展實(shí)現(xiàn),而你再這個(gè)基礎(chǔ)上,可以繼續(xù)繼承和重寫。

OK,此時(shí)你要重寫如何重寫呢,比如我們要重寫一個(gè)getParameter方法和getParameterValues方法,其余的方法保持和原來一致,我們?cè)谧宇愔?,自己定義一個(gè)Map用來放參數(shù),結(jié)合request本身的參數(shù),加上外部其他自定義的參數(shù),做成一個(gè)新的參數(shù)表。

如下所示:

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.HashMap;
import java.util.Map;
public class ParameterRequestWrapper extends HttpServletRequestWrapper {    
    private Map<String , String[]> params = new HashMap<String, String[]>();  
    @SuppressWarnings("unchecked")
    public ParameterRequestWrapper(HttpServletRequest request) {
        // 將request交給父類,以便于調(diào)用對(duì)應(yīng)方法的時(shí)候,將其輸出,其實(shí)父親類的實(shí)現(xiàn)方式和第一種new的方式類似
        super(request);
        //將參數(shù)表,賦予給當(dāng)前的Map以便于持有request中的參數(shù)
        this.params.putAll(request.getParameterMap());
    }
    //重載一個(gè)構(gòu)造方法
    public ParameterRequestWrapper(HttpServletRequest request , Map<String , Object> extendParams) {
        this(request);
        addAllParameters(extendObject);//這里將擴(kuò)展參數(shù)寫入?yún)?shù)表
    }
    
    @Override
    public String getParameter(String name) {//重寫getParameter,代表參數(shù)從當(dāng)前類中的map獲取
        String[]values = params.get(name);
        if(values == null || values.length == 0) {
            return null;
        }
        return values[0];
    }
    
    public String[] getParameterValues(String name) {//同上
         return params.get(name);
    }
 
   public void addAllParameters(Map<String , Object>otherParams) {//增加多個(gè)參數(shù)
        for(Map.Entry<String , Object>entry : otherParams.entrySet()) {
            addParameter(entry.getKey() , entry.getValue());
        }
    } 
 
    public void addParameter(String name , Object value) {//增加參數(shù)
        if(value != null) {
            if(value instanceof String[]) {
                params.put(name , (String[])value);
            }else if(value instanceof String) {
                params.put(name , new String[] {(String)value});
            }else {
                params.put(name , new String[] {String.valueOf(value)});
            }
        }
    }
}

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

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

讓業(yè)務(wù)層知道的方式很簡(jiǎn)單,最簡(jiǎn)單的方式是:

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

ParameterRequestWrapper requestWrapper = new ParameterRequestWrapper((HttpServletRequest)request);
requestWrapper.addParameter("fff" , "我靠");
filterChain.doFilter(requestWrapper, servletResponse);

接下來,應(yīng)用使用到的request對(duì)象,通過getParameter方法就能得到一個(gè)字符串叫:“我靠”,呵呵;注意,這個(gè)Fiter一定要在類似struts或者spring MVC之前處理。

還有什么方式呢,在傳入業(yè)務(wù)層之前你還可以做AOP,如果業(yè)務(wù)層的入口方法是傳入request的;還有些特殊自理,如struts2里面的request對(duì)象是通過:ServletActionContext.getRequest()來獲取的,而不是直接入?yún)⒌?,你只需要,在業(yè)務(wù)代碼調(diào)用前,調(diào)用代碼:

ServletActionContext.setRequest(HttpServletRequest request),參數(shù)是你自己new出來的這個(gè)request就可以了,簡(jiǎn)單吧。方法多多,任意你選。

好,開心了一會(huì),回到正題,有缺陷沒有,有的,肯定有的。是什么,是什么,是什么?

剛才重載方法的時(shí)候,Map是自己寫的,getParameter方法、getParameterValues方法是重寫了,但是,其他的方法呢?回答是其他方法還是用request以前的值,是的,是以前的值,但是子類的Map數(shù)據(jù)有增加,request實(shí)際沒增加,當(dāng)你獲取getParameterMap、getParameterNames這些方法的時(shí)候,參數(shù)就又有問題了,會(huì)不一致,這個(gè)可以自己測(cè)試,當(dāng)然,最直接的解決方法是將這些方法也給換掉,也沒問題,只要你愿意寫,呵呵!

接下來,我們介紹第二種方法,我不推薦使用,但是從技術(shù)角度,不得不說是一種方法,只是這種方法是讓java的安全機(jī)制在你面前裸奔,變得一絲不掛。

可能說到這里,很多人已經(jīng)知道我要說啥了,因?yàn)榭梢宰屗兊靡唤z不掛的東西,沒幾樣,在這個(gè)層面,一般說的就是“反射”,是的,request既然不讓我改,那么我又想修改,那么我就用反射。

那么用反射的條件是什么?熟悉源碼,是的,你必須看懂request怎么獲取參數(shù)的,看源碼容易走入誤區(qū),雖然是錯(cuò)誤的,但是我還是先說下我走入的那些個(gè)誤區(qū),然后再來說怎么實(shí)際的改東西。

我走入的誤區(qū),但是也跟蹤了源碼,因禍得福:

首先通過以下方式找到request的實(shí)例來自于哪里,是那個(gè)類(因?yàn)镠ttpServletRequest是一個(gè)接口),那個(gè)jar包:

request.getClass() 就獲取到是那個(gè)類,在tomcat下,看到是:org.apache.catalina.connector.RequestFacade這個(gè)類,其實(shí)看package就基本知道jar包的名稱是啥了

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

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

可以得到request所在的jar包的源文件文件路徑。

或者這樣也可以:

request.getClass().getResource("/org/apache/catalina/connector/RequestFacade.class").getPath()

一樣可以獲取到,主要要加第一個(gè)反斜杠哦,否則會(huì)認(rèn)為是當(dāng)前class的相對(duì)路徑的,第一個(gè)為長(zhǎng)度為0的字符串""就是指當(dāng)前路徑了。

可以得到是tomcat下面的lib目錄下的catalina.jar這個(gè)包。

這些可以反編譯,也可以到官方下載源碼,我們下面來看看源碼:

我當(dāng)時(shí)第一理解是getParameterMap獲取的map和getParameter時(shí)獲取參數(shù)的位置是一樣的,然后,我就想嘗試去修改這個(gè)Map,可惜當(dāng)然獲取到這個(gè)map的時(shí)候,發(fā)生put、remove這些操作的時(shí)候,直接拋出異常:

IllegalStateException內(nèi)容里面會(huì)提示:parameterMap.locked這樣的字樣在里面,為啥呢,我們進(jìn)去看看:

先看看getParameterMap這個(gè)方法:

那么這個(gè)request是什么呢?看到定義:

protected Request request = null;

發(fā)現(xiàn)上面沒有import,那就應(yīng)該是同一層包下面的Request類(我沒有直接跟蹤進(jìn)去就是想要讓大家知道雖然簡(jiǎn)單,但是容易混淆,在tomcat源碼中,不止有一個(gè)類叫Request,而且存在相互調(diào)用)

這個(gè)類的全名就是:

org.apache.catalina.connector.Request

跟蹤進(jìn)去看看他的getParameterMap方法:

可以看到如果map被lock,直接返回,若沒有,則將里面做了一個(gè)填充的操作,然后再設(shè)置為L(zhǎng)ock,很簡(jiǎn)單吧。這個(gè)Lock貌似就和上面的異常有點(diǎn)關(guān)系了。

我們到這個(gè)parameterMap看看是什么類型,里面發(fā)生了什么:

protected ParameterMap parameterMap = new ParameterMap();

那么ParameterMap 是什么定義的呢:

public final class ParameterMap extends HashMap {
  .....
}

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

在看看里面的lock到底做了啥,找個(gè)put方法:

乖乖,終于找到兇手了,再看看其他的clear方法都做了類似操作,要修改這個(gè)怎么辦?簡(jiǎn)單想辦法把這個(gè)Map拿到,然后setLock(false)然后就可以操作了,然后操作完再setLock(true)呵呵,怎么獲取到這個(gè)Map呢?

getParameterMap其實(shí)就是返回了他,將他強(qiáng)制類型轉(zhuǎn)換為ParameterMap,貌似不靠譜,因?yàn)檫@個(gè)Class不在你的應(yīng)用內(nèi)存里面,引用不到,不過可以做的是什么反射?

呵呵!簡(jiǎn)單來說,獲取到這個(gè)Map后,假如被命名為map

Filed  lockedField = map.getClass().getDeclaredField("locked");
lockedField.setAccessible(true);//打開訪問權(quán)限,讓他裸奔,private類型照樣玩他
lockedField.setBoolean(map, false);//將lock參數(shù)設(shè)置為false了,就是可以修改了
這下子爽了,可以調(diào)用map.put了
map.put("newNode" , new String[] {"阿拉拉拉"});
....
調(diào)用完了,記得:
lockedField.setBoolean(map, true);

否則看上述代碼,發(fā)現(xiàn)lock是false,會(huì)重新初始化,你的設(shè)置就悲劇了。

OK,這個(gè)時(shí)候發(fā)現(xiàn),request.getParameterMap對(duì)了,可是其他的貌似不對(duì),getParameter、getParameterValues、getParameterNames這幾個(gè)都不對(duì);

最后我發(fā)現(xiàn)我走錯(cuò)了,下面開始糾正錯(cuò)誤了:

跟蹤另外幾個(gè)方法進(jìn)去:

發(fā)現(xiàn)也是在這個(gè)request里面,進(jìn)去看看:

發(fā)現(xiàn)又出來一個(gè)coyoteRequest,又是哪里冒出來的:看定義:

protected org.apache.coyote.Request coyoteRequest;

這個(gè)類竟然也叫Request,而且是包裝在現(xiàn)在Request類里面的,這就是為什么開始我要說全名(org.apache.catalina.connector.Request)了繼續(xù)跟蹤到后面這個(gè)Reuqest(org.apache.coyote.Request)里面去過后,看這個(gè)里面的getParameters方法,因?yàn)榭梢钥闯鯬arameters獲取到,后面就是鍵值對(duì)了。

進(jìn)去看下:

原來是一個(gè)屬性,看下屬性定義:

private Parameters parameters = new Parameters();

這個(gè)Parameters到底是啥東西,我能修改么?

public final class Parameters extends MultiMap {
....
}

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

跟蹤進(jìn)去,盡然沒發(fā)現(xiàn)父類,正在我納悶的時(shí)候,翻看這個(gè)Parameters的源碼的時(shí)候,發(fā)現(xiàn)沒用集成,用了下組合,呵呵:

private Hashtable<String,String[]> paramHashStringArray =
        new Hashtable<String,String[]>();

和我想想的差不多,再看看方法,有個(gè)addParameterValues,估計(jì)它就是用這個(gè)方法來設(shè)置參數(shù)的:

再確認(rèn)下,發(fā)現(xiàn)getParameter、getParameterValues、getParameterNames都間接會(huì)直接調(diào)用這個(gè)hashtable;

這下笑了,因?yàn)檎业搅耍涂梢宰屗惚肌?/p>

要么找到這個(gè)parameter對(duì)象,然后調(diào)用方法addParam、addParameterValues這些方法,不過貌似要remove不行,還有,這個(gè)addParam通過上圖可以看到,如果同一個(gè)Key,存在參數(shù),不是替換,而是將結(jié)果的數(shù)組擴(kuò)大,要替換還是不行,所以拿到paramHashStringArray這個(gè)值就可以干任何事情了,呵呵!

好,我們簡(jiǎn)單寫個(gè)測(cè)試代碼,放在一個(gè)filter里面:

try {
            Class clazz = request.getClass();
            Field requestField = clazz.getDeclaredField("request");
            requestField.setAccessible(true);
            Object innerRequest = requestField.get(request);//獲取到request對(duì)象 
 
            //設(shè)置尚未初始化 (否則在獲取一些參數(shù)的時(shí)候,可能會(huì)導(dǎo)致不一致)
            Field field = innerRequest.getClass().getDeclaredField("parametersParsed");
            field.setAccessible(true);
            field.setBoolean(innerRequest , false); 
 
            Field coyoteRequestField = innerRequest.getClass().getDeclaredField("coyoteRequest");
            coyoteRequestField.setAccessible(true);
            Object coyoteRequestObject = coyoteRequestField.get(innerRequest);//獲取到coyoteRequest對(duì)象 
 
            Field parametersField = coyoteRequestObject.getClass().getDeclaredField("parameters");
            parametersField.setAccessible(true);
            Object parameterObject = parametersField.get(coyoteRequestObject);//獲取到parameter的對(duì)象
//獲取hashtable來完成對(duì)參數(shù)變量的修改
            Field hashTabArrField = parameterObject.getClass().getDeclaredField("paramHashStringArray");
            hashTabArrField.setAccessible(true);
            @SuppressWarnings("unchecked")
            Map<String,String[]> map = (Map<String,String[]>)hashTabArrField.get(parameterObject);
            map.put("fuck" , new String[] {"fuck you"});
//也可以通過下面的方法,不過下面的方法只能添加參數(shù),如果有相同的key,會(huì)追加參數(shù),即,同一個(gè)key的結(jié)果集會(huì)有多個(gè)
//            Method method = parameterObject.getClass().getDeclaredMethod("addParameterValues" , String.class , String[].class);
//            method.invoke(parameterObject , "fuck" , new String[] {"fuck you!" , "sssss"}); 
 
        } catch (Exception e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        System.out.println(request.getParameter("fuck"));

此時(shí)getParameter就能獲取到寫進(jìn)去的值了哦,getParameterValues也是可以的;這種方式改掉的參數(shù),所有的getParameterMap(還沒調(diào)用過這個(gè)方法之前執(zhí)行上面的代碼)、getParameterNames全部都會(huì)被改掉。

測(cè)試OK了,發(fā)現(xiàn)上面的代碼有點(diǎn)亂,整理下,至少初始化的時(shí)候可以省掉很多反射的代碼:

定義幾個(gè)靜態(tài)變量,初始化的時(shí)候,暫時(shí)先別做任何動(dòng)作:

    private static Field requestField;    
    private static Field parametersParsedField;    
    private static Field coyoteRequestField;    
    private static Field parametersField;    
    private static Field hashTabArrField;

在static或放在filter的init方法中去執(zhí)行:

try {
            Class clazz = Class.forName("org.apache.catalina.connector.RequestFacade");
            requestField = clazz.getDeclaredField("request");
            requestField.setAccessible(true); 
 
            parametersParsedField = requestField.getType().getDeclaredField("parametersParsed");
            parametersParsedField.setAccessible(true); 
 
            coyoteRequestField = requestField.getType().getDeclaredField("coyoteRequest");
            coyoteRequestField.setAccessible(true); 
 
            parametersField = coyoteRequestField.getType().getDeclaredField("parameters");
            parametersField.setAccessible(true); 
 
            hashTabArrField = parametersField.getType().getDeclaredField("paramHashStringArray");
            hashTabArrField.setAccessible(true);
        } catch (Exception e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }

這段代碼執(zhí)行后,反射的很多代碼就省下來了;

OK,生下來就是調(diào)用了,調(diào)用的時(shí)候,如果放在Utils里面,就提供靜態(tài)方法,放在Filter里面隨你,反正filter是單例的:

我們就想得到那個(gè)Map,所以就提供一個(gè):getRequestMap方法就O了:

@SuppressWarnings("unchecked")
    private Map<String , String[]> getRequestMap(ServletRequest request) {
        try {
            Object innerRequest = requestField.get(request);
            parametersParsedField.setBoolean(innerRequest, true);
            Object coyoteRequestObject = coyoteRequestField.get(innerRequest);
            Object parameterObject = parametersField.get(coyoteRequestObject);
            return (Map<String,String[]>)hashTabArrField.get(parameterObject);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            return Collections.emptyMap();
        }
    }

doFilter的時(shí)候,調(diào)用下:

Map<String , String[]> map = getRequestMap(request);
        if(map != null) {
            map.put("fuck" , new String[] {"fuck you!"});
        }

你就可以瘋狂設(shè)置你的參數(shù)了,呵呵,這個(gè)程序開始裸奔了,你clear掉,后臺(tái)的人瘋了,小心被槍斃,在一些特殊應(yīng)用中,你可以嘗試去修改一些值達(dá)到一些特殊的目的,所以裸奔還是有意義的,呵呵!

最后再補(bǔ)充一種方式是:

MockHttpServletRequest,全名為:org.springframework.mock.web.MockHttpServletRequest,是spring提供的,前提是你用了spring 2.5或更高的版本,另外需要注意的是,這個(gè)spring僅僅提供一個(gè)模擬的request,所以里面有些東西可能獲取的內(nèi)容并不是你特別想要的,他實(shí)現(xiàn)的方式和第一中方式類似,通常用在測(cè)試框架中。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論