012 提供Https支持(2017.06.22更新)
更新了关于生成证书的部分,添加了参数,详见下面生成证书部分
视频发布在youtube上面了
https://youtu.be/fnxF0kXVEJQ
优酷上面的链接
http://v.youku.com/v_show/id_XMjgzOTg5MDgwNA==.html?f=49760672
HTTPS(全称:Hypertext Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。
HTTPS的作用
确认网站的真实性:通过权威机构的证书来证明
保证数据传输的安全:客户端产生一个对称的密钥,通过服务器的证书来交换密钥,之后的数据使用密钥加密之后传输。
双向认证 SSL 协议的具体过程
① 浏览器发送一个连接请求给安全服务器。
② 服务器将自己的证书,以及同证书相关的信息发送给客户浏览器。
③ 客户浏览器检查服务器送过来的证书是否是由自己信赖的 CA 中心所签发的。如果是,就继续执行协议;如果不是,客户浏览器就给客户一个警告消息:警告客户这个证书不是可以信赖的,询问客户是否需要继续。
④ 接着客户浏览器比较证书里的消息,例如域名和公钥,与服务器刚刚发送的相关消息是否一致,如果是一致的,客户浏览器认可这个服务器的合法身份。
⑤ 服务器要求客户发送客户自己的证书。收到后,服务器验证客户的证书,如果没有通过验证,拒绝连接;如果通过验证,服务器获得用户的公钥。
⑥ 客户浏览器告诉服务器自己所能够支持的通讯对称密码方案。
⑦ 服务器从客户发送过来的密码方案中,选择一种加密程度最高的密码方案,用客户的公钥加过密后通知浏览器。
⑧ 浏览器针对这个密码方案,选择一个通话密钥,接着用服务器的公钥加过密后发送给服务器。
⑨ 服务器接收到浏览器送过来的消息,用自己的私钥解密,获得通话密钥。
⑩ 服务器、浏览器接下来的通讯都是用对称密码方案,对称密钥是加过密的。
Java提供相对简单的被称为keytool的命令行工具,可以简单地产生“自己签名”的证书。自己签名的证书只是用户产生的证书,没有正式在大家所熟知的认证权威那里注册过,因此不能确保它的真实性。但却能保证数据传输的安全性。
下面是官方网址
http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/keytool.html
生成证书
-dname属性说明
CN=cName, OU=orgUnit, O=org, L=city, S=state, C=countryCode
参数 | 说明 |
---|---|
CN | 个人名字,例如:”Susan Jones” |
OU | 短的组织名字,例如:”Purchasing” |
O | 长的组织名字,例如:”ABCSystems, Inc.” |
L | 本地(城市)名字,例如:”Palo Alto” |
S | 省名字,例如:”California” |
C | 两位国家代码,例如:中国是“CN” |
这里增加了-ext参数,不然会出现http://localhost:8190/template可以访问。但是http://127.0.0.1:8190/template 不能访问的问题。
1 、生成服务器证书库
keytool -validity 365 -genkeypair -v -alias server -keyalg RSA -keysize 2048 -keystore /Users/zhengjun/Documents/java_library/server.keystore -dname "CN=server,OU=Demo,O=Https Demo,L=Pudong,S=Shanghai,c=CN" -storepass 111111 -keypass 111111 -ext SAN=DNS:localhost,IP:127.0.0.1
2 、生成客户端证书库
keytool -validity 365 -genkeypair -v -alias client -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore /Users/zhengjun/Documents/java_library/client.p12 -dname "CN=client,OU=Demo,O=Https Demo,L=Pudong,S=Shanghai,c=CN" -storepass 222222 -keypass 222222 -ext SAN=DNS:localhost,IP:127.0.0.1
3 、从客户端证书库中导出客户端证书
keytool -export -v -alias client -keystore /Users/zhengjun/Documents/java_library/client.p12 -storetype PKCS12 -storepass 222222 -rfc -file /Users/zhengjun/Documents/java_library/client.cer
4 、从服务器证书库中导出服务器证书
keytool -export -v -alias server -keystore /Users/zhengjun/Documents/java_library/server.keystore -storepass 111111 -rfc -file /Users/zhengjun/Documents/java_library/server.cer
5 、生成客户端信任证书库(由服务端证书生成的证书库)
keytool -import -v -alias server -file /Users/zhengjun/Documents/java_library/server.cer -keystore /Users/zhengjun/Documents/java_library/client.truststore -storepass 222222
6 、将客户端证书导入到服务器证书库(使得服务器信任客户端证书)
keytool -import -v -alias client -file /Users/zhengjun/Documents/java_library/client.cer -keystore /Users/zhengjun/Documents/java_library/server.keystore -storepass 111111
7 、查看证书库中的全部证书
keytool -list -keystore /Users/zhengjun/Documents/java_library/server.keystore -storepass 111111
mac下面如果keychain里面没有证书,可以手动导入进去.File-import items选择*.p12那个,需要输入生成证书时指定的密码。
配置tomcat中conf下的server.xml文件
<!-- Define a SSL/TLS HTTP/1.1 Connector on port 8443
This connector uses the NIO implementation that requires the JSSE
style configuration. When using the APR/native implementation, the
OpenSSL style configuration is required as described in the APR/native
documentation -->
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
clientAuth="false" sslProtocol="SSL"
keystoreFile="/Users/zhengjun/Documents/java_library/server.keystore" keystorePass="111111"
truststoreFile="/Users/zhengjun/Documents/java_library/server.keystore" truststorePass="111111"/>
字段 | 说明 |
---|---|
keystoreFile | 指定服务器密钥库,如“/opt/web/ssl/server.keystore” |
keystorePass | 密钥库生成时的密码 |
truststoreFile | 受信任密钥库,和密钥库相同即可 |
truststorePass | 受信任密钥库密码 |
需要说明的是,修改TOMCAT的server.xml文件,如果是ECLIPSE中建的服务器,不会有效果,需要删除服务器重新建个服务器。配置好后重启tomcat就可以访问
配置web.xml去掉默认主页支持,交给spring mvc controller处理,配置SSL参数
<!-- 强制SSL配置,即普通的请求也会重定向为SSL请求 -->
<security-constraint>
<web-resource-collection>
<web-resource-name>SSL</web-resource-name>
<url-pattern>/*</url-pattern> <!-- 全站使用SSL -->
</web-resource-collection>
<user-data-constraint>
<description>SSL required</description>
<!-- CONFIDENTIAL: 要保证服务器和客户端之间传输的数据不能够被修改,且不能被第三方查看到 -->
<!-- INTEGRAL: 要保证服务器和client之间传输的数据不能够被修改 -->
<!-- NONE: 指示容器必须能够在任一的连接上提供数据。(即用HTTP或HTTPS,由客户端来决定) -->
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<welcome-file-list>
<welcome-file></welcome-file>
</welcome-file-list>
在controller里面添加一个处理主页信息的
AdminController
@Controller
public class AdminController
{
private static final Logger logger = LogManager.getLogger(AdminController.class.getName());
@RequestMapping("/")
public String index(ServletRequest req, ServletResponse res)
{
logger.info("controller index enter.");
return "login";
}
@RequestMapping(value = "/v1/login", method = RequestMethod.POST)
public String loginProc(ServletRequest req, ServletResponse res)
{
logger.info("controller loginProc enter.");
return "main";
}
}
现在通过wireshark抓包就看不出来具体的信息了。
这里因为是自签名所以chrome不信任这个证书,虽然是https,但是被划掉了。
那么如何使证书受信任呢?这就需要专门的权威部门来签发给你证书。这些机构称为CA,由于CA的信息已经被集成在操作系统中了,所以这些颁发证书的CA机构是操作系统可信的。操作系统检测到证书的颁发者是可信的时候,就会信任此证书。
也可以自己充当CA机构来签发证书,该CA所签发的证书只能本机可信。过程如下:
1.首先将CA信息安装到操作系统,使操作系统对该CA可信。
安装server.cer到操作系统,过程默认。
2.使用该CA来签发证书。
可以使用openssl来签发数字证书,这里用的是JAVA程序来签发证书的。
在网上找了一个登陆页面,加入工程
views/login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>HTML5/CSS3登录表单DEMO演示</title>
<script type="text/javascript" charset="UTF-8" src="resources/js/prefixfree.min.js"></script>
<style type="text/css">
/**
* 使用属性选择器 隐藏所有的checkboxes
*/
input[type=checkbox] {
visibility: hidden;
}
body {
background: url(resources/images/bg.png);
background-size: 100% 100%;
margin:0;
padding:0;}
.content {
width:600px;
height:350px;
margin:50px auto 10px auto;
}
.login-form {
width:400px;
height:177px;
margin:70px auto 0;
padding-top:73px;
position:relative;
background-image:-*-linear-gradient(top,rgb(255,255,255),rgb(242,242,242));
box-shadow:0 3px 3px rgba(21,62,78,0.8);
}
.login-form:before {
content:"";
position:absolute;
top:-50px;
left:150px;
width:102px;
height:102px;
padding:2px;
border:1px solid rgb(216,216,219);
background:#fff url("resources/images/profilepicture.jpg") no-repeat 2px 2px;
}
.not-registered {
position:absolute;
color:rgb(153,153,153);
font-weight:bold;
font-size:13px;
top:calc(100% + 20px);
background-color:rgb(255,255,255);
width:400px;
height:46px;
margin:0 auto;
line-height:46px;
text-align: center;
box-shadow:0 3px 3px rgba(21,62,78,0.8);
}
.not-registered a {
margin-left:5px;
text-decoration: none;
color:rgb(52,119,182);
cursor: pointer;
}
.login-form div {
width:216px;
height:28px;
margin:20px auto;
position:relative;
line-height:28px;
border:none;
}
.login-form .user-icon,
.login-form .password-icon {
display:inline-block;
font-family: 'loginform-icon';
font-size:15px;
text-align:center;
line-height:28px;
color:rgb(153,153,153);
position:absolute;
left:1px;
top:1px;
background-color:rgb(255,255,255);
border:none;
border-right:1px solid rgb(229,229,232);
width:30px;
height:28px;
transition: all 300ms linear;
}
.login-form .username input, .login-form .password input {
height:100%;
width:calc(100% - 40px);
padding-left:40px;
border-radius:2px;
border:1px solid;
border-color:rgb(229,229,232) rgb(220,220,221) rgb(213,213,213) rgb(220,220,221);
display:block;
transition: all 300ms linear;
}
.login-form .icon:before, .login-form .icon:after {
content:"";
position:absolute;
top:10px;
left:30px;
width:0;
height:0;
border:4px solid transparent;
border-left-color:rgb(255,255,255);
}
.login-form .icon:before {
top:9px;
border:5px solid transparent;
border-left-color:rgb(229,229,232);
}
.login-form .username input:focus, .login-form .password input:focus {
border-color:rgb(69,153,228);
box-shadow:0 0 2px 1px rgb(200,223,244);
}
.login-form .username input:focus + span, .login-form .password input:focus + span {
background:-*-linear-gradient(top,rgb(255,255,255),rgb(245,245,245));
color:rgb(51,51,51);
}
.login-form .username input:focus + span:after, .login-form .password input:focus + span:after {
border-left-color:rgb(250,250,250);
}
.login-form .account-control label {
margin-left:24px;
font-size:12px;
font-family: Arial, Helvetica, sans-serif;
cursor:pointer;
}
.login-form button[type="submit"] {
color:#fff;
font-weight:bold;
float:right;
width:68px;
height:30px;
position:relative;
background:-*-linear-gradient(top,rgb(74,162,241),rgb(52,119,182)) 1px 0 no-repeat,
-*-linear-gradient(top,rgb(52,118,181),rgb(36,90,141)) left top no-repeat;
background-size:66px 28px,68px 29px;
border:none;
border-top:1px solid rgb(52,118,181);
border-radius:2px;
box-shadow:inset 0 1px 0 rgb(86,174,251);
text-shadow:0 1px 1px rgb(51,113,173);
transition: all 200ms linear;
}
.login-form button[type="submit"]:hover {
text-shadow:0 0 2px rgb(255,255,255);
box-shadow:inset 0 1px 0 rgb(86,174,251),0 0 10px 3px rgba(74,162,241,0.5);
}
.login-form button[type="submit"]:active {
background:-*-linear-gradient(top,rgb(52,119,182),rgb(74,162,241)) 1px 0 no-repeat,
-*-linear-gradient(top,rgb(52,118,181),rgb(36,90,141)) left top no-repeat;
}
.login-form .account-control input {
width:0px;
height:0px;
}
.login-form label.check {
position:absolute;
left:0;
top:50%;
margin:-8px 0;
display:inline-block;
width:16px;
height:16px;
line-height: 16px;
text-align:center;
border-radius:2px;
background:-*-linear-gradient(top,rgb(255,255,255),rgb(246,246,246)) 1px 1px no-repeat,
-*-linear-gradient(top,rgb(227,227,230),rgb(165,165,165)) left top no-repeat;
background-size:14px 14px,16px 16px;
}
.login-form .account-control input:checked + label.check:before {
content:attr(data-on);
font-family:loginform-icon;
}
@font-face {
font-family: 'loginform-icon';
src: url("resources/font/loginform-icon.eot");
src: url("resources/font/loginform-icon.eot?#iefix") format('embedded-opentype'),
url("resources/font/loginform-icon.woff") format('woff'),
url("resources/font/loginform-icon.ttf") format('truetype'),
url("resources/font/loginform-icon.svg#loginform-icon") format('svg');
font-weight: normal;
font-style: normal;
}
</style>
<script type="text/javascript">
function doLogin() {
var password = hex_md5($("#password").val());
document.getElementById("form1").submit();
}
</script>
</head>
<body>
<div class="content">
<form action="v1/login" method="post" class="login-form">
<div class="username">
<input type="text" name="username" placeholder="user@gmail.com" autocomplete="on" />
<span class="user-icon icon">u</span>
</div>
<div class="password">
<input type="password" name="password" placeholder="********" />
<span class="password-icon icon">p</span>
</div>
<div class="account-control">
<input type="checkbox" name="Remember me" id="Remember me" value="Remember me" checked="checked" />
<label for="Remember me" data-on="c" class="check"></label>
<label for="Remember me" class="info">Remember me</label>
<button type="submit" onclick="doLogin();">Login</button>
</div>
<p class="not-registered">Not a registered user yet?<a>Sign up now!</a></p>
</form>
</div>
</body>
</html>
这里css样式没有使用外部链接的方式,因为那样加载页面的时候能看出来样式的改变,比较慢。
images添加图片
font添加字体
js添加脚本
添加一个main.jsp作为登录成功之后的主页。
简单的打个信息<h3>Main Page</h3>
现在应该可以看到登录页面了,点击Login可以跳转到主页。具体的验证功能下次再弄吧。这次到这里了。