package com.ruoyi.common.utils; import javax.servlet.http.HttpServletRequest; /** * 请求公开地址工具。 * *

应用位于反向代理后时,Servlet 请求看到的 serverName/serverPort 可能是代理的内网地址, * 因此优先使用代理传递的公开地址,再回退到 Host 和 Servlet 请求信息。

*/ public final class RequestUrlUtils { /** 反向代理未透传公开地址时使用的默认访问地址(不带末尾斜杠,便于拼接路径)。 */ private static final String DEFAULT_PUBLIC_BASE_URL = "http://39.98.44.136:6024/shihezi"; private RequestUrlUtils() { } public static String buildBaseUrl(HttpServletRequest request) { if (request == null) { return ""; } String forwardedHeader = request.getHeader("Forwarded"); String forwarded = firstForwardedValue(forwardedHeader, "host"); String forwardedHost = firstHeaderValue(request.getHeader("X-Forwarded-Host")); String hostHeader = request.getHeader("Host"); String requestHost = firstNonBlank(hostHeader, request.getServerName()); String forwardedPublicHost = firstNonBlank(forwarded, forwardedHost); if (isInternalHost(requestHost) && isInternalHost(forwardedPublicHost)) { String contextPath = request.getContextPath(); return DEFAULT_PUBLIC_BASE_URL + (contextPath == null ? "" : contextPath); } boolean hasPublicHost = !isBlank(forwarded) || !isBlank(forwardedHost) || !isBlank(hostHeader); String host = firstNonBlank( forwarded, forwardedHost, hostHeader, request.getServerName()); host = normalizeHost(host); String scheme = firstNonBlank( firstForwardedValue(request.getHeader("Forwarded"), "proto"), firstHeaderValue(request.getHeader("X-Forwarded-Proto")), request.getScheme()); scheme = normalizeScheme(scheme, request.getScheme()); String forwardedPort = firstNonBlank( extractPort(forwarded), firstHeaderValue(request.getHeader("X-Forwarded-Port"))); if (!hasPort(host) && shouldIncludePort(forwardedPort, scheme)) { host += ":" + forwardedPort; } if (!hasPort(host) && isBlank(forwardedPort) && !hasPublicHost && shouldIncludePort(String.valueOf(request.getServerPort()), scheme)) { host += ":" + request.getServerPort(); } String contextPath = request.getContextPath(); return scheme + "://" + host + (contextPath == null ? "" : contextPath); } 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)) { return null; } String firstElement = header.split(",", 2)[0]; String[] parameters = firstElement.split(";"); for (String parameter : parameters) { String[] pair = parameter.split("=", 2); if (pair.length == 2 && name.equalsIgnoreCase(pair[0].trim())) { return stripQuotes(pair[1].trim()); } } return null; } private static String firstHeaderValue(String header) { if (isBlank(header)) { return null; } return stripQuotes(header.split(",", 2)[0].trim()); } private static String normalizeHost(String host) { if (isBlank(host)) { return "localhost"; } String normalized = stripQuotes(host.trim()); if (normalized.indexOf('\r') >= 0 || normalized.indexOf('\n') >= 0 || normalized.indexOf('/') >= 0 || normalized.indexOf('?') >= 0 || normalized.indexOf('#') >= 0) { return "localhost"; } return normalized; } private static String normalizeScheme(String scheme, String fallback) { if ("https".equalsIgnoreCase(scheme)) { return "https"; } if ("http".equalsIgnoreCase(scheme)) { return "http"; } return "https".equalsIgnoreCase(fallback) ? "https" : "http"; } private static String extractPort(String host) { if (isBlank(host)) { return null; } String normalized = host.trim(); if (normalized.startsWith("[") && normalized.contains("]:")) { return normalized.substring(normalized.lastIndexOf(':') + 1); } int colon = normalized.lastIndexOf(':'); return colon > -1 && normalized.indexOf(':') == colon ? normalized.substring(colon + 1) : null; } private static boolean hasPort(String host) { return !isBlank(extractPort(host)); } private static boolean shouldIncludePort(String port, String scheme) { if (isBlank(port)) { return false; } try { int portNumber = Integer.parseInt(port); return portNumber > 0 && portNumber <= 65535 && !(("http".equalsIgnoreCase(scheme) && portNumber == 80) || ("https".equalsIgnoreCase(scheme) && portNumber == 443)); } catch (NumberFormatException e) { return false; } } private static String firstNonBlank(String... values) { for (String value : values) { if (!isBlank(value)) { return value.trim(); } } return null; } private static String stripQuotes(String value) { if (value != null && value.length() >= 2 && value.startsWith("\"") && value.endsWith("\"")) { return value.substring(1, value.length() - 1); } return value; } private static boolean isBlank(String value) { return value == null || value.trim().isEmpty(); } }