Spring Boot 将 HTTP 重定向到 HTTPS
Posted
技术标签:
【中文标题】Spring Boot 将 HTTP 重定向到 HTTPS【英文标题】:Spring Boot redirect HTTP to HTTPS 【发布时间】:2014-12-26 15:56:10 【问题描述】:对于基于 Spring Boot 的应用程序,我在 application.properties 中配置了 ssl 属性,请在此处查看我的配置:
server.port=8443
server.ssl.key-alias=tomcat
server.ssl.key-password=123456
server.ssl.key-store=classpath:key.p12
server.ssl.key-store-provider=SunJSSE
server.ssl.key-store-type=pkcs12
我在 Application.class 中添加了连接,比如
@Bean
public EmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory()
final TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
factory.addAdditionalTomcatConnectors(this.createConnection());
return factory;
private Connector createConnection()
final String protocol = "org.apache.coyote.http11.Http11NioProtocol";
final Connector connector = new Connector(protocol);
connector.setScheme("http");
connector.setPort(9090);
connector.setRedirectPort(8443);
return connector;
但是当我尝试以下操作时
http://127.0.0.1:9090/
重定向到
https://127.0.0.1:8443/
不执行。谁遇到过类似的问题?
【问题讨论】:
【参考方案1】:要让 Tomcat 执行重定向,您需要为它配置一个或多个安全约束。您可以通过使用TomcatEmbeddedServletContainerFactory
子类对Context
进行后处理来做到这一点。
例如:
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory()
@Override
protected void postProcessContext(Context context)
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
;
由于CONFIDENTIAL
和/*
,这将导致Tomcat 将每个请求重定向到HTTPS。如果您需要对重定向和不重定向的内容进行更多控制,您可以配置多个模式和多个约束。
上述TomcatEmbeddedServletContainerFactory
子类的实例应使用@Configuration
类中的@Bean
方法定义为bean。
【讨论】:
Jetty 有类似的方法吗? 这段代码sn-p的合适位置是什么? 如问题所示,TomcatEmbeddedServletContainerFactory
应作为配置类中的 bean 公开。
POST
请求不会被重定向。
@Oleksii 是的,没错。这个问题专门关于Tomcat,所以这就是答案所要解决的问题。如果您对不同的嵌入式服务器感兴趣(并且没有使用 Spring Security,因此此处的其他答案不适用),您应该问另一个特定于您正在使用的服务器的问题。【参考方案2】:
对于 Jetty(用 9.2.14 测试),您需要向 WebAppContext
添加额外配置(根据您的喜好调整 pathSpec
):
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.webapp.AbstractConfiguration;
import org.eclipse.jetty.webapp.WebAppContext;
class HttpToHttpsJettyConfiguration extends AbstractConfiguration
// http://wiki.eclipse.org/Jetty/Howto/Configure_SSL#Redirecting_http_requests_to_https
@Override
public void configure(WebAppContext context) throws Exception
Constraint constraint = new Constraint();
constraint.setDataConstraint(2);
ConstraintMapping constraintMapping = new ConstraintMapping();
constraintMapping.setPathSpec("/*");
constraintMapping.setConstraint(constraint);
ConstraintSecurityHandler constraintSecurityHandler = new ConstraintSecurityHandler();
constraintSecurityHandler.addConstraintMapping(constraintMapping);
context.setSecurityHandler(constraintSecurityHandler);
然后通过添加一个实现EmbeddedServletContainerCustomizer
的@Configuration
类以及一个侦听非安全端口的新Connector
来连接这个类:
@Configuration
public class HttpToHttpsJettyCustomizer implements EmbeddedServletContainerCustomizer
@Override
public void customize(ConfigurableEmbeddedServletContainer container)
JettyEmbeddedServletContainerFactory containerFactory = (JettyEmbeddedServletContainerFactory) container;
//Add a plain HTTP connector and a WebAppContext config to force redirect from http->https
containerFactory.addConfigurations(new HttpToHttpsJettyConfiguration());
containerFactory.addServerCustomizers(server ->
HttpConfiguration http = new HttpConfiguration();
http.setSecurePort(443);
http.setSecureScheme("https");
ServerConnector connector = new ServerConnector(server);
connector.addConnectionFactory(new HttpConnectionFactory(http));
connector.setPort(80);
server.addConnector(connector);
);
这意味着 SSL Connector
在本例中已配置并侦听端口 443。
【讨论】:
【参考方案3】:在您的应用程序*.properties 文件上设置此属性(如果您在代理后面运行,则为 HTTPS 标头设置相应的特定于 servlet 的配置)并设置 Spring Security(例如,具有 org.springframework. boot:spring-boot-starter-security 在你的类路径上)应该足够了:
security.require-ssl=true
现在,由于某种原因,禁用基本身份验证时不支持配置(至少在旧版本的 Spring Boot 上)。因此,在这种情况下,您需要采取额外的步骤并自己通过手动配置代码的安全性来兑现它,如下所示:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter
@Inject private SecurityProperties securityProperties;
@Override
protected void configure(HttpSecurity http) throws Exception
if (securityProperties.isRequireSsl()) http.requiresChannel().anyRequest().requiresSecure();
因此,如果您在代理后面使用 Tomcat,您的应用程序*.properties 文件中将包含所有这些属性:
security.require-ssl=true
server.tomcat.remote_ip_header=x-forwarded-for
server.tomcat.protocol_header=x-forwarded-proto
【讨论】:
也可以使用 server.use-forward-headers=true 启用 forwarded-* 标头 属性 'security.require-ssl' 已弃用:安全自动配置不再可自定义。改为提供您自己的 WebSecurityConfigurer bean。security.require-ssl
现已弃用。
@Learner 我是这样做的***.com/a/56288331/826983
@Learner 没错。不用了。【参考方案4】:
批准的答案对我来说还不够。
我还必须将以下内容添加到我的网络安全配置中,因为我没有使用默认的 8080 端口:
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
@Autowired
private Environment environment;
@Override
public void configure(HttpSecurity http) throws Exception
// other security configuration missing
http.portMapper()
.http(Integer.parseInt(environment.getProperty("server.http.port"))) // http port defined in yml config file
.mapsTo(Integer.parseInt(environment.getProperty("server.port"))); // https port defined in yml config file
// we only need https on /auth
http.requiresChannel()
.antMatchers("/auth/**").requiresSecure()
.anyRequest().requiresInsecure();
【讨论】:
作为替代方案,您也可以使用:@Value("$server.http.port") private int httpPort;
和 @Value("$server.port") private int httpsPort;
字段,那么您就不必 parseInt,并且代码 (imo) 看起来更清晰一些。 :)【参考方案5】:
只需执行 2 个步骤。
1- 在 pom.xml 中添加 spring 安全依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2- 在应用程序的根包中添加此类。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
@Override
protected void configure(HttpSecurity http) throws Exception
http.requiresChannel().anyRequest().requiresSecure();
【讨论】:
太棒了。在 ALB 后面时效果很好。如果不使用 spring security 的任何其他功能,也可以添加http.authorizeRequests().antMatchers("/**").permitAll()
这是“安全自动配置不再可定制。请提供您自己的 WebSecurityConfigurer bean”的解决方案。使用security.require-ssl=true
时的消息。谢谢! :) 注意:不要使用<groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId>
,因为这会导致某些安全类出现ClassNotFound
异常。
我在上面的代码中遇到了以下异常 - 2019-12-04 14:10:55.264 INFO 12236 --- [nio-8080-exec-5] o.apache.coyote.http11。 Http11Processor : Error parsing HTTP request header 注意:进一步出现的 HTTP 请求解析错误将在 DEBUG 级别记录。 java.lang.IllegalArgumentException:在方法名称中发现无效字符。 HTTP 方法名称必须是 org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:415) ~[tomcat-embed-core-9.0.27.jar:9.0.27] at 的标记
@PriyankaChaurishia 请将您的控制器 HTTP 方法重命名为有效名称。【参考方案6】:
由于 TomcatEmbeddedServletContainerFactory
has been removed 在 Spring Boot 2 中,使用这个:
@Bean
public TomcatServletWebServerFactory httpsRedirectConfig()
return new TomcatServletWebServerFactory ()
@Override
protected void postProcessContext(Context context)
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
;
【讨论】:
【参考方案7】:在Spring-Boot中,需要下面的依赖
第 1 步-
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
第 2 步- 只需对 application.properties 文件进行以下配置
- server.port=8443
- server.ssl.key.alias=ode-https
- server.ssl.key-store-type=JKS (just for testing i USED JSK, but for production normally use pkcs12)
- server.ssl.key-password=password
- server.ssl.key-store=classpath:ode-https.jks
第 3 步 - 现在需要使用上述详细信息生成证书。
keytool -genkey -alias ode-https -storetype JKS -keyalg RSA -keys ize 2048 -validity 365 -keystore ode-https.jks
第 4 步 - 将证书移动到程序中的资源文件夹。
第 5 步 - 创建配置类
@Configuration
public class HttpsConfiguration
@Bean
public ServletWebServerFactory servletContainer()
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory()
@Override
protected void postProcessContext(Context context)
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
;
tomcat.addAdditionalTomcatConnectors(redirectConnector());
return tomcat;
@Value("$server.port.http") //Defined in application.properties file
int httpPort;
@Value("$server.port") //Defined in application.properties file
int httpsPort;
private Connector redirectConnector()
Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
connector.setScheme("http");
connector.setPort(httpPort);
connector.setSecure(false);
connector.setRedirectPort(httpsPort);
return connector;
就是这样。
【讨论】:
【参考方案8】:@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter
@Override
protected void configure(HttpSecurity http) throws Exception
http.requiresChannel().anyRequest().requiresSecure();
如果您的应用程序位于负载均衡器或反向代理服务器之后,您需要将以下内容添加到您的 application.properties 文件中:
server.forward-headers-strategy=NATIVE
这将防止重定向循环。
如果您使用的是 Tomcat,您可以在 application.properties 文件中配置转发头的名称:
server.tomcat.remote_ip_header=x-forwarded-for
server.tomcat.protocol_header=x-forwarded-proto
请参阅Spring Boot Documentation 了解更多信息。
【讨论】:
你忘了提到的一件事是上面需要声明implementation 'org.springframework.boot:spring-boot-starter-security'
(或者它是Maven等价物)
这个答案告诉你如何强制 https-only连接,而不是如何重定向,不是吗?【参考方案9】:
使用拦截器发送重定向到 https://
(不需要 Spring Security)
这些看起来都很复杂。我们为什么不添加一个拦截器来检查端口,如果是 80 端口,则将其重定向到相同的 url,但以 https:// 为前缀。
@Component
public class HttpsConfig implements HandlerInterceptor
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
// String requestedPort = request.getServerPort() if you're not behind a proxy
String requestedPort = request.getHeader("X-Forwarded-Port"); // I'm behind a proxy on Heroku
if (requestedPort != null && requestedPort.equals("80")) // This will still allow requests on :8080
response.sendRedirect("https://" + request.getServerName() + request.getRequestURI() + (request.getQueryString() != null ? "?" + request.getQueryString() : ""));
return false;
return true;
别忘了注册你可爱的拦截器
@Configuration
public class WebMvcConfig implements WebMvcConfigurer
@Override
public void addInterceptors(InterceptorRegistry registry)
registry.addInterceptor(new HttpsConfig());
注意:您的生产网络服务器通常会在 1 或 2 个端口(80 不安全 443 安全)上运行,您应该知道它们是什么,所以我不认为这是允许其他端口的安全风险。
【讨论】:
安全性很复杂,我个人会使用 Spring security 而不是滚动我自己的解决方案,因为 Spring Security 抽象出许多我不需要添加到我的代码库中的问题。例如,通过简单地使用 Spring Security,我的应用程序将实现 HSTS、HPKP、X-Frame-Options、X-XSS-Protection、CSP 等。您可能希望在您的解决方案中添加对片段(在 HttpServletRequest 中称为部分)的支持。 抱歉,我刚刚了解到片段由用户代理处理,而不是发送到服务器。 我想了很久。我认为简短的回答是,你是对的。不要重新发明***。对于我的简单用例,我不需要磁轮,只需要一个木制圆盘。因此,保持我的代码超级简单和可维护,并实现 2 秒解决方案#WorksForMe 我已经运行了几个星期,到目前为止一切顺利。以上是关于Spring Boot 将 HTTP 重定向到 HTTPS的主要内容,如果未能解决你的问题,请参考以下文章
负载均衡器后面带有 Spring Security 的 Spring Boot:将 HTTP 重定向到 HTTPS
Elastic Beanstalk 和 Spring Boot 将 HTTP 重定向到 HTTPS
(spring-boot) http 到 https 重定向,405 方法不允许消息
在 Spring Boot 应用程序中为 AWS Beanstalk 的 nginx 设置 HTTP 到 HTTPS 重定向
Spring Boot 2.3.2 server.forward-headers-strategy=framework 重定向到方案 http 而不是 https