栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

服务端实现对客户端请求转发到另一个应用程序并添加Request的请求头

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

服务端实现对客户端请求转发到另一个应用程序并添加Request的请求头

在使用ProxyServlet对客户端发送的请求进行转发到另一个应用程序时,会发现HttpServletRequest并没有添加Header的方法,但是在某些场景我们需要这个方法,那么我们可以重写HttpServletRequestWrapper类中的String getHeader(String name)、Enumeration getHeaderNames()、Enumeration getHeaders(String name)三个方法,并且自定义一个void addHeader(String name, String value)来实现对HttpServletRequest的Header进行添加。

先上代码:


    org.mitre.dsmiley.httpproxy
    smiley-http-proxy-servlet
    1.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会通过这三个函数去去获取原始请求头的信息,而我们通过重写这三个函数,从而达到修改请求头的目的。

简单地画了一下函数调用的链路,希望对大家有帮助。

 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/424501.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号