Spring Boot 實(shí)現(xiàn)https ssl免密登錄(X.509 pki登錄)
要讓項(xiàng)目實(shí)現(xiàn) ssl 免密登錄,首先需要開(kāi)啟 https 。
所以先從 Spring Boot 如何開(kāi)啟 https 說(shuō)起。
創(chuàng)建服務(wù)端證書(shū)為了開(kāi)啟 https ,我們需要一份證書(shū)。
實(shí)際開(kāi)發(fā)中,會(huì)在網(wǎng)上申請(qǐng)一個(gè)機(jī)構(gòu)頒發(fā)的證書(shū)。這里為了方便,我會(huì)使用 openssl 命令自己生成一個(gè)證書(shū)來(lái)使用。
openssl req -x509 -sha256 -days 3650 -newkey rsa:4096 -keyout rootCA.key -out rootCA.crt
所有的密碼都是 123456 ,然后根據(jù)提示輸入相關(guān)信息就好,如果嫌麻煩也可以直接回車(chē)跳過(guò)。
這樣我們就得到了證書(shū) rootCA.crt 和私鑰 rootCA.key 。
要在 Spring Boot 中實(shí)現(xiàn)服務(wù)器端 X.509 身份驗(yàn)證,還需要給我們的服務(wù)端也生成一個(gè)證書(shū)。
openssl req -new -newkey rsa:4096 -keyout localhost.key -out localhost.csr
同樣,密碼是 123456 ,文件名 localhost 可以自行修改。
接下來(lái)就是用 rootCA 給我們的服務(wù)端證書(shū)做簽名了,在此之前,我們先寫(xiě)一個(gè)配置文件,里面寫(xiě)有一些基本的配置
vi conf.config
authorityKeyIdentifier=keyid,issuerbasicConstraints=CA:FALSEsubjectAltName = @alt_names[alt_names]DNS.1 = localhost
其中 DNS.1 的值就是你的域名,比如 www.segmentfault.com , localhost 等等。如果這里填錯(cuò)了,訪問(wèn)網(wǎng)站時(shí),瀏覽器會(huì)提示網(wǎng)站不安全。
然后給服務(wù)端證書(shū)簽名,會(huì)提示你輸入 rootCA 的密碼
openssl x509 -req -CA rootCA.crt -CAkey rootCA.key -in localhost.csr -out localhost.crt -days 365 -CAcreateserial -extfile conf.config
成功后,讓我們查看一下證書(shū)的信息
openssl x509 -in localhost.crt -text
最后再將簽名證書(shū)和私鑰打包到 PKCS 文件中
openssl pkcs12 -export -out localhost.p12 -name 'localhost' -inkey localhost.key -in localhost.crt
這條指令會(huì)要你先輸入 localhost.key 的密碼,然后再要你定義 localhost.p12 的密碼。 localhost.p12 這個(gè)密碼一定要記住,因?yàn)樵?Spring 的配置文件中有用到。
另外需要特別注意的是, Spring 配置文件中 server.ssl.keyAlias 的值,就是命令中的 localhost(-name 'localhost') 。
Spring Boot開(kāi)啟https把 localhost.p12 復(fù)制到 resources 目錄下之后編譯項(xiàng)目
修改application.properties文件
server.port=8888server.ssl.key-store=classpath:localhost.p12server.ssl.key-store-password=123456server.ssl.keyStoreType=PKCS12server.ssl.keyAlias=localhost
在 chrome://settings/security 中,選擇 受信任的根證書(shū)頒發(fā)機(jī)構(gòu) 導(dǎo)入 rootCA.crt
這時(shí)啟動(dòng)項(xiàng)目,就可以使用 https 訪問(wèn)網(wǎng)站了,而且瀏覽器提示網(wǎng)站時(shí)安全的。
信托證書(shū)中會(huì)存有 信任的外部實(shí)體的證書(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.jksserver.ssl.trust-store-password=123456server.ssl.client-auth=need
注意:此時(shí)由于添加了server.ssl.client-auth=need,因?yàn)闆](méi)有添加個(gè)人證書(shū),所以這個(gè)時(shí)候刷新頁(yè)面,項(xiàng)目會(huì)無(wú)法訪問(wèn),如果想要同時(shí)兼任普通登錄,可以將need改成want,但是want只會(huì)在第一次訪問(wèn)頁(yè)面時(shí)才會(huì)向客戶(hù)索取個(gè)人證書(shū)
現(xiàn)在創(chuàng)建一個(gè)客戶(hù)端的證書(shū),步驟和服務(wù)端的差不多一樣。
openssl req -new -newkey rsa:4096 -nodes -keyout shurlormes.key -out shurlormes.csr
在生成客戶(hù)端證書(shū)時(shí),那些信息不建議跳過(guò),因?yàn)樵诤罄m(xù)的步驟中,會(huì)獲取其中的信息用以登錄。比如我在 Common Name 處填寫(xiě)的信息,就是等下用來(lái)登錄的用戶(hù)名。
接下來(lái)用 RootCA 給客戶(hù)端證書(shū)簽名
openssl x509 -req -CA rootCA.crt -CAkey rootCA.key -in shurlormes.csr -out shurlormes.crt -days 365 -CAcreateserial
然后再將簽名證書(shū)和私鑰打包到 PKCS 文件中
openssl pkcs12 -export -out shurlormes.p12 -name 'shurlormes' -inkey shurlormes.key -in shurlormes.crt
最后在 chrome://settings/security 選擇 個(gè)人證書(shū) 把 shurlormes.p12 導(dǎo)入,期間會(huì)要你輸入它的密碼。
這時(shí)候刷新頁(yè)面,瀏覽器就會(huì)彈出一個(gè)對(duì)話框,讓你選擇個(gè)人認(rèn)證了。
恭喜你,到了這一步, pki 登錄已經(jīng)完成了 99% 了。接下來(lái)就是通過(guò) request 獲取證書(shū)信息,然后處理字符串,拿到用戶(hù)名做登錄即可。
@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è)人證書(shū)信息:' + 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('用戶(hù)名:' + 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';}
相信大家都發(fā)現(xiàn)了,現(xiàn)在項(xiàng)目只能通過(guò) https 訪問(wèn),如果用 http 訪問(wèn)瀏覽器直接返回 Bad request 了。
要同時(shí)開(kāi)啟 https 和 http ,只需添加一個(gè) TomcatConfig 就可以
@Configurationpublic 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í)候啟動(dòng)項(xiàng)目,注意看控制臺(tái)打印的信息。
說(shuō)明已經(jīng)成功啟動(dòng) http 在端口 9999 , https 在 8888 ,頁(yè)面也可以成功訪問(wèn)了。
上面我們已經(jīng)可以同時(shí)訪問(wèn) htt p和 https ,但如果我要訪問(wèn) http 的時(shí)候,自動(dòng)跳轉(zhuǎn)的 https 呢?
只需要在上面的基礎(chǔ)上稍微改改就可以了。
@Configurationpublic 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ù)端證書(shū)p12文件添加到項(xiàng)目resources后,記得rebuild項(xiàng)目,否則target的classes中沒(méi)有生成證書(shū)文件,會(huì)導(dǎo)致項(xiàng)目啟動(dòng)失敗。 application.properties中的server.ssl.keyAlias需要和生成p12文件的-name一致,否則也會(huì)導(dǎo)致項(xiàng)目無(wú)法啟動(dòng)。 如果要指定域名,需要修改conf.confg中的DNS.1,否則瀏覽器會(huì)提示網(wǎng)站不安全。代碼地址
https://github.com/Shurlormes/pkilogin
參考資料https://www.baeldung.com/x-509-authentication-in-spring-security
到此這篇關(guān)于Spring Boot 實(shí)現(xiàn)https ssl免密登錄(X.509 pki登錄)的文章就介紹到這了,更多相關(guān)Spring Boot https ssl免密登錄內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!
相關(guān)文章:
1. moment轉(zhuǎn)化時(shí)間戳出現(xiàn)Invalid Date的問(wèn)題及解決2. python爬蟲(chóng)實(shí)戰(zhàn)之制作屬于自己的一個(gè)IP代理模塊3. 如何在jsp界面中插入圖片4. HTML 絕對(duì)路徑與相對(duì)路徑概念詳細(xì)5. WML的簡(jiǎn)單例子及編輯、測(cè)試方法第1/2頁(yè)6. 詳解盒子端CSS動(dòng)畫(huà)性能提升7. 解決ajax請(qǐng)求后臺(tái),有時(shí)收不到返回值的問(wèn)題8. asp批量添加修改刪除操作示例代碼9. .NET6打包部署到Windows Service的全過(guò)程10. ajax請(qǐng)求后臺(tái)得到j(luò)son數(shù)據(jù)后動(dòng)態(tài)生成樹(shù)形下拉框的方法
