也来聊下Spring Boot开启SSL

in Spring Boot with 2 comments

这部分的内容在官方文档中有提到。链接在这儿:Configure SSL 。

网络上关于Spring Boot开启SSL访问的文章有很多。希望这篇文章能带来一点不一样的。

生成证书

首先,开启SSL访问得有证书,因为是本地访问,那么我们就使用jdk自带的keytool生成一个。

PS C:\Program Files\Java\jdk1.8.0_241\bin> .\keytool -genkey -keyalg RSA -keysize 2048 -keystore D:\keystore.jks
输入密钥库口令:
再次输入新口令:
您的名字与姓氏是什么?
  [Unknown]:  hanbin
您的组织单位名称是什么?
  [Unknown]:  home
您的组织名称是什么?
  [Unknown]:  hanbin-pc
您所在的城市或区域名称是什么?
  [Unknown]:  Xi'an
您所在的省/市/自治区名称是什么?
  [Unknown]:  Shaanxi
该单位的双字母国家/地区代码是什么?
  [Unknown]:  China
CN=hanbin, OU=home, O=hanbin-pc, L=Xi'an, ST=Shaanxi, C=China是否正确?
  [否]:  y

输入 <mykey> 的密钥口令
        (如果和密钥库口令相同, 按回车):

Warning:
JKS 密钥库使用专用格式。建议使用 "keytool -importkeystore -srckeystore D:\keystore.jks -destkeystore D:\keystore.jks -deststoretype pkcs12" 迁移到行业标准格式 PKCS12。

基础配置

本文示例项目使用 https://blog.hanbinit.com.cn/archives/146 中的demo application。将D盘下面生成的keystore.jks复制到项目的resources目录下。

按照官方文档中的示例在application.properties中配置如下:

server.port=8443
server.ssl.key-store=classpath:keystore.jks
server.ssl.key-store-password=123456
server.ssl.key-password=123456

启动的时候可以看到如图1所示日志。

图1

可以看到,系统启动的时候只启动了8443端口,且是https协议的。

访问https://localhost:8443/get_name,结果如图2所示。

图2

server.ssl.enabled默认就是true,配置了证书信息后,SSL就算是被激活了。这个时候server.port配置的就不是http的端口了。官方有如下说明:

如果只配置了上面的信息,就相当于开启了https,禁用了http。

支持https后http去哪儿了

如果还需要支持http,需要通过代码实现。官方文档在这儿说明了如何开启多个Connector。

@Bean
public ServletWebServerFactory servletContainer() {
    TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
    tomcat.addAdditionalTomcatConnectors(createSslConnector());
    return tomcat;
}

private Connector createSslConnector() {
    Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
    Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
    try {
        File keystore = new ClassPathResource("keystore").getFile();
        File truststore = new ClassPathResource("keystore").getFile();
        connector.setScheme("https");
        connector.setSecure(true);
        connector.setPort(8443);
        protocol.setSSLEnabled(true);
        protocol.setKeystoreFile(keystore.getAbsolutePath());
        protocol.setKeystorePass("changeit");
        protocol.setTruststoreFile(truststore.getAbsolutePath());
        protocol.setTruststorePass("changeit");
        protocol.setKeyAlias("apitester");
        return connector;
    }
    catch (IOException ex) {
        throw new IllegalStateException("can't access keystore: [" + keystore
                + "] or truststore: [" + truststore + "]", ex);
    }
}

上面是官方提供的示例代码,它开启了一个SSL Connector。我们要在默认开启https的基础上至此http访问。应该对上面的代码进行改造。在启动类中添加如下代码:

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

    @Bean
    public ServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
        tomcat.addAdditionalTomcatConnectors(createHttpConnector());
        return tomcat;
    }

    private Connector createHttpConnector() {
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        connector.setScheme("http");
        connector.setSecure(false);
        connector.setPort(httpPort);
        return connector;
    }

在application.properties中添加配置项: server.http.port=8080

重新启动应用。可以看到日志和刚才不一样了,如图3。
图3
通过启动日志可以看到多出了8080的http支持。这个时候使用http://localhost:8080/get_name访问也能正常拿到结果了。

最后,说说http转发到https, 直接在代码中添加下面的代码是不会自动跳转的。

@Value("${server.port}")
private int httpsPort;
...
connector.setRedirectPort(httpsPort);
http的redirectPort只有在所使用的接口一定要使用https访问的时候才会跳转。所以还要再添加一些代码,指定默认Https Connector的拦截url。修改前面的servletContainer方法

@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(createHttpConnector());
    return tomcat;
}

接下来访问http://localhost:8080/get_name 会直接跳转到 https://localhost:8443/get_name

Comments are closed.
  1. 大佬您好,请问可以换一个友情链接吗

  2. @WhatZ2333

    可以。你加好了通知我。