要让项目实现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...