JAVA實現的HTTP反向代理 [smiley-http-proxy-servlet]學習


 反向代理(Reverse Proxy)方式是指以代理服務器來接受internet上的連接請求,然后將請求轉發給內部網絡上的服務器,並將從服務器上得到的結果返回給internet上請求連接的客戶端,此時代理服務器對外就表現為一個反向代理服務器。

 簡單來說,你的反向代理服務器會接收請求,但其自身不處理該請求,而是對請求經過一些處理,例如添加日志、緩存、身份驗證等服務,然后再將請求轉發到相應的應用服務器中進行處理,最后將處理結果返回。

我目前的需求是,A應用需要訪問B應用的報表頁面,B應用沒有源碼,要求自動處理B應用的登錄權限。避免用戶重復登錄。

1. 引入相關依賴 

<dependency>
<groupId>org.mitre.dsmiley.httpproxy</groupId>
<artifactId>smiley-http-proxy-servlet</artifactId>
<version>1.11</version>
</dependency>
這個jar包,只有兩個類,其中核心的就是 ProxyServlet,
作者重寫了HttpServlet的相關方法。他復制了新的request為proxyRequest,
然后替換了地址和相關屬性,並使用HttpClient將proxyRequest發送出去,
然后將接收到的proxyResponse的內容再復制給 HttpResponse 。
相當於中轉站。具體請看源碼。
https://github.com/mitre/HTTP-Proxy-Servlet
2.spring使用:
2.1 application.yml增加代理參數配置
# 設置代理
proxy:
  servlet_url: /webappB/*
  target_url: https://webappB_HOST_IP:8001/webappB #已有的app路徑

其他demo
 # servlet_url: /proxybaidu/*
  # target_url: https://www.baidu.com

此處有玄機:
為什么代理url的app子路徑和目標url的app子路徑要一致(都為/webappB/)呢?
這是因為: target_url頁面里不止文本顯示,還有其他資源的調用,比如圖片,比如里面的js又調用了其他url.
這樣的話,假如 target頁面里某個圖片的url是相對路徑
img/test.jpg (https://webappB_HOST_IP:8001/webappB/img/test.jpg); 你的代理頁面app子路徑用webappC,那么圖片地址就成 /webappC/image/test.jpg; 這樣app子url換了以后是找不到圖片地址的。https://webappB_HOST_IP:8001/webappC/img/test.jpg 不存在。 當然你也可以改下proxyServlet的源碼,讓它換成正確的url地址。如果不想改源碼的話,那還是一致的比較好。少麻煩。

2.2 注冊servlet.

 @Configuration
 public class ProxyServletConfiguration {
     /**
      * 讀取配置文件中路由設置
      */
    @Value("${proxy.servlet_url}")
     private String servlet_url;
    /**
      * 讀取配置中代理目標地址
      */
     @Value("${proxy.target_url}")
    private String target_url;
 
    @Bean
     public Servlet createProxyServlet() {
         /** 創建新的ProxyServlet */
         return new ProxyServlet();
     }
 
    @Bean
     public ServletRegistrationBean proxyServletRegistration() {
         ServletRegistrationBean registrationBean = new ServletRegistrationBean(createProxyServlet(), servlet_url);
         //設置網址以及參數
         Map<String, String> params = ImmutableMap.of("targetUri", target_url, "log", "true");
        registrationBean.setInitParameters(params);
         return registrationBean;
    }

/**
* fix springboot中使用proxyservlet的 bug.
* https://github.com/mitre/HTTP-Proxy-Servlet/issues/83
* https://stackoverflow.com/questions/8522568/why-is-httpservletrequest-inputstream-empty
* @param filter
* @return 關閉springboot 自帶的 HiddenHttpMethodFilter 防止post提交的form數據流被提前消費

*/
  @Bean
  public FilterRegistrationBean registration(HiddenHttpMethodFilter filter) {
   FilterRegistrationBean registration = new FilterRegistrationBean(filter);
   registration.setEnabled(false);
   return registration;
  }
}
2.3測試。ok.
localhost:8080/proxybaidu/

2.4. 當然你也可以重新寫個類,
MyProxyServlet繼承ProxyServlet.
 重新其中的 execute方法。添加相關的功能。日志,權限登錄等。
 1     @Override
 2     protected HttpResponse execute(HttpServletRequest servletRequest, HttpServletResponse servletResponse,  3 HttpRequest proxyRequest) throws IOException {
          //設置header里的授權信息  4         proxyRequest.setHeader("Authorization", "Basic " + getWebappBLoginAuth());  5         HttpResponse response = super.doExecute(servletRequest, servletResponse, proxyRequest);  6  
 7 // 設置跨域,暫時不用。  8 // String origin = servletRequest.getHeader("origin");  9 // response.setHeader("Access-Control-Allow-Origin", origin); 10 // response.setHeader("Access-Control-Allow-Credentials", "true"); 11 // response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); 12 // response.setHeader("Access-Control-Allow-Headers", 13 // "Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin" + 14 // ",User-Agent,X-Mx-ReqToken,X-Requested-With");
15         return response; 16     }

 

2.5 后記: 這個ProxyServlet 跟 nginx看着基本功能一樣,都可以簡單的反向代理。
不過擴展功能和IO性能肯定跟nginx沒法比。但是這個是用java語言寫的。
方便二次開發。不錯!再次感謝[smiley-http-proxy-servlet]作者!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM