如何修改request的parameter的⼏种⽅式
这篇⽂章仅仅⽤来参考,本⾝不想写,request之所以不想让你修改parameter的值,就是因为这个东西⼀般不然改,有⼈问我为什么不让改,表⾯上说我只能说这属于篡改数据,因为这个使⽤户的请求数据,如果被篡改就相当于篡改消息,如果你⼀天给别⼈发消息发的是:你好,⽽对⽅收到的是:fuck you!,你会怎么想,呵呵!当然它主要是怕不安全把参数数据该乱了,因为程序员毕竟是⾃⼰写程序,尤其是在公共程序⾥⾯写,后台程序员发现⾃⼰的数据不对,也不到原因;⼀般WEB应⽤会提供⼀个attribute来提供⾃⼰的参数设置,这样就OK了,但是有些⼈就是那么变态说为啥就不能改呢,⾯向对象不是相互的么,有get应该有set的呀,我只能说,⾯向对象来⾃于⽣活现实,⽣活现实中每天逛⼤街,街上有很多形形⾊⾊如花似⽟的,但是⼜可能你只能看,不能摸,更不能XX,呵呵,否则⼀个异常就出来了:臭流氓!
呵呵,不过就技术的⾓度来讲,能实现吗,当然可以,没有不可以实现的,源码之下,了⽆秘密,这是⼀个⼤⽜说的,那么我们先来思考下有那些实现的⽅式:
1、我⾃⼰new⼀个request,然后放到容器⾥头,放那呢?等会来说,先记录下。
2、如果我能改掉request⾥⾯的值,那就好了呗,好的,先记录下,等会来想怎么改。
先说第⼀种⽅式,我⾃⼰new⼀个,呵呵,怎么new,怎么让其他的程序知道。
new的两种⽅式之⼀(开始思考的起源):
先说new的⽅式,在不知道具体的容器怎么实现HttpSevletRequest的时候,很简单,我⾃⼰写个类,implements HttpServletRequest呵呵,这个貌似很简单,OK,继承下试⼀试:
public class 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;
public class 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.ParameterMap());
}
//重载⼀个构造⽅法
public ParameterRequestWrapper(HttpServletRequest request , Map<String , Object> extendParams) {
this(request);
addAllParameters(extendObject);//这⾥将扩展参数写⼊参数表
}
@Override
public String getParameter(String name) {//重写getParameter,代表参数从当前类中的map获取
String[]values = (name);
if(values == null || values.length == 0) {
return null;
}
return values[0];
}
public String[] getParameterValues(String name) {//同上
(name);
}
public void addAllParameters(Map<String , Object>otherParams) {//增加多个参数
for(Map.Entry<String , Object>entry : Set()) {
Key() , Value());
}
}
public void addParameter(String name , Object value) {//增加参数
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的⽅式都有了,我们推荐那种?⼀般来说推荐第⼆种⽅式,⾄少他给你提供好了⼀些东西,不过怎么说呢,你要明⽩是怎么回事,第⼀种⽅式到第⼆种⽅式的演变是需要知道的,⾄少你要知道,效果是⼀样的就是了,第⼀种⽅式⾥⾯有⼤量的⽅法需要重写,第⼆种不需要,这属于设计模式的知识,我们这不详细探讨了。
接下来我们说下将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对象是通过:Request()来获取的,⽽不是直接⼊参的,你只需要,在业务代码调⽤前,调⽤代码:
ServletActionContext.setRequest(HttpServletRequest request),参数是你⾃⼰new出来的这个request就可以了,简单吧。⽅法多多,任意你选。
好,开⼼了⼀会,回到正题,有缺陷没有,有的,肯定有的。是什么,是什么,是什么?
刚才重载⽅法的时候,Map是⾃⼰写的,getParameter⽅法、getParameterValues⽅法是重写了,但是,其他的⽅法呢?回答是其他⽅法还是⽤request以前的值,是的,是以前的值,但是⼦类的Map数据有增加,request实际没增加,当你获取getParameterMap、getParameterNames这些⽅法的时候,参数就⼜有问题了,会不⼀致,这个可以⾃⼰测试,当然,最直接的解决⽅法是将这些⽅法也给换掉,也没问题,只要你愿意写,呵呵!
接下来,我们介绍第⼆种⽅法,我不推荐使⽤,但是从技术⾓度,不得不说是⼀种⽅法,只是这种⽅法是让java的安全机制在你⾯前裸奔,变得⼀丝不挂。
可能说到这⾥,很多⼈已经知道我要说啥了,因为可以让他变得⼀丝不挂的东西,没⼏样,在这个层⾯,⼀般说的就是“反射”,是的,request既然不让我改,那么我⼜想修改,那么我就⽤反射。
那么⽤反射的条件是什么?熟悉源码,是的,你必须看懂request怎么获取参数的,看源码容易⾛⼊误区,虽然是错误的,但是我还是先说下我⾛⼊的那些个误区,然后再来说怎么实际的改东西。
我⾛⼊的误区,但是也跟踪了源码,因祸得福:
⾸先通过以下⽅式到request的实例来⾃于哪⾥,是那个类(因为HttpServletRequest是⼀个接⼝),那个jar包:
不过可以通过程序看下是啥:
可以得到request所在的jar包的源⽂件⽂件路径。
或者这样也可以:
⼀样可以获取到,主要要加第⼀个反斜杠哦,否则会认为是当前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.tor.Request
跟踪进去看看他的getParameterMap⽅法:
可以看到如果map被lock,直接返回,若没有,则将⾥⾯做了⼀个填充的操作,然后再设置为Lock,很简单吧。这个Lock貌似就和上⾯的异常有点关系了。
我们到这个parameterMap看看是什么类型,⾥⾯发⽣了什么:
protected ParameterMap parameterMap = new ParameterMap();
那么ParameterMap 是什么定义的呢:
public final class ParameterMap extends HashMap {
.....
}
有点意思了,貌似到组织了,竟然是HashMap的⼉⼦,还有搞不定的嘛,眼看就要⼀切拨开云雾见青天了。
在看看⾥⾯的lock到底做了啥,个put⽅法:
乖乖,终于到凶⼿了,再看看其他的clear⽅法都做了类似操作,要修改这个怎么办?简单想办法把这个Map拿到,然后setLock(false)然后就可以操作了,然后操作完再setLock(true)呵呵,怎么获取到这个Map呢?
getParameterMap其实就是返回了他,将他强制类型转换为ParameterMap,貌似不靠谱,因为这个Class不在你的应⽤内存⾥⾯,引⽤不到,不过可以做的是什么反射?
呵呵!简单来说,获取到这个Map后,假如被命名为map
Filed lockedField = Class().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,这个时候发现,ParameterMap对了,可是其他的貌似不对,getParameter、getParameterValues、getParameterNames这⼏个都不对;
最后我发现我⾛错了,下⾯开始纠正错误了:
跟踪另外⼏个⽅法进去:
发现也是在这个request⾥⾯,进去看看:
发现⼜出来⼀个coyoteRequest,⼜是哪⾥冒出来的:看定义:
protected Request coyoteRequest;
这个类竟然也叫Request,⽽且是包装在现在Request类⾥⾯的,这就是为什么开始我要说全名(org.tor.Request)了继续跟踪到后⾯这个Reuqest(Request)⾥⾯去过后,看这个⾥⾯的getParameters⽅法,因为可以看出Parameters获取到,后⾯就是键值对了。
进去看下:
原来是⼀个属性,看下属性定义:
private Parameters parameters = new Parameters();
这个Parameters到底是啥东西,我能修改么?
public final class Parameters extends MultiMap {
....
}
没开始那么兴奋,貌似没见过MultiMap 是什么。
跟踪进去,尽然没发现⽗类,正在我纳闷的时候,翻看这个Parameters的源码的时候,发现没⽤集成,⽤了下组合,呵呵:
private Hashtable<String,String[]> paramHashStringArray =
spring到底是干啥的new Hashtable<String,String[]>();
和我想想的差不多,再看看⽅法,有个addParameterValues,估计它就是⽤这个⽅法来设置参数的:
再确认下,发现getParameter、getParameterValues、getParameterNames都间接会直接调⽤这个hashtable;
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论