From 1108ada1db545f53458c94b5ea2d3f833e47e60b Mon Sep 17 00:00:00 2001 From: lapuda <577732344@qq.com> Date: Tue, 14 Jul 2026 22:09:58 +0800 Subject: [PATCH] feat: Enhance RequestUrlUtils to handle browser Referer for public URL resolution and add corresponding unit tests --- .../cms/OutdoorFairControllerTest.java | 15 ++++ .../ruoyi/common/utils/RequestUrlUtils.java | 75 ++++++++++++++++++- 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/ruoyi-bussiness/src/test/java/com/ruoyi/cms/controller/cms/OutdoorFairControllerTest.java b/ruoyi-bussiness/src/test/java/com/ruoyi/cms/controller/cms/OutdoorFairControllerTest.java index dae688b..93c927d 100644 --- a/ruoyi-bussiness/src/test/java/com/ruoyi/cms/controller/cms/OutdoorFairControllerTest.java +++ b/ruoyi-bussiness/src/test/java/com/ruoyi/cms/controller/cms/OutdoorFairControllerTest.java @@ -53,6 +53,21 @@ class OutdoorFairControllerTest buildCompanyH5Url(request, 12L, 34L)); } + @Test + void companyQrCodeUrlUsesBrowserRefererWhenProxyRewritesHost() throws Exception + { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("127.0.0.1"); + request.setServerPort(9091); + request.addHeader("Host", "127.0.0.1:9091"); + request.addHeader("Referer", "http://39.98.44.136:6024/shihezi/jobfair/outdoor-fair/detail?id=8"); + + assertEquals( + "http://39.98.44.136:6024/h5/outdoor-fair/company.html?fairId=12&companyId=34", + buildCompanyH5Url(request, 12L, 34L)); + } + private String buildCompanyH5Url(MockHttpServletRequest request, Long fairId, Long companyId) throws Exception { diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/RequestUrlUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/RequestUrlUtils.java index 8b629a2..6d957dd 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/RequestUrlUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/RequestUrlUtils.java @@ -1,6 +1,8 @@ package com.ruoyi.common.utils; import javax.servlet.http.HttpServletRequest; +import java.net.URI; +import java.net.URISyntaxException; /** * 请求公开地址工具。 @@ -24,10 +26,17 @@ public final class RequestUrlUtils String forwarded = firstForwardedValue(forwardedHeader, "host"); String forwardedHost = firstHeaderValue(request.getHeader("X-Forwarded-Host")); String hostHeader = request.getHeader("Host"); - boolean hasPublicHost = !isBlank(forwarded) || !isBlank(forwardedHost) || !isBlank(hostHeader); + String requestHost = firstNonBlank(hostHeader, request.getServerName()); + URI browserOrigin = isInternalHost(requestHost) + ? firstBrowserOrigin(request) + : null; + String browserHost = browserOrigin == null ? null : getAuthority(browserOrigin); + boolean hasPublicHost = !isBlank(forwarded) || !isBlank(forwardedHost) + || !isBlank(hostHeader) || browserOrigin != null; String host = firstNonBlank( forwarded, forwardedHost, + browserHost, hostHeader, request.getServerName()); host = normalizeHost(host); @@ -35,12 +44,17 @@ public final class RequestUrlUtils String scheme = firstNonBlank( firstForwardedValue(request.getHeader("Forwarded"), "proto"), firstHeaderValue(request.getHeader("X-Forwarded-Proto")), + browserOrigin == null ? null : browserOrigin.getScheme(), request.getScheme()); scheme = normalizeScheme(scheme, request.getScheme()); String forwardedPort = firstNonBlank( extractPort(forwarded), - firstHeaderValue(request.getHeader("X-Forwarded-Port"))); + firstHeaderValue(request.getHeader("X-Forwarded-Port")), + browserOrigin == null ? null : String.valueOf(browserOrigin.getPort())); + if ("-1".equals(forwardedPort)) { + forwardedPort = null; + } if (!hasPort(host) && shouldIncludePort(forwardedPort, scheme)) { host += ":" + forwardedPort; } @@ -53,6 +67,63 @@ public final class RequestUrlUtils return scheme + "://" + host + (contextPath == null ? "" : contextPath); } + /** + * 反向代理未透传 Host 时,Servlet 可能只能看到 127.0.0.1:9091。 + * 对浏览器请求,Origin/Referer 仍然包含用户实际访问的公开地址,作为兜底来源。 + */ + private static URI firstBrowserOrigin(HttpServletRequest request) + { + URI origin = parseHttpUri(firstHeaderValue(request.getHeader("Origin"))); + return origin != null + ? origin + : parseHttpUri(firstHeaderValue(request.getHeader("Referer"))); + } + + private static URI parseHttpUri(String value) + { + if (isBlank(value)) { + return null; + } + try { + URI uri = new URI(value.trim()); + String scheme = uri.getScheme(); + return uri.getHost() != null && ("http".equalsIgnoreCase(scheme) + || "https".equalsIgnoreCase(scheme)) + ? uri : null; + } catch (URISyntaxException e) { + return null; + } + } + + private static String getAuthority(URI uri) + { + String host = uri.getHost(); + if (host == null) { + return null; + } + if (host.indexOf(':') >= 0 && !host.startsWith("[")) { + host = "[" + host + "]"; + } + return uri.getPort() > 0 ? host + ":" + uri.getPort() : host; + } + + private static boolean isInternalHost(String host) + { + if (isBlank(host)) { + return true; + } + String normalized = host.trim().toLowerCase(); + if (normalized.startsWith("[") && normalized.contains("]")) { + normalized = normalized.substring(1, normalized.indexOf(']')); + } else if (normalized.indexOf(':') == normalized.lastIndexOf(':')) { + normalized = normalized.substring(0, normalized.indexOf(':')); + } + return "localhost".equals(normalized) + || "127.0.0.1".equals(normalized) + || "0.0.0.0".equals(normalized) + || "::1".equals(normalized); + } + private static String firstForwardedValue(String header, String name) { if (isBlank(header)) {