【问题标题】:How to get host name with port from a http or https request如何从 http 或 https 请求中获取带有端口的主机名
【发布时间】: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

【问题讨论】:

    标签: java http servlets http-headers httprequest


    【解决方案1】:

    您可以使用HttpServletRequest.getScheme() 检索“http”或“https”。

    将它与 HttpServletRequest.getServerName() 一起使用应该足以重建您需要的 URL 部分。

    如果您使用标准端口(http 为 80,https 为 443),则无需在 URL 中明确放置端口。

    编辑:如果您的 servlet 容器位于终止 SSL 的反向代理或负载平衡器之后,则有点棘手,因为请求将作为纯 http 转发到 servlet 容器。您有几个选择:

    1. 请改用HttpServletRequest.getHeader("x-forwarded-proto");这仅在您的负载均衡器正确设置标头时才有效(Apache 应该 afaik)。

    2. 在 JBoss/Tomcat 中配置一个 RemoteIpValve,这将使 getScheme() 按预期工作。同样,这仅在负载平衡器设置正确的标头时才有效。

    3. 如果上述方法不起作用,您可以在 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.getRequestURLHttpServletRequest.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, ...);
          }
          

          您仍然需要考虑反向代理的情况:

          可以为端口使用常量,但不确定是否有可靠的来源,默认端口:

          无论如何,大多数开发人员都知道端口 80 和 443,因此常量没有那么有用。

          另见类似post

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-11-20
            • 2011-02-05
            • 1970-01-01
            • 1970-01-01
            • 2021-02-20
            • 2016-02-09
            • 1970-01-01
            • 2014-05-18
            相关资源
            最近更新 更多