Files
shz-backend/ruoyi-common/src/main/java/com/ruoyi/common/utils/RequestUrlUtils.java

238 lines
8.2 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.ruoyi.common.utils;
import java.net.URI;
import java.net.URISyntaxException;
import javax.servlet.http.HttpServletRequest;
/**
* 请求公开地址工具。
*
* <p>应用位于反向代理后时Servlet 请求看到的 serverName/serverPort 可能是代理的内网地址,
* 因此优先使用代理传递的公开地址,再回退到 Host 和 Servlet 请求信息。</p>
*/
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);
}
/**
* 将历史数据中的回环地址改为当前请求的公开 origin同时保留资源路径。
* 这样浏览器不会把 127.0.0.1/localhost 解释为访问者自己的电脑。
*/
public static String normalizePublicUrl(HttpServletRequest request, String value)
{
if (isBlank(value) || request == null) {
return value;
}
try {
URI uri = new URI(value.trim());
String host = uri.getHost();
if (host == null || !isInternalHost(host)) {
return value;
}
URI base = new URI(buildBaseUrl(request));
String authority = base.getRawAuthority();
if (isBlank(authority)) {
return value;
}
return base.getScheme() + "://" + authority
+ (uri.getRawPath() == null ? "" : uri.getRawPath())
+ (uri.getRawQuery() == null ? "" : "?" + uri.getRawQuery())
+ (uri.getRawFragment() == null ? "" : "#" + uri.getRawFragment());
} catch (URISyntaxException e) {
return value;
}
}
/** 回环资源地址不能写入公开招聘会图片字段。相对路径不受此校验影响。 */
public static boolean isLoopbackUrl(String value)
{
if (isBlank(value)) {
return false;
}
try {
URI uri = new URI(value.trim());
return uri.getHost() != null && isInternalHost(uri.getHost());
} catch (URISyntaxException e) {
return false;
}
}
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 {
int portSeparator = normalized.lastIndexOf(':');
if (portSeparator > -1 && normalized.indexOf(':') == portSeparator) {
normalized = normalized.substring(0, portSeparator);
}
}
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();
}
}