如何从 http 或 https 请求中获取带有端口的主机名
Posted
技术标签:
【中文标题】如何从 http 或 https 请求中获取带有端口的主机名【英文标题】:How to get host name with port from a http or https request 【发布时间】:2013-11-05 02:15:27 【问题描述】:我有两个应用程序部署在一个 JBoss 容器(同一个 unix 盒子)中。如果我收到来自 app1 的请求,我需要为 app2 发送相应的请求。
示例:如果 app1 请求:http://example.com/context?param1=123
,
然后我需要提取http://example.com/
,以便我可以发送第二个应用的请求。
我尝试过使用
HttpServletRequest.getServerName() &
HttpServletRequest.getServerPort() & \
HttpServletRequest.getHeader("host")
但我如何区分http
或https
?
【问题讨论】:
【参考方案1】:您可以使用HttpServletRequest.getScheme()
检索“http”或“https”。
将它与 HttpServletRequest.getServerName()
一起使用应该足以重建您需要的 URL 部分。
如果您使用标准端口(http 为 80,https 为 443),则无需在 URL 中明确放置端口。
编辑:如果您的 servlet 容器位于终止 SSL 的反向代理或负载平衡器之后,则有点棘手,因为请求将作为纯 http 转发到 servlet 容器。您有几个选择:
请改用HttpServletRequest.getHeader("x-forwarded-proto")
;这仅在您的负载均衡器正确设置标头时才有效(Apache 应该 afaik)。
在 JBoss/Tomcat 中配置一个 RemoteIpValve,这将使 getScheme()
按预期工作。同样,这仅在负载平衡器设置正确的标头时才有效。
如果上述方法不起作用,您可以在 Tomcat/JBoss 中配置两个不同的连接器,一个用于 http,一个用于 https,如 this article 中所述。
【讨论】:
你说得对,大卫。这对于我的生产环境没问题,但对于开发环境,url 将类似于10.1.1.1:8080/bla.. 在这种情况下,我需要指定端口。如果我设置条件并检索端口信息,它可能看起来不太好。 我实现了你的方法,我注意到它部分工作。我的意思是,当请求的 url 是 https 类型时,getScheme() 方法不会返回“https”。你有什么线索吗? 您的 SSL 是由 JBoss 直接处理还是由位于 JBoss 前面的 Web 服务器(例如 Apache)处理? 是的。前面有一个负载均衡器。在互联网上发现与此相关的问题:issues.apache.org/jira/browse/… 您提供的链接描述了 HttpClient 库的问题,我不确定它是否直接相关。无论如何,请参阅我的编辑以获取建议。【参考方案2】:似乎你需要从 URL 中去除 URL,所以你可以通过以下方式做到这一点:
request.getRequestURL().toString().replace(request.getRequestURI(), "")
【讨论】:
【参考方案3】:您可以使用HttpServletRequest.getRequestURL 和HttpServletRequest.getRequestURI。
StringBuffer url = request.getRequestURL();
String uri = request.getRequestURI();
int idx = (((uri != null) && (uri.length() > 0)) ? url.indexOf(uri) : url.length());
String host = url.substring(0, idx); //base url
idx = host.indexOf("://");
if(idx > 0)
host = host.substring(idx); //remove scheme if present
【讨论】:
这将给出整个 url,这不是我所期望的,我不想操纵 url 字符串并获得所需的部分。 @DenisMakarskiy 这是一个简单的修复。更新。 (并删除了该方案) 感谢@jtahlborn 的及时修复。实际上,我采用了您的变体应用了类似的修复。【参考方案4】:我迟到了,但我在使用 Java 8 时遇到了同样的问题。
这对我有用,在 HttpServletRequest request
对象上。
request.getHeader("origin");
和
request.getHeader("referer");
我是如何得出这个结论的:
我有一个在 http://localhost:3000 上运行的 java 应用程序向我在 http://localhost:8080 上运行的另一个 java 应用程序发送 Http Post。
从 http://localhost:8080 上运行的 Java 代码中,我无法使用上述答案从 HttpServletRequest 中获取 http://localhost:3000。对我来说,使用 getHeader
方法和正确的字符串输入是有效的。
request.getHeader("origin")
给了我“http://localhost:3000”,这正是我想要的。
request.getHeader("referer")
给了我 "http://localhost:3000/xxxx" 其中 xxxx 是我从请求应用程序获得的完整 URL。
【讨论】:
【参考方案5】:如果您的服务器在代理服务器后面运行,请确保您的代理标头已设置:
proxy_set_header X-Forwarded-Proto $scheme;
然后要获得正确的scheme & url
,您可以使用springframework的类:
public String getUrl(HttpServletRequest request)
HttpRequest httpRequest = new ServletServerHttpRequest(request);
UriComponents uriComponents = UriComponentsBuilder.fromHttpRequest(httpRequest).build();
String scheme = uriComponents.getScheme(); // http / https
String serverName = request.getServerName(); // hostname.com
int serverPort = request.getServerPort(); // 80
String contextPath = request.getContextPath(); // /app
// Reconstruct original requesting URL
StringBuilder url = new StringBuilder();
url.append(scheme).append("://");
url.append(serverName);
if (serverPort != 80 && serverPort != 443)
url.append(":").append(serverPort);
url.append(contextPath);
return url.toString();
【讨论】:
【参考方案6】:如果您使用负载均衡器和 nginx,请在不修改代码的情况下配置它们。
Nginx:
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
Tomcat 的 server.xml 引擎:
<Valve className="org.apache.catalina.valves.RemoteIpValve"
remoteIpHeader="X-Forwarded-For"
protocolHeader="X-Forwarded-Proto"
protocolHeaderHttpsValue="https"/>
如果只修改 Nginx 配置文件,java 代码应该是:
String XForwardedProto = request.getHeader("X-Forwarded-Proto");
【讨论】:
我没有使用 nginx。我使用 AWS ELB,我添加了如果您想要原始 URL,请使用 jthalborn 描述的方法。 如果你想像 David Levesque 解释的那样重建 url,这里有一个代码 sn-p:
final javax.servlet.http.HttpServletRequest req = (javax.servlet.http.HttpServletRequest) ...;
final int serverPort = req.getServerPort();
if ((serverPort == 80) || (serverPort == 443))
// No need to add the server port for standard HTTP and HTTPS ports, the scheme will help determine it.
url = String.format("%s://%s/...", req.getScheme(), req.getServerName(), ...);
else
url = String.format("%s://%s:%s...", req.getScheme(), req.getServerName(), serverPort, ...);
您仍然需要考虑反向代理的情况:
request.getScheme() is returning http instead of returning https in java可以为端口使用常量,但不确定是否有可靠的来源,默认端口:
Are HTTP and HTTPS default port numbers defined in the JDK?无论如何,大多数开发人员都知道端口 80 和 443,因此常量没有那么有用。
另见类似post。
【讨论】:
以上是关于如何从 http 或 https 请求中获取带有端口的主机名的主要内容,如果未能解决你的问题,请参考以下文章
如何从 STUN 请求中获取我的外部 IP 地址和外部端口号?
ngxin常用配置--nginx之proxy_pass代理后端https请求完全解析