CAS利用Iframe框架跨域Ajax登录
 因最近经常有时候被一些朋友问到关于 CAS 跨全域下的 Ajax 登录方式实现,正好之前也分析Sina微博的SSO实现,文中也说了 SINA SSO 实际上(或机制)直接使用了 CAS 这个开源项目。于是本文中要说的CAS AJAX登录方式便参考了 SINA AJAX登录实现。 关于具体方案,CAS官方上好象没有提供相关说明,倒是有一文说到 Without the Login Screen(详情参见 CAS 之自定义登录页实践),其具体实现方式甚是麻烦,又是改源码,又是通过JS跳转,又是一堆配置。 当然,虽然如此,但该文中所提到的获取 login tikcet 的方式还是值的参考的,因为无论什么方式登录,前提是必须获取到该ticket才允许登录验证。 
    虽然这里所说的主要是针对 CAS,其实具体的实现方式中有些还是值得参考的,如跨域设置 cookie, jsonp + iframe 跨域异步请求、P3P 关于 spring webflow 等其它相关的一些信息。 

思路 
    关于具体的实现思路基本上都是参考了 SINA,所以详细信息可以在 分析Sina微博的SSO实现 看到 自己去 firebug 一下 sina micro-blog 



实践 
    Environment 
        cas-server-3.4.2.1      www.passport:8080/cas/ 
        cas-client-3.1.10        www.portal:8080/login 
   以上域名是方便测试跨域,故修改本机 hosts 

    Step 1:  在首次进入登录时(portal域中/login),通过 JSONP passport 域中获取 login ticket 
    登录表单: 
   
Html代码  
1. <form action="www.passport:8080/cas/login" method="post" onsubmit="retur
n loginValidate();" target="ssoLoginFrame"> 
2.     <ul> 
3.         <span class="red" style="height:12px;" id="J_ErrorMsg"></span> 
4.  
5.         <li> 
6.             <em>用户名:</em> 
7.             <input name="username" id="J_Username" type="text" autocomplete="off" class="line" style="width: 180px" /> 
8.         </li> 
9.         <li> 
10.             <em> 码:</em> 
11.             <input name="password" type="password"  id="J_Password" class="line" style="width: 180px" /> 
12.         </li> 
13.  
14.         <li class="mai"> 
15.             <em> </em> 
16.             <input type="checkbox" name="rememberMe" id="rememberMe" value="true"/> 
17.              自动登录 
18.             <a href="/retrieve">忘记密码?</a> 
19.         </li> 
20.         <li> 
21.             <em> </em> 
22.             <input type="hidden" name="isajax" value="true" /> 
23.             <input type="hidden" name="isframe" value="true" /> 
24.             <input type="hidden" name="lt" value="" id="J_LoginTicket"> 
25.             <input type="hidden" name="_eventId" value="submit" /> 
26.             <input name="" type="submit" value="登录" class="loginbanner" /> 
27.         </li> 
28.     </ul> 
29. </form> 
   
Js代码  
1. $(document).ready(function(){   
2.         flushLoginTicket();  // 进入登录页,则获取login ticket,该函数在下面定义。 
3.     }); 
    关于 cas-server 如何返回 lt ,在 Without the Login Screen 文章中有提到。 


    Step 2:  输入用户名密码,提交验证。将表单信息将会被POST提交至 动态的iframe中,定义该登录页面中登录后的处理逻辑。 
Js代码  
1. // 登录验证函数 onsubmit 事件触发 
2. var loginValidate = function(){ 
3.     var msg; 
4.     if ($.trim($('#J_Username').val()).length == 0 ){ 
5.         msg = "用户名不能为空。"
6.     } else if ($.trim($('#J_Password').val()).length == 0 ){ 
7.         msg = "密码不能为空。"
8.     } 
9.     if (msg && msg.length > 0) { 
10.         $('#J_ErrorMsg').fadeOut().text(msg).fadeIn(); 
11.         return false
12.         // Can't request the login ticket. 
13.     } else if ($('#J_LoginTicket').val().length == 0){ 
14.         $('#J_ErrorMsg').text('服务器正忙,请稍后再试..'); 
15.         return false
16.     } else { 
17.         // 验证成功后,动态创建用于提交登录的 iframe 
18.         $('body').append($('<iframe/>').attr({ 
19.             style: "display:none;width:0;height:0",   
20.             id: "ssoLoginFrame"
21.             name: "ssoLoginFrame"
22.             src: "javascript:false;" 
23.         })); 
24.         return true
25.     } 
26.
27.  
28. // 登录处理回调函数,将由 iframe 中的页同自动回调 
29. var feedBackUrlCallBack = function (result) { 
30.     customLoginCallBack(result); 
31.     deleteIFrame('#ssoLoginFrame');// 删除用完的iframe,但是一定不要在回调前删除,Firefox可能有问题的 
32. }; 
33.  
34. // 自定义登录回调逻辑 
35. var customLoginCallBack = function(result){ 
36.     // 登录失败,显示错误信息 
37.     if (result.login == 'fails'){ 
38.         $('#J_ErrorMsg').fadeOut().text(result.msg).fadeIn(); 
39.         // 重新刷新 login ticket 
iframe参数传递40.         flushLoginTicket(); 
41.     } 
42.     // do& 
43.
44.  
45. var deleteIFrame = function (iframeName) { 
46.     var iframe = $(iframeName);   
47.     if (iframe) { // 删除用完的iframe,避免页面刷新或前进、后退时,重复执行该iframe的请求 
48.         ve() 
49.     } 
50. }; 
51.  
52. // 由于一个 login ticket 只允许使用一次当每次登录需要调用该函数刷新 lt 
53. var flushLoginTicket = function(){ 
54.     var _services = 'service=' + encodeURIComponent('www.portal:8080/uc/'); 
55.     $.getScript('www.passport:8080/cas/login?'+_services+'&get-lt=true&n='   
56.             + new Date().getTime(),   
57.     function(){ 
58.         // 将返回的 _loginTicket 变量设置到  input name="lt" value中。 

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。