You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将主机名重定向至FQDN?Spring Boot HTTPS场景

实现Spring Boot非FQDN到FQDN的强制重定向

我之前刚好搞定过一模一样的场景——用自签HTTPS的Spring Boot服务,要把所有短主机名的访问都跳转到带域名的FQDN上。下面给你两个靠谱的方案,都不用额外加Nginx这类反向代理,纯Spring Boot原生实现:

前提准备

先确认你的配置文件(比如application.properties/application.yml)已经配置好这几个关键参数:

# 短主机名
app.hostname=mymachine
# 域名后缀
app.domain=mydomain.com
# 你的HTTPS端口
server.port=8888
server.ssl.enabled=true
# 自签证书的其他配置(比如key-store路径、密码等)
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password=yourpassword
server.ssl.key-store-type=PKCS12

方案一:用Spring MVC拦截器(推荐,适合Web应用)

这个方案适合基于Spring MVC的应用,能精准控制哪些路径需要重定向,也容易维护:

  1. 创建一个拦截器类,注入配置的主机名和域名,在请求处理前判断是否需要重定向:
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

@Component
public class FqdnRedirectInterceptor implements HandlerInterceptor {

    @Value("${app.hostname}")
    private String shortHostname;

    @Value("${app.domain}")
    private String domain;

    @Value("${server.port}")
    private int serverPort;

    private final String fqdn;

    // 初始化FQDN
    public FqdnRedirectInterceptor(@Value("${app.hostname}") String shortHostname,
                                   @Value("${app.domain}") String domain) {
        this.fqdn = shortHostname + "." + domain;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String requestHost = request.getServerName();
        // 如果请求的主机名不是FQDN,且是我们配置的短主机名(避免误跳转到其他合法域名)
        if (!fqdn.equalsIgnoreCase(requestHost) && shortHostname.equalsIgnoreCase(requestHost)) {
            // 拼接完整的FQDN URL,保留原请求的路径和参数
            String redirectUrl = String.format("https://%s:%d%s%s",
                    fqdn,
                    serverPort,
                    request.getRequestURI(),
                    request.getQueryString() != null ? "?" + request.getQueryString() : "");
            // 301永久重定向(如果是临时需求可以用302)
            response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
            response.setHeader("Location", redirectUrl);
            return false; // 终止后续请求处理
        }
        return true; // 正常放行
    }
}
  1. 注册这个拦截器到Spring MVC配置:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    private final FqdnRedirectInterceptor fqdnRedirectInterceptor;

    @Autowired
    public WebMvcConfig(FqdnRedirectInterceptor fqdnRedirectInterceptor) {
        this.fqdnRedirectInterceptor = fqdnRedirectInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 拦截所有请求路径
        registry.addInterceptor(fqdnRedirectInterceptor).addPathPatterns("/**");
    }
}

方案二:用Servlet Filter(更底层,适合所有请求)

如果你的应用有静态资源或者非MVC的请求(比如Actuator端点),用Filter会更全面,覆盖所有进入Tomcat的请求:

  1. 创建Filter类:
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
@WebFilter(urlPatterns = "/*")
@Order(1) // 设置优先级,确保这个Filter最先执行
public class FqdnRedirectFilter implements Filter {

    @Value("${app.hostname}")
    private String shortHostname;

    @Value("${app.domain}")
    private String domain;

    @Value("${server.port}")
    private int serverPort;

    private String fqdn;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.fqdn = shortHostname + "." + domain;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        String requestHost = httpRequest.getServerName();
        if (!fqdn.equalsIgnoreCase(requestHost) && shortHostname.equalsIgnoreCase(requestHost)) {
            String redirectUrl = String.format("https://%s:%d%s%s",
                    fqdn,
                    serverPort,
                    httpRequest.getRequestURI(),
                    httpRequest.getQueryString() != null ? "?" + httpRequest.getQueryString() : "");
            httpResponse.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
            httpResponse.setHeader("Location", redirectUrl);
            return;
        }
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        // 无需清理资源
    }
}

注意事项

  • 301 vs 302重定向:如果是永久的域名规范,用301(永久重定向),搜索引擎会更新索引;如果只是临时测试,用302(临时重定向)。
  • 自签证书的信任问题:重定向后用户浏览器还是会提示证书不安全,这个是自签证书的固有问题,生产环境建议用CA颁发的证书。
  • 端口处理:如果你的HTTPS用的是默认443端口,可以去掉URL里的:8888,代码里可以加个判断:serverPort == 443 ? "" : ":" + serverPort。
  • 避免无限重定向:代码里已经判断了只有请求主机是配置的短主机名才会重定向,所以不会出现循环跳转的问题。

内容的提问来源于stack exchange,提问作者spring5150

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:37:38