`
Kenny.Lee
  • 浏览: 511129 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

自定义客户端登录CAS服务器-iframe实现

    博客分类:
  • SSO
阅读更多
环境:
tomcat7.05
cas-server-3.4.5
cas-client-3.2.0

呃,你没看错,是用iframe实现。这个不算是一个好的方案,甚至我自己都有点看不起自己的感觉。但却是一个简单易用并最能兼容以后CAS-server更新的一套方案(还用说么,你都用iframe了)。目前项目比较紧急没时间去研究Spring WebFlow 3的情况下只好出此下策了,大神请喷。

这个题目相信使用过CAS的朋友都不会陌生了。由于安全性的问题,CAS-Server 一直不建议把验证放到客户端之上。所以不提供验证表单的接口,或者说必须我们去开发扩展。而在实际应用中,很多单点登录的表单却是在客户端中完成,例如在某门户首页的登陆框。这些是不可能在Cas-Server上直接改造成门户的。所以造成了必须在门户中进行CAS服务器表单验证的局面。

当然这一切都是建立在我目前对CAS的熟悉程度来说的,欢迎指教。

其实网上也有比较多的方案,但基本上是版本对不上了,我也逼于无奈才采取这种比较傻瓜式的方法进行验证。

不管怎样,虽然觉得说名字大家都知道实现原理了,但也应该照顾一些不理解的。

1.自己的登录页面中用iframe加载cas-server的登录页面。
2.当用户输入账号密码登录的时候,同时把账号密码信息传入cas-server登录页面,并且提交。
3.若成功则刷新页面,登录窗口消失。若失败则提示用户。这个iframe相对用户来说是不可见的。

引用一下前辈的客户端实现目标描述:
引用
客户端实现目标

客户端实现主要需要满足5个case:

1. 用户未在中央认证服务器登陆,访问客户端受保护资源时,客户端重定向到中央认证服务器请求TGT认证,认证失败,转回客户端登陆页面,保证受保护资源URL信息不丢失
2. 用户未在中央认证服务器登陆,访问客户端登陆页面时,客户端重定向到中央认证服务器请求TGT认证,认证失败,转回客户端登陆页面,此次登录页面不再受保护,允许访问
3. 用户已在中央认证服务器登陆,访问客户端受保护资源时,客户端重定向到中央认证服务器请求TGT认证,认证成功,直接转回受保护资源
4. 用户在客户端登陆页面提交用户名密码,客户端将用户名密码信息提交给服务器端,认证失败,转回客户端登陆页面,携带失败信息并保证转到登陆页面前受保护资源URL信息不丢失
5. 用户在客户端登陆页面提交用户名密码,客户端将用户名密码信息提交给服务器端,认证成功,转回转到登陆页面前受保护资源


而技术方面没什么太大的要求,最主要是一些基本的javascript知识。

一、改造Cas-Server默登陆页面,让它对反馈信息支持。

打开 WEB-INF\view\jsp\default\ui\casLoginView.jsp
在<jsp:directive.include file="includes/bottom.jsp" />之上加上以下代码

<%-- add by Kenny Begin --%>
<script type="text/javascript">
    function showErrorMsg(){
        if(window.parent.window.document.getElementById('cas_serverFrame')){
            var statusEl = document.getElementById('status');
            if(statusEl && statusEl.className === 'errors'){
                window.parent.window.alert('账号或密码有误!');
            }
        }
    }
    if(top.location !== self.location){
        showErrorMsg();
    }
</script>
<%-- add by Kenny End --%>


其中cas_serverFrame为我自定义登陆页面中iframe的id,稍后会看到。判断其实可以简化些,或者其他可以自由发挥了,供参考。

查找页面中的submit表单提交按钮,如果name为submit的话记得改掉(默认是name=submit),因为如果在html中有任何一个元素的name属性为submit时,则用javascript操作form元素时是无法执行submit(),会提示'不存在的方法'。这个卡了我不少时间。

二、门户中自定义登陆表单页面

<%-- 
    Document   : login
    Created on : 2011-1-11, 3:07:16
    Author     : Kenny
--%>

<%@ 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">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>远程CAS客户端登陆页面</title>
    <link rel="stylesheet" type="text/css" href="<%= request.getContextPath() %>/styles/main.css" />
    <script type="text/javascript" src="js/clientLogin.js"> </script>
</head>
<body>
    <h1>远程CAS客户端登陆页面</h1>
    <% if (request.getRemoteUser() == null) { %>
        <div id="errorMessage"></div>
        <form id="myLoginForm" action="https://localhost:8443/cas-server/login" method="post">
            <input type="hidden" id="service" name="service" value="">
            <input type="hidden" name="loginUrl" value="https://localhost:8443/sso_demo/login.jsp">
            <input type="hidden" name="submit" value="true" />
            <table>
                <tr>
                    <td>用户名:</td>
                    <td><input type="text" id="username" name="username"></td>
                </tr>
                <tr>
                    <td>密&nbsp;&nbsp;码:</td>
                    <td><input type="password" id="password" name="password"></td>
                </tr>
                <tr>
                    <td colspan="2"><input type="button" value="登陆" id="loginButt" /></td>
                </tr>
            </table>
        </form>
        <iframe style="display:none;" src="" name="cas_serverFrame"  id="cas_serverFrame" ></iframe>
        <script type="text/javascript">
            loadedIFrame(window.event);
        </script>
    <% } else { %>
        <div class="welcome">您好:<%= request.getRemoteUser() %></div>
        <div id="logout">
            <a href="https://localhost:8443/cas-server/logout?service=https://localhost:8443/sso_demo/login.jsp">单点登出</a>
        </div>
    <% } %>
</body>
</html>


三、登陆页面中用到的clientLogin.js

var constants = {
    loginButt : 'loginButt',
    clientLoginForm : 'myLoginForm',
    casServerFrame : 'cas_serverFrame',
    loginFormUser : 'username',
    loginFormPassword : 'password',
    serverLoginFormId : 'fm1',
    redirectURLName : 'service',
    loginURL : 'https://localhost:8443/sso_demo/login.jsp',
    casServerURL : 'https://localhost:8443/cas-server/login'
}

function initEvents(e){
    var loginButtEl = document.getElementById(constants.loginButt);
    if(loginButtEl){
        registerListener(loginButtEl, 'click', loginCAS);
    }
    var loginFormEl = document.getElementById(constants.clientLoginForm);
    if(loginFormEl){
        registerListener(loginFormEl, 'keydown', function(e){
            if(isKeyDownEnter(e)){
                loginCAS();
            }
        });
    }
}

loginCAS  = function(){
    var username = document.getElementById(constants.loginFormUser).value;
    var password = document.getElementById(constants.loginFormPassword).value;
    var casServerFrameDoc = document.getElementById(constants.casServerFrame).contentWindow.document;
    var cas_username = casServerFrameDoc.getElementById(constants.loginFormUser);
    var cas_password = casServerFrameDoc.getElementById(constants.loginFormPassword);
    cas_username.value = username;
    cas_password.value = password;
    var cas_loginForm = casServerFrameDoc.forms[constants.serverLoginFormId];
    if(cas_loginForm){
        cas_loginForm.submit();
    }else{
        alert('cas_loginForm is undefined');
    }
}

function resetWindow(){
    if (top.location !== self.location) {
        top.location = self.location;
    }
}

function loadedIFrame(e){
    var frameURL = constants.casServerURL;
//    frameURL += '?service=';
//    var redirectURL = getParam(constants.redirectURLName);
//    redirectURL = redirectURL ? redirectURL : constants.loginURL;
//    frameURL += redirectURL;
    var cas_serverFrame = document.getElementById(constants.casServerFrame);
    if(!cas_serverFrame)return;
    cas_serverFrame.src = frameURL;
    if(cas_serverFrame.attachEvent){
        cas_serverFrame.attachEvent('onload', function(){
            initEvents(e);
        });
    }else{
        cas_serverFrame.onload = function(){
            initEvents(e);
        }
    }
}

function registerListener(el, eventName, handle) {
    if (window.attachEvent) {
        el.attachEvent('on' + eventName, handle);
    } else if (window.addEventListener) {
        el.addEventListener(eventName, handle, false);
    }
}

function isKeyDownEnter(e) {
    e = e ? e : window.event;
    var _isie = (window.attachEvent) ? true : false;
    var _key = '';
    if (_isie) {
        _key = e.keyCode;
    } else {
        _key = e.which;
    }
    if (_key == 13)
        return true;
    else
        return false;
}

function getParam(name) {
    var queryString = window.location.search;
    var param = queryString.substr(1, queryString.length - 1).split("&");
    for (var i = 0; i < param.length; i++) {
        var keyValue = param[i].split("=");
        if (keyValue[0] == name) return keyValue[1];
    }
    return null;
}


四、为了实现验证成功后跳转到验证之前的URL资源,cas-server的验证成功后的redirect修改是必不可少了。

①修改WEB-INF/login-webflow.xml文件

搜索<end-state id="viewGenericLoginSuccess" view="casLoginGenericSuccessView" />
修改为
<end-state id="viewGenericLoginSuccess" view="redirectView" />


②添加redirectView
打开WEB-INF\classes\default_views.properties
增加如下字段:
### 跳转回调页面
redirectView.(class)=org.springframework.web.servlet.view.JstlView
redirectView.url=/WEB-INF/view/jsp/default/ui/redirect.jsp


③新建/WEB-INF/view/jsp/default/ui/redirect.jsp
代码参考如下:

<%-- 
    Document   : redirect
    Created on : 2011-1-11, 17:42:09
    Author     : Kenny
--%>

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

        <script type="text/javascript">
            var defLoginURL = 'https://localhost:8443/sso_demo/login.jsp';
            function redirect(){
                var _window = top.location != self.location? window.parent.window : window;
                var serviceURL = getParam('service', _window);
                if(!serviceURL){
                    serviceURL = defLoginURL;
                }
                _window.location.href = 'login?service='+serviceURL;
            }
            function getParam(name, _window) {
                _window ? _window : window;
                var queryString = _window.location.search;
                var param = queryString.substr(1, queryString.length - 1).split("&");
                for (var i = 0; i < param.length; i++) {
                    var keyValue = param[i].split("=");
                    if (keyValue[0] == name) return keyValue[1];
                }
                return null;
            }
        </script>
    </head>
    <body onload="redirect()">

    </body>
</html>


附上本帖子提及过的文件和我测试的客户端项目,详细见附件。

题外:
本次开发是在netbeans上开发的,所以用eclipse的朋友导入demo项目可能要麻烦一些。不知道为何我的MyEclipse上的m4eclipse怎么没了,所以用不了maven插件,懒得弄就直接用netbeans了,因为NetBeans 6.9版本对maven支持很好。

参考:
http://fallenlord.blogbus.com/logs/36905130.html
http://fallenlord.blogbus.com/logs/36907990.html
http://fallenlord.blogbus.com/logs/36907044.html
5
0
分享到:
评论
14 楼 周聪龙 2015-03-05  
按照你的配置,点击登录按钮没反应呀,所有的Ip都改为自己的了呀
13 楼 周聪龙 2015-03-05  
denglihong 写道
shangliuyan 写道
我点击提交,页面没有反应...这是怎么回事儿阿

我的也是吗,你是怎么解决的呢?

12 楼 wangxing870555 2013-07-05  
 var cas_loginForm = casServerFrameDoc.forms[constants.serverLoginFormId];  
    if(cas_loginForm){  
        cas_loginForm.submit();  
    }else{  
        alert('cas_loginForm is undefined');  
    }  

LZ,这段JS无法获取到CAS页面上的表单对象,无法提交。。
11 楼 qing407066271 2012-05-25  
这段代码有用吗

 <input type="hidden" name="submit" value="true" /> 
10 楼 amd157 2012-05-22  
加在引用clientLogin.js的页面login.jsp中.呵呵。瞎碰到的对了...
9 楼 amd157 2012-05-22  
看到cas-server源码 top.jsp里面有一句<%@ page session="true" %>

有时候点击按钮不反映的话,应该是所谓的跨域session丢失貌似这样就好使了。。。
8 楼 denglihong 2012-05-09  
shangliuyan 写道
我点击提交,页面没有反应...这是怎么回事儿阿

我的也是吗,你是怎么解决的呢?
7 楼 dlh_fairyta 2012-05-08  
请问按你这种方式需要修改原代码吗?
6 楼 dlh_fairyta 2012-05-08  
给个联系方式,请教你一下
5 楼 tiansskk 2012-05-04  
你的客户端和服务器在同一个Tomcat下,但是实际应用中客户端和服务器分别部署在不同的tomcat下,这个时候用Iframe 会碰到跨域访问的问题,你如何解决此问题?
4 楼 denger 2012-01-03  
非常讨厌 iframe 之类的... 呵呵 。
3 楼 lhx222 2011-11-01  
支持一下:
如果在html中有任何一个元素的name属性为submit时,则用javascript操作form元素时是无法执行submit(),会提示'不存在的方法。

这个以前用jquery来提交的时候看英文API(api上有说)处理过一次,但没放心上,这次遇上了怎么也没想起来。幸好看到楼主的红色标记的文字,呵呵,太感谢了!
2 楼 shangliuyan 2011-07-25  
我点击提交,页面没有反应...这是怎么回事儿阿
1 楼 xgx1985xgx 2011-07-06  
顶一个  这种方式测试成功 
但是有个问题  就是caserver里面的casLoginView.jsp中有个input的名称是submit,导致js方法form.submit报错 把名字改掉即可

相关推荐

Global site tag (gtag.js) - Google Analytics