要讓項目實現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
全部的密碼都是123456,而後根據提示輸入相關信息就好,若是嫌麻煩也能夠直接回車跳過。github
這樣咱們就獲得了證書rootCA.crt和私鑰rootCA.key。spring
要在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.com,localhost等等。若是這裏填錯了,訪問網站時,瀏覽器會提示網站不安全。瀏覽器
而後給服務端證書籤名,會提示你輸入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
最後再將簽名證書和私鑰打包到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")。
把localhost.p12複製到resources目錄下以後編譯項目
修改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
這時啓動項目,就可使用https訪問網站了,並且瀏覽器提示網站時安全的。
信託證書中會存有信任的外部實體的證書
這裏咱們只要將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添加到項目中,並修改配置文件
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只會在第一次訪問頁面時纔會向客戶索取我的證書
如今建立一個客戶端的證書,步驟和服務端的差很少同樣。
openssl req -new -newkey rsa:4096 -nodes -keyout shurlormes.key -out shurlormes.csr
在生成客戶端證書時,那些信息不建議跳過,由於在後續的步驟中,會獲取其中的信息用以登陸。好比我在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導入,期間會要你輸入它的密碼。
這時候刷新頁面,瀏覽器就會彈出一個對話框,讓你選擇我的認證了。
恭喜你,到了這一步,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"; }
相信你們都發現了,如今項目只能經過https訪問,若是用http訪問瀏覽器直接返回Bad request了。
要同時開啓https和http,只需添加一個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; } }
這時候啓動項目,注意看控制檯打印的信息。
說明已經成功啓動http在端口9999,https在8888,頁面也能夠成功訪問了。
上面咱們已經能夠同時訪問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; } }
https://github.com/Shurlormes...