All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cc.shacocloud.mirage.web.util.CorsUtils Maven / Gradle / Ivy


package cc.shacocloud.mirage.web.util;


import cc.shacocloud.mirage.web.HttpRequest;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpMethod;
import org.jetbrains.annotations.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * CORS请求处理的工具类  W3C CORS 标准.
 */
public abstract class CorsUtils {

    private static final String SCHEME_PATTERN = "([^:/?#]+):";

    private static final String USERINFO_PATTERN = "([^@\\[/?#]*)";

    private static final String HOST_IPV4_PATTERN = "[^\\[/?#:]*";

    private static final String HOST_IPV6_PATTERN = "\\[[\\p{XDigit}:.]*[%\\p{Alnum}]*]";

    private static final String HOST_PATTERN = "(" + HOST_IPV6_PATTERN + "|" + HOST_IPV4_PATTERN + ")";

    private static final String PORT_PATTERN = "(\\d*(?:\\{[^/]+?})?)";

    private static final String PATH_PATTERN = "([^?#]*)";

    private static final String QUERY_PATTERN = "([^#]*)";

    private static final String LAST_PATTERN = "(.*)";

    // 匹配uri的正则表达式模式 见 RFC 3986, 附录 B
    private static final Pattern URI_PATTERN = Pattern.compile(
            "^(" + SCHEME_PATTERN + ")?" + "(//(" + USERINFO_PATTERN + "@)?" + HOST_PATTERN + "(:" + PORT_PATTERN +
                    ")?" + ")?" + PATH_PATTERN + "(\\?" + QUERY_PATTERN + ")?" + "(#" + LAST_PATTERN + ")?");


    /**
     * 如果请求是有效的CORS,通过检查源头是否存在并确保源是不同的,返回{@code true}
     */
    public static boolean isCorsRequest(HttpRequest request) {
        String origin = request.headers().get(HttpHeaders.ORIGIN);
        if (origin == null) return false;

        Matcher matcher = URI_PATTERN.matcher(origin);
        Assert.isTrue(matcher.matches(), "[" + origin + "] 不是有效的 \"Origin\" 请求头");

        String originScheme = matcher.group(2);
        String originHost = matcher.group(6);
        String originPortStr = matcher.group(8);

        // 端口解析判断
        int originPort;
        if (originPortStr == null) {
            originPort = -1;
        } else if (originPortStr.contains("{")) {
            throw new IllegalStateException("该端口包含一个URI变量,但尚未展开: " + originPortStr);
        } else {
            originPort = Integer.parseInt(originPortStr);
        }

        String scheme = request.getScheme();
        String host = request.getServerName();
        int port = request.getServerPort();
        return !(ObjectUtils.nullSafeEquals(scheme, originScheme) &&
                ObjectUtils.nullSafeEquals(host, originHost) &&
                getPort(scheme, port) == getPort(originScheme, originPort));
    }

    private static int getPort(@Nullable String scheme, int port) {
        if (port == -1) {
            if ("http".equals(scheme) || "ws".equals(scheme)) {
                port = 80;
            } else if ("https".equals(scheme) || "wss".equals(scheme)) {
                port = 443;
            }
        }
        return port;
    }

    /**
     * 如果请求是一个有效的CORS 执行前检查{code OPTIONS}方法的起源和访问-控制-请求-方法头存在。
     */
    public static boolean isPreFlightRequest(HttpRequest request) {
        return HttpMethod.OPTIONS == request.method() &&
                request.headers().get(HttpHeaders.ORIGIN) != null &&
                request.headers().get(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD) != null;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy