posts - 122,  comments - 25,  trackbacks - 0
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 80
Listen 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里的变量变为全局环境的变量
    SSLOptions +StdEnvVars

    #将证书中的邮件地址添加到请求头中
    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 开发

只有注册用户登录后才能发表评论。


网站导航:
 
<2011年12月>
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

常用链接

留言簿(3)

随笔分类

随笔档案

收藏夹

搜索

  •  

最新评论

阅读排行榜

评论排行榜