From 94baeb5544e0913e40d3e8a22692321288bcfeba Mon Sep 17 00:00:00 2001 From: yhd Date: Fri, 14 Aug 2026 22:13:01 +0800 Subject: [PATCH] fix(auth): force https scheme for requests matching the public base URL Higress terminates TLS and forwards plain HTTP without a usable X-Forwarded-Proto, so {baseUrl} expansion produced http:// OAuth2 redirect URIs and Secure session cookies were dropped. Rewrite the scheme to https when the request host matches skillhub.public.base-url. Signed-off-by: yhd --- .../filter/PublicBaseUrlSchemeFilter.java | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 server/skillhub-app/src/main/java/com/iflytek/skillhub/filter/PublicBaseUrlSchemeFilter.java diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/filter/PublicBaseUrlSchemeFilter.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/filter/PublicBaseUrlSchemeFilter.java new file mode 100644 index 00000000..530680df --- /dev/null +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/filter/PublicBaseUrlSchemeFilter.java @@ -0,0 +1,88 @@ +package com.iflytek.skillhub.filter; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.net.URI; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +/** + * Some TLS-terminating gateways (e.g. Higress) forward requests over plain HTTP without a + * usable X-Forwarded-Proto, so the container reports scheme http. When the public base URL is + * https, force the scheme back to https for requests matching the public host; otherwise + * {baseUrl} expansion (OAuth2 redirect URIs) and Secure session cookies break. + */ +@Component +@Order(Ordered.HIGHEST_PRECEDENCE + 10) +public class PublicBaseUrlSchemeFilter extends OncePerRequestFilter { + + private final String publicHost; + + public PublicBaseUrlSchemeFilter(@Value("${skillhub.public.base-url:}") String publicBaseUrl) { + this.publicHost = resolveHttpsHost(publicBaseUrl); + } + + private static String resolveHttpsHost(String publicBaseUrl) { + if (publicBaseUrl == null || publicBaseUrl.isBlank()) { + return null; + } + URI uri = URI.create(publicBaseUrl.trim()); + if (!"https".equalsIgnoreCase(uri.getScheme()) || uri.getHost() == null) { + return null; + } + return uri.getHost().toLowerCase(); + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + if (publicHost == null + || !"http".equals(request.getScheme()) + || !publicHost.equals(request.getServerName().toLowerCase())) { + filterChain.doFilter(request, response); + return; + } + filterChain.doFilter(new HttpsSchemeRequest(request), response); + } + + private static final class HttpsSchemeRequest extends HttpServletRequestWrapper { + + private HttpsSchemeRequest(HttpServletRequest request) { + super(request); + } + + @Override + public String getScheme() { + return "https"; + } + + @Override + public boolean isSecure() { + return true; + } + + @Override + public int getServerPort() { + int port = super.getServerPort(); + return port == 80 ? 443 : port; + } + + @Override + public StringBuffer getRequestURL() { + HttpServletRequest request = (HttpServletRequest) getRequest(); + StringBuffer url = new StringBuffer("https://").append(request.getServerName()); + String uri = request.getRequestURI(); + if (uri != null) { + url.append(uri); + } + return url; + } + } +}