Spring Boot 實現https ssl免密登陸(X.509 pki登陸)

要讓項目實現ssl免密登陸,首先須要開啓https
因此先從Spring Boot如何開啓https提及。java

建立服務端證書


爲了開啓https,咱們須要一份證書。node

實際開發中,會在網上申請一個機構頒發的證書。這裏爲了方便,我會使用openssl命令本身生成一個證書來使用。git

openssl req -x509 -sha256 -days 3650 -newkey rsa:4096 -keyout rootCA.key -out rootCA.crt

image.png

全部的密碼都是123456,而後根據提示輸入相關信息就好,若是嫌麻煩也能夠直接回車跳過。github

這樣咱們就獲得了證書rootCA.crt和私鑰rootCA.keyspring

要在Spring Boot中實現服務器端X.509身份驗證,還須要給咱們的服務端也生成一個證書。chrome

openssl req -new -newkey rsa:4096 -keyout localhost.key -out localhost.csr

一樣,密碼是123456,文件名localhost能夠自行修改。apache

接下來就是用rootCA給咱們的服務端證書作簽名了,在此以前,咱們先寫一個配置文件,裏面寫有一些基本的配置segmentfault

vi conf.config
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost

其中DNS.1的值就是你的域名,好比www.segmentfault.comlocalhost等等。若是這裏填錯了,訪問網站時,瀏覽器會提示網站不安全。瀏覽器

而後給服務端證書籤名,會提示你輸入rootCA的密碼tomcat

openssl x509 -req -CA rootCA.crt -CAkey rootCA.key -in localhost.csr -out localhost.crt -days 365 -CAcreateserial -extfile conf.config

成功後,讓咱們查看一下證書的信息

openssl x509 -in localhost.crt -text

image.png

最後再將簽名證書和私鑰打包到PKCS文件中

openssl pkcs12 -export -out localhost.p12 -name "localhost" -inkey localhost.key -in localhost.crt

這條指令會要你先輸入localhost.key的密碼,而後再要你定義localhost.p12的密碼。localhost.p12這個密碼必定要記住,由於在Spring的配置文件中有用到。
另外須要特別注意的是,Spring配置文件中server.ssl.keyAlias的值,就是命令中的localhost(-name "localhost")

Spring Boot開啓https


localhost.p12複製到resources目錄下以後編譯項目

image.png

修改application.properties文件

server.port=8888
server.ssl.key-store=classpath:localhost.p12
server.ssl.key-store-password=123456
server.ssl.keyStoreType=PKCS12
server.ssl.keyAlias=localhost

chrome://settings/security中,選擇受信任的根證書頒發機構導入rootCA.crt

image.png

這時啓動項目,就可使用https訪問網站了,並且瀏覽器提示網站時安全的。

image.png

建立信託證書


信託證書中會存有信任的外部實體的證書
這裏咱們只要將rootCA.crt添加進去就能夠了

keytool -import -trustcacerts -noprompt -alias ca -ext san=dns:localhost,ip:127.0.0.1 -file rootCA.crt -keystore localhost.jks

而後將localhost.jks添加到項目中,並修改配置文件

image.png

application.properties添加:

server.ssl.trust-store=classpath:localhost.jks
server.ssl.trust-store-password=123456
server.ssl.client-auth=need

注意:此時因爲添加了server.ssl.client-auth=need,由於沒有添加我的證書,因此這個時候刷新頁面,項目會沒法訪問,若是想要同時兼任普通登陸,能夠將need改爲want,可是want只會在第一次訪問頁面時纔會向客戶索取我的證書

image.png

建立客戶端證書


如今建立一個客戶端的證書,步驟和服務端的差很少同樣。

openssl req -new -newkey rsa:4096 -nodes -keyout shurlormes.key -out shurlormes.csr

image.png

在生成客戶端證書時,那些信息不建議跳過,由於在後續的步驟中,會獲取其中的信息用以登陸。好比我在Common Name處填寫的信息,就是等下用來登陸的用戶名。

接下來用RootCA給客戶端證書籤名

openssl x509 -req -CA rootCA.crt -CAkey rootCA.key -in shurlormes.csr -out shurlormes.crt -days 365 -CAcreateserial

而後再將簽名證書和私鑰打包到PKCS文件中

openssl pkcs12 -export -out shurlormes.p12 -name "shurlormes" -inkey shurlormes.key -in shurlormes.crt

最後在chrome://settings/security選擇我的證書shurlormes.p12導入,期間會要你輸入它的密碼。

PW}D(IN0%Q{[{G0X43KJWE3.png

2DLC301CH$(W9Y54@[P[FI0.png

ORX86_DV8RC9K7DM~MK[2MB.png

這時候刷新頁面,瀏覽器就會彈出一個對話框,讓你選擇我的認證了。

image.png

Spring Boot獲取我的證書信息


恭喜你,到了這一步,pki登陸已經完成了99%了。接下來就是經過 request 獲取證書信息,而後處理字符串,拿到用戶名作登陸便可。

@RequestMapping("/login")
public String login(HttpServletRequest request) {
    X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
    if(certs != null) {
        X509Certificate gaX509Cert = certs[0];
        String dn = gaX509Cert.getSubjectDN().toString();
        System.out.println("我的證書信息:" + dn);
        String username = "";
        String[] dnArray = dn.split(",");
        for (String dnItem : dnArray) {
            String[] dnInfo = dnItem.split("=");
            String key = dnInfo[0];
            String value = dnInfo[1];
            if("cn".equalsIgnoreCase(key.trim())) {
                username = value;
                break;
            }
        }
        System.out.println("用戶名:" + username);

        if(!StringUtils.isEmpty(username)) {
            SecurityContext securityContext = SecurityContextHolder.getContext();
            User userDetails = new User(username, "", Collections.EMPTY_LIST);
            securityContext.setAuthentication(new UsernamePasswordAuthenticationToken(userDetails, "", Collections.EMPTY_LIST));
            return "redirect:/";
        }

    }
    return "login";
}

image.png

image.png

Spring Boot 同時開啓http和https


相信你們都發現了,如今項目只能經過https訪問,若是用http訪問瀏覽器直接返回Bad request了。

image.png

要同時開啓httpshttp,只需添加一個TomcatConfig就能夠

@Configuration
public class TomcatHttpConfig {
    @Bean
    public TomcatServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
        tomcat.addAdditionalTomcatConnectors(initiateHttpConnector());
        return tomcat;
    }

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

這時候啓動項目,注意看控制檯打印的信息。

image.png

說明已經成功啓動http在端口9999https8888,頁面也能夠成功訪問了。

image.png

Spring Boot http自動跳轉https


上面咱們已經能夠同時訪問http和https,但若是我要訪問http的時候,自動跳轉的https呢?
只須要在上面的基礎上稍微改改就能夠了。

@Configuration
public class TomcatHttpConfig {
    @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(9999);
        connector.setSecure(false);
        connector.setRedirectPort(8888);
        return connector;
    }
}

踩坑總結


  1. 把服務端證書p12文件添加到項目resources後,記得rebuild項目,不然targetclasses中沒有生成證書文件,會致使項目啓動失敗。
  2. application.properties中的server.ssl.keyAlias須要和生成p12文件的-name一致,不然也會致使項目沒法啓動。
  3. 若是要指定域名,須要修改conf.confg中的DNS.1,不然瀏覽器會提示網站不安全。

代碼地址


https://github.com/Shurlormes...

參考資料


https://www.baeldung.com/x-50...

相關文章
相關標籤/搜索