虽然 Springboot 提供了相关参数用来启用 HTTPS 及相关配置,但在有些场景下需要我们做些定制化才能结合实际很好的启用 HTTPS 功能。比如,在我司有专门的秘钥系统来托管ssl证书及秘钥。此时,就不能简单的使用 server.ssl.xxx 来开启 HTTPS 了,而是需要先从秘钥系统下载 ssl 证书及秘钥,然后才能打开 springboot 的 HTTPS 功能。下面针对这种情况介绍下 springboot 中定制 https 的几种方式。
方式一:ServletWebServerFactory
代码语言:java复制@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(DemoHttpsProperties.class)
@ConditionalOnExpression("${demo.ssl.enabled}")
public class TomcatHttpsConfiguration {
@Resource
private DemoHttpsProperties demoHttpsProperties;
@Bean
public ServletWebServerFactory servletContainer() throws Exception {
// 获取 key-store 密码, 下载SSL证书并进行校验
String keyStorePassword = getKeyStorePassword();
String keyStoreFile = downloadKeyStore();
verifyCertificate(keyStoreFile, keyStorePassword);
log.info("create TomcatServletWebServerFactory");
TomcatServletWebServerFactory factory = 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);
}
};
// 添加 SSL Connector
factory.addConnectorCustomizers(connector -> {
connector.setPort(demoHttpsProperties.getPort());
connector.setSecure(true);
connector.setScheme("https");
Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
protocol.setSSLEnabled(true);
protocol.setKeystoreFile(keyStoreFile);
protocol.setKeystorePass(keyStorePassword);
protocol.setKeystoreType(demoHttpsProperties.getKeyStoreType());
protocol.setKeyAlias(demoHttpsProperties.getKeyAlias());
protocol.setSslProtocol("TLSv1.2");
protocol.setSslEnabledProtocols("TLSv1.2 TLSv1.3");
protocol.setClientAuth("false");
});
return factory;
}
private void verifyCertificate(String keyStoreFile, String password) throws Exception {
KeyStore ks = KeyStore.getInstance(demoHttpsProperties.getKeyStoreType());
FileInputStream fis = new FileInputStream(keyStoreFile);
ks.load(fis, password.toCharArray());
fis.close();
X509Certificate x509Certificate = (X509Certificate) ks.getCertificate(demoHttpsProperties.getKeyAlias());
x509Certificate.checkValidity();
}
private String downloadKeyStore(KeyApi keyApi){
...
}
private String getKeyStorePassword() {
...
}
}
这种方式下,如果想做成 autoconfig 需要禁止 springboot web 的自动配置,否则启动时会出现多个 tomcat 的错误,可用以下两种方式解决:
@SpringBootApplication(exclude = ServletWebServerFactoryAutoConfiguration.class)
- 或者配置文件中添加:spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
方式二:WebServerFactoryCustomizer
代码语言:java复制public class TomcatWebServerCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
public TomcatWebServerCustomizer(DemoHttpsProperties props) throws Exception {
// 获取 key-store 密码, 下载SSL证书并进行校验
this.keyStoreType = props.getKeyStoreType();
this.keyAlias = props.getKeyAlias();
this.keyStorePassword = getKeyStorePassword();
this.keyStoreFile = downloadKeyStore();
verifyCertificate(this.keyStoreFile, this.keyStoreType, this.keyStorePassword);
}
@Override
public void customize(TomcatServletWebServerFactory factory) {
factory.addConnectorCustomizers(connector -> {
connector.setSecure(true);
connector.setScheme("https");
Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
protocol.setSSLEnabled(true);
protocol.setKeystoreFile(keyStoreFile);
protocol.setKeystorePass(keyStorePassword);
protocol.setKeystoreType(keyStoreType);
protocol.setKeyAlias(keyAlias);
protocol.setSslProtocol("TLSv1.2");
protocol.setSslEnabledProtocols("TLSv1.2 TLSv1.3");
protocol.setClientAuth("false");
});
}
private void verifyCertificate(String keyStoreFile, String keyStoreType, String keyStorePwd) throws Exception {
KeyStore ks = KeyStore.getInstance(keyStoreType);
FileInputStream fis = new FileInputStream(keyStoreFile);
ks.load(fis, keyStorePwd.toCharArray());
fis.close();
X509Certificate x509Certificate = (X509Certificate) ks.getCertificate(keyAlias);
x509Certificate.checkValidity();
}
private String downloadKeyStore() {
...
}
private String getKeyStorePassword() {
...
}
}
方式三:重定向
网上见到最多的就是这种方式,同时启用 http 和 https 端口,将 http 请求重定向到 https,具体如下:
代码语言:java复制@Configuration
public class HttpsConfig {
@Bean
public TomcatServletWebServerFactory 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(initiateHttpConnector());
return tomcat;
}
private Connector initiateHttpConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
connector.setPort(8080);
connector.setSecure(true);
connector.setRedirectPort(443);
return connector;
}
}