1、介绍
通过证书验证用户身份(浏览器),其核心是利用cookie实现http和https的信息共享(同域名)。如http://test.abc.com/app/index.html 发现未验证后,跳转到https://test.abc.com:443/app/checkCrt.html身份验证,要求出去证书,确认后将身份信息带入http请求头部,跳转到原请求页面(http://test.abc.com/app/index.html ),读取身份信息后进入页面(出于安全考虑Cookie需要加密)。
流程图
流程说明:
登录流程详细介绍:
1). 未登录用户访问页面 如:http://test.abc.com/app/index.html
2). 【CertAuthValve】判断是否访问受限制资源,如访问受限制的资源则判断用户身份是否已验证,未验证则将用户重定向到身份验证页面,原始请求的url做为
query的一部分,登录成功后可以跳转回来, 如:https://test.abc.com:443/app/checkCrt.htm?done=/index.html。
3). 【CertAuthValve】对于https请求,apache读取请求提供的用户证书,获取证书中的邮件地址,并将该信息写入请求头中。
4). 【GetUserInfoValve】读取请求头,获取刚刚设置的用户邮件地址信息,进一步获取用户的详细信息,然后将这些信息加密后放入cookie中。
5). 登录完成,将用户外部重定向回原始页面。
2、具体实现
1)、安装apache、ssh、java、jboss等环境,略。
2)、生成服务证书和服务密码
openssl req -new -x509 -nodes -out /home/admin/app/conf/ssl.crt/server.crt -keyout /home/admin/app/conf/ssl.crt/server.key -days 3600
因为要和内网证书交互,所以需要一个内网证书公钥文件,可以通过以下方式获取:
获取方法:IE->工具->Internet选项->内容->证书->受信任的根证书颁发机构,找到intranet行,点击导出,选择下一步,选择Base64编码X.509,将证书文件保存为intranet-ca.crt,拷贝到目录/home/admin/app/conf/ssl.crt/。
3)、apache(httpd.conf)配置
应用和身份验证页面放在一起,所以需要同时配置两个虚拟主机,同时监听80(处理http请求)、443(处理https请求)端口。
#监听端口Listen 80Listen 443#app的虚拟主机配置NameVirtualHost *:80
<VirtualHost *:80>
ServerAdmin sa@abc.com
ServerName test.abc.com
DocumentRoot /home/admin/app/target/app/htdocs/
</VirtualHost>#身份验证的虚拟主机配置NameVirtualHost *:443
<VirtualHost *:443>
ServerAdmin sa@abc.com
ServerName test.abc.com
DocumentRoot /home/admin/app/target/app/htdocs/
SSLEngine on
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+SSLv3:+EXP:+eNULL
#该指令为虚拟主机指定证书文件名。
SSLCertificateFile /home/
admin/app/conf/ssl.crt/server.crt
#该指令为证书指定一个对应的私钥文件
SSLCertificateKeyFile /home
/admin/app/conf/ssl.crt/server.key
#该指令为指定一个包含Certificate Authority证书的文件
SSLCACertificateFile /home/admin/app/conf/ssl.crt/intranet-ca.cer
SSLProxyEngine on
RewriteEngine on
#设置客户端证书验证为必须
SSLVerifyClient require
#因为一个CA证书能够被另一个CA证书验证,所以可以形成一个CA证书链.使用该指令可指定服务器验证用户证书时可以查找多少个CA证明。
#设置认证深度:一般用默认10。
SSLVerifyDepth 10
#把mod_ssl里的变量变为全局环境的变量
RequestHeader unset SSL_CLIENT_S_DN_Email
RequestHeader add SSL_CLIENT_S_DN_Email %{SSL_CLIENT_S_DN_Email}e
</VirtualHost>
4)、代码片段
//CertAuthValve.java
//判断session中是否有用户邮箱地址
SessionValue session = SessionHelper.getSessionValue(rundata);
if (StringUtil.isNotEmpty(session.getCropEmail())) {
return null;
}
// 从内网证书中获取用户邮箱地址: SSL_CLIENT_S_DN_Email
String cropEmail = rundata.getRequest().getHeader(SSL_CLIENT_HEADER_MAIL);
if (StringUtil.isNotEmpty(cropEmail)) {
//将邮箱地址保存到session
session.setCropEmail(cropEmail);
SessionHelper.saveSessionValue(rundata, session);
if (log.isDebugEnabled()) {
log.debug("用户" + session.getCropEmail() + "已经通过证书验证");
}
return null;
}
URIBrokerService uriBrokerService = (URIBrokerService) getWebxComponent().getService(
URIBrokerService.SERVICE_NAME);
URIBroker noPermissionUriBroker = uriBrokerService.getURIBroker(CHECK_CRT_URL);
//请求的原始URL & 验证的URL
String requestPath = rundata.getPathInfo().replace("_", "");
String checkCrtUrl = (String) noPermissionUriBroker.getPath().get(
noPermissionUriBroker.getPath().size() - 1);
try {
//原始请求判断
if (requestPath.equalsIgnoreCase(checkCrtUrl)) {
//当前是https请求,但是依然不能得到证书信息,转到禁止页面
//(要将禁止页面加入到允许访问的配置文件中,不然会导致循环重定向)
URIBroker uriBroker = uriBrokerService.getURIBroker("forbidden");
rundata.setRedirectLocation(uriBroker.render());
} else {
//转到证书验证页面
rundata.setRedirectLocation(noPermissionUriBroker.render() + "?done=" + rundata.getPathInfo());
}
} catch (IOException e) {
log.error("权限验证重定向出错", e);
}
return new BreakPipeline();
//GetUserInfoValve.java
Object user = rundata.getSession().getAttribute("userInfo");
if (user == null) {
SessionValue session = SessionHelper.getSessionValue(rundata);
String email = session.getCropEmail();
Employe employe = PersonInfoUtil.getPersonInfoByEmail(email);
// 写入cookie
session.setEmployeeId(employe.getEmployeId());
session.setName(employe.getName());
session.setCropEmail(employe.getEmail());
SessionHelper.saveSessionValue(rundata, session);
}
采集到花瓣
posted on 2011-12-09 16:09
josson 阅读(2430)
评论(0) 编辑 收藏 所属分类:
java 开发