在使用ProxyServlet对客户端发送的请求进行转发到另一个应用程序时,会发现HttpServletRequest并没有添加Header的方法,但是在某些场景我们需要这个方法,那么我们可以重写HttpServletRequestWrapper类中的String getHeader(String name)、Enumeration
先上代码:
org.mitre.dsmiley.httpproxy smiley-http-proxy-servlet1.11
//MyHttpServletRequestWrapper.java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.*;
public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {
//存储要添加的请求头
private Map extraHeaders;
public MyHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
this.extraHeaders = new HashMap<>();
}
public void addHeader(String name, String value){
extraHeaders.put(name, value);
}
@Override
public String getHeader(String name) {
String value = super.getHeader(name);
if (extraHeaders.containsKey(name)){
value = extraHeaders.get(name);
}
return value;
}
@Override
public Enumeration getHeaderNames() {
Enumeration enumeration = super.getHeaderNames();
Set extraSet = extraHeaders.keySet();
while (enumeration.hasMoreElements()){
String name = enumeration.nextElement();
extraSet.add(name);
}
return Collections.enumeration(extraSet);
}
@Override
public Enumeration getHeaders(String name) {
List header = Collections.list(super.getHeaders(name));
if (extraHeaders.containsKey(name)){
header = Arrays.asList(extraHeaders.get(name));
}
return Collections.enumeration(header);
}
}
//MyServlet.java
import org.apache.http.client.utils.URIUtils;
import org.mitre.dsmiley.httpproxy.ProxyServlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URI;
@WebServlet(name = "MyServlet", urlPatterns = {"
@Override
protected void initTarget() throws ServletException {
this.targetUri = "http://127.0.0.1:8080/target/api";
try {
this.targetUriObj = new URI(this.targetUri);
}catch (Exception e){
throw new ServletException("Trying to process targetUri init parameter:"+e,e);
}
this.targetHost = URIUtils.extractHost(this.targetUriObj);
}
}
接下来我们来分析一下源码看一看为什么我们重写这三个方法可以添加请求头。
-
首先进入回调的函数,看看servlet如何处理我们的请求,super.service(requestWrapper, servletResponse);,找到跟请求头相关的函数,可以看到有三个跟请求头相关的函数,在代码中标注。
//ProxyServlet.class ··· protected void service(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws ServletException, IOException { ··· //第一个 if (servletRequest.getHeader("Content-Length") == null && servletRequest.getHeader("Transfer-Encoding") == null) { proxyRequest = new BasicHttpRequest(method, proxyRequestUri); } else { proxyRequest = this.newProxyRequestWithEntity(method, proxyRequestUri, servletRequest); } //第二个 this.copyRequestHeaders(servletRequest, (HttpRequest)proxyRequest); //第三个 this.setXForwardedForHeader(servletRequest, (HttpRequest)proxyRequest); ··· } ··· protected void copyRequestHeaders(HttpServletRequest servletRequest, HttpRequest proxyRequest) { Enumeration enumerationOfHeaderNames = servletRequest.getHeaderNames(); while(enumerationOfHeaderNames.hasMoreElements()) { String headerName = (String)enumerationOfHeaderNames.nextElement(); //这里也是一个请求头相关的函数,进入查看 this.copyRequestHeader(servletRequest, proxyRequest, headerName); } } -
可以看到这里调用了.getHeaderNames()函数,下面也有一个函数,进入查看
protected void copyRequestHeader(HttpServletRequest servletRequest, HttpRequest proxyRequest, String headerName) { if (!headerName.equalsIgnoreCase("Content-Length")) { if (!hopByHopHeaders.containsHeader(headerName)) { String headerValue; //这里也有一个函数调用 for(Enumeration headers = servletRequest.getHeaders(headerName); headers.hasMoreElements(); proxyRequest.addHeader(headerName, headerValue)) { headerValue = (String)headers.nextElement(); if (!this.doPreserveHost && headerName.equalsIgnoreCase("Host")) { HttpHost host = this.getTargetHost(servletRequest); headerValue = host.getHostName(); if (host.getPort() != -1) { headerValue = headerValue + ":" + host.getPort(); } } else if (!this.doPreservecookies && headerName.equalsIgnoreCase("cookie")) { headerValue = this.getRealcookie(headerValue); } } } } } -
可以看到这里调用了函数.getHeaders(String headerName)。接下来回到service(),进入this.setXForwardedForHeader(servletRequest, (HttpRequest)proxyRequest);查看
private void setXForwardedForHeader(HttpServletRequest servletRequest, HttpRequest proxyRequest) { if (this.doForwardIP) { String forHeaderName = "X-Forwarded-For"; String forHeader = servletRequest.getRemoteAddr(); String existingForHeader = servletRequest.getHeader(forHeaderName); if (existingForHeader != null) { forHeader = existingForHeader + ", " + forHeader; } proxyRequest.setHeader(forHeaderName, forHeader); String protoHeaderName = "X-Forwarded-Proto"; String protoHeader = servletRequest.getScheme(); proxyRequest.setHeader(protoHeaderName, protoHeader); } } -
可以看到调用了.getHeader(String headerName)进行复制。
-
到这里整个原始请求的请求头复制过程我们便知道了,在复制过程中,servlet会通过这三个函数去去获取原始请求头的信息,而我们通过重写这三个函数,从而达到修改请求头的目的。
简单地画了一下函数调用的链路,希望对大家有帮助。



