1. 程式人生 > 程式設計 >Spring Boot 實現https ssl免密登入(X.509 pki登入)

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

要讓專案實現 ssl 免密登入,首先需要開啟 https

所以先從 Spring Boot 如何開啟 https 說起。

建立服務端證書

為了開啟 https ,我們需要一份證書。

實際開發中,會在網上申請一個機構頒發的證書。這裡為了方便,我會使用 openssl 命令自己生成一個證書來使用。

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

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

所有的密碼都是 123456 ,然後根據提示輸入相關資訊就好,如果嫌麻煩也可以直接回車跳過。

這樣我們就得到了證書 rootCA.crt

和私鑰 rootCA.key

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

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

同樣,密碼是 123456 ,檔名 localhost 可以自行修改。

接下來就是用 rootCA 給我們的服務端證書做簽名了,在此之前,我們先寫一個配置檔案,裡面寫有一些基本的配置

vi conf.config

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

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

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

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

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

最後再將簽名證書和私鑰打包到 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 目錄下之後編譯專案

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

修改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

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

這時啟動專案,就可以使用 https 訪問網站了,而且瀏覽器提示網站時安全的。

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

建立信託證書

信託證書中會存有 信任的外部實體的證書

這裡我們只要將 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 新增到專案中,並修改配置檔案

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

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只會在第一次訪問頁面時才會向客戶索取個人證書

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

建立客戶端證書

現在建立一個客戶端的證書,步驟和服務端的差不多一樣。

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

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

在生成客戶端證書時,那些資訊不建議跳過,因為在後續的步驟中,會獲取其中的資訊用以登入。比如我在 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 匯入,期間會要你輸入它的密碼。

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

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

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

這時候重新整理頁面,瀏覽器就會彈出一個對話方塊,讓你選擇個人認證了。

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

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";
}

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

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

Spring Boot 同時開啟http和https

相信大家都發現了,現在專案只能通過 https 訪問,如果用 http 訪問瀏覽器直接返回 Bad request 了。

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

要同時開啟 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;
  }
}

這時候啟動專案,注意看控制檯列印的資訊。

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

說明已經成功啟動 http 在埠 9999https8888 ,頁面也可以成功訪問了。

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

Spring Boot http自動跳轉https

上面我們已經可以同時訪問 htt p和 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;
  }
}

踩坑總結

  • 把服務端證書p12檔案新增到專案resources後,記得rebuild專案,否則target的classes中沒有生成證書檔案,會導致專案啟動失敗。
  • application.properties中的server.ssl.keyAlias需要和生成p12檔案的-name一致,否則也會導致專案無法啟動。
  • 如果要指定域名,需要修改conf.confg中的DNS.1,否則瀏覽器會提示網站不安全。

程式碼地址

https://github.com/Shurlormes/pkilogin

參考資料

https://www.baeldung.com/x-509-authentication-in-spring-security

到此這篇關於Spring Boot 實現https ssl免密登入(X.509 pki登入)的文章就介紹到這了,更多相關Spring Boot https ssl免密登入內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!