如何从 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")

但我如何区分httphttps

【问题讨论】:

【参考方案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 类似于“example.com:443/path/to/somwhere”。我怎样才能摆脱 :443?【参考方案7】:

如果您想要原始 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 地址和外部端口号?

https请求过程

ngxin常用配置--nginx之proxy_pass代理后端https请求完全解析

python 如何获取url信息

Vuejs Axios POST 请求从 Laravel 后端获取 HTTP422 错误

如何从 POST 请求中获取带有存档器的压缩文件?