要讓項(xiàng)目實(shí)現(xiàn) ssl 免密登錄,首先需要開啟 https 。
所以先從 Spring Boot 如何開啟 https 說起。
創(chuàng)建服務(wù)端證書
為了開啟 https ,我們需要一份證書。
實(shí)際開發(fā)中,會在網(wǎng)上申請一個(gè)機(jī)構(gòu)頒發(fā)的證書。這里為了方便,我會使用 openssl 命令自己生成一個(gè)證書來使用。
openssl req -x509 -sha256 -days 3650 -newkey rsa:4096 -keyout rootCA.key -out rootCA.crt

所有的密碼都是 123456 ,然后根據(jù)提示輸入相關(guān)信息就好,如果嫌麻煩也可以直接回車跳過。
這樣我們就得到了證書 rootCA.crt 和私鑰 rootCA.key 。
要在 Spring Boot 中實(shí)現(xiàn)服務(wù)器端 X.509 身份驗(yàn)證,還需要給我們的服務(wù)端也生成一個(gè)證書。
openssl req -new -newkey rsa:4096 -keyout localhost.key -out localhost.csr
同樣,密碼是 123456 ,文件名 localhost 可以自行修改。
接下來就是用 rootCA 給我們的服務(wù)端證書做簽名了,在此之前,我們先寫一個(gè)配置文件,里面寫有一些基本的配置
vi conf.config
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
其中 DNS.1 的值就是你的域名,比如 www.segmentfault.com , localhost 等等。如果這里填錯(cuò)了,訪問網(wǎng)站時(shí),瀏覽器會提示網(wǎng)站不安全。
然后給服務(wù)端證書簽名,會提示你輸入 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

最后再將簽名證書和私鑰打包到 PKCS 文件中
openssl pkcs12 -export -out localhost.p12 -name "localhost" -inkey localhost.key -in localhost.crt
這條指令會要你先輸入 localhost.key 的密碼,然后再要你定義 localhost.p12 的密碼。localhost.p12 這個(gè)密碼一定要記住,因?yàn)樵?Spring 的配置文件中有用到。
另外需要特別注意的是, Spring 配置文件中 server.ssl.keyAlias 的值,就是命令中的localhost(-name "localhost") 。
Spring Boot開啟https
把 localhost.p12 復(fù)制到 resources 目錄下之后編譯項(xiàng)目

修改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 中,選擇 受信任的根證書頒發(fā)機(jī)構(gòu) 導(dǎo)入 rootCA.crt

這時(shí)啟動項(xiàng)目,就可以使用 https 訪問網(wǎng)站了,而且瀏覽器提示網(wǎng)站時(shí)安全的。

創(chuàng)建信托證書
信托證書中會存有 信任的外部實(shí)體的證書
這里我們只要將 rootCA.crt 添加進(jìn)去就可以了
keytool -import -trustcacerts -noprompt -alias ca -ext san=dns:localhost,ip:127.0.0.1 -file rootCA.crt -keystore localhost.jks
然后將 localhost.jks 添加到項(xiàng)目中,并修改配置文件

application.properties添加:
server.ssl.trust-store=classpath:localhost.jks
server.ssl.trust-store-password=123456
server.ssl.client-auth=need
注意:此時(shí)由于添加了server.ssl.client-auth=need,因?yàn)闆]有添加個(gè)人證書,所以這個(gè)時(shí)候刷新頁面,項(xiàng)目會無法訪問,如果想要同時(shí)兼任普通登錄,可以將need改成want,但是want只會在第一次訪問頁面時(shí)才會向客戶索取個(gè)人證書

創(chuàng)建客戶端證書
現(xiàn)在創(chuàng)建一個(gè)客戶端的證書,步驟和服務(wù)端的差不多一樣。
openssl req -new -newkey rsa:4096 -nodes -keyout shurlormes.key -out shurlormes.csr
在生成客戶端證書時(shí),那些信息不建議跳過,因?yàn)樵诤罄m(xù)的步驟中,會獲取其中的信息用以登錄。比如我在 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 選擇 個(gè)人證書 把 shurlormes.p12 導(dǎo)入,期間會要你輸入它的密碼。



這時(shí)候刷新頁面,瀏覽器就會彈出一個(gè)對話框,讓你選擇個(gè)人認(rèn)證了。

Spring Boot獲取個(gè)人證書信息
恭喜你,到了這一步, pki 登錄已經(jīng)完成了 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("個(gè)人證書信息:" + 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 同時(shí)開啟http和https
相信大家都發(fā)現(xiàn)了,現(xiàn)在項(xiàng)目只能通過 https 訪問,如果用 http 訪問瀏覽器直接返回 Bad request 了。

要同時(shí)開啟 https 和 http ,只需添加一個(gè) 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;
}
}
這時(shí)候啟動項(xiàng)目,注意看控制臺打印的信息。

說明已經(jīng)成功啟動 http 在端口 9999 , https 在 8888 ,頁面也可以成功訪問了。

Spring Boot http自動跳轉(zhuǎn)https
上面我們已經(jīng)可以同時(shí)訪問 htt p和 https ,但如果我要訪問 http 的時(shí)候,自動跳轉(zhuǎn)的https 呢?
只需要在上面的基礎(chǔ)上稍微改改就可以了。
@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;
}
}
踩坑總結(jié)
把服務(wù)端證書 p12 文件添加到項(xiàng)目 resources 后,記得 rebuild 項(xiàng)目,否則 target的 classes 中沒有生成證書文件,會導(dǎo)致項(xiàng)目啟動失敗。
application.properties 中的 server.ssl.keyAlias 需要和生成 p12 文件的 -name一致,否則也會導(dǎo)致項(xiàng)目無法啟動。
如果要指定域名,需要修改 conf.confg 中的 DNS.1 ,否則瀏覽器會提示網(wǎng)站不安全。
原文地址:https://www.toutiao.com/i6914248771161604615/