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方法。添加相关的功能。日志,权限登录等。
@Override protected HttpResponse execute(HttpServletRequest servletRequest, HttpServletResponse servletResponse, HttpRequest proxyRequest) throws IOException { //设置header里的授权信息 proxyRequest.setHeader("Authorization", "Basic " + getWebappBLoginAuth()); HttpResponse response = super.doExecute(servletRequest, servletResponse, proxyRequest); // 设置跨域,暂时不用。 // String origin = servletRequest.getHeader("origin"); // response.setHeader("Access-Control-Allow-Origin", origin); // response.setHeader("Access-Control-Allow-Credentials", "true"); // response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); // response.setHeader("Access-Control-Allow-Headers", // "Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin" + // ",User-Agent,X-Mx-ReqToken,X-Requested-With"); return response; }
2.5 后记: 这个ProxyServlet 跟 nginx看着基本功能一样,都可以简单的反向代理。
不过扩展功能和IO性能肯定跟nginx没法比。但是这个是用java语言写的。
方便二次开发。不错!再次感谢[smiley-http-proxy-servlet]作者!
世界丰富多彩,知识天花乱坠。
---如果有帮到你,点个赞吧~
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步