CAS自定义登陆页面

参考文档:

http://www.360doc.com/content/14/0719/16/834950_395508432.shtml

http://blog.sina.com.cn/s/blog_7d491cfb0101fa5o.html

前两篇都是参考以下两篇文章:

http://denger.iteye.com/blog/809170

http://denger.iteye.com/blog/1119233

一.前言

cas版本:cas-server-3.5.2-release

以下我们所做的服务器端代码的修改都是在cas-server-webapp中

二.问题分析

获取cas登录时所需要的参数,其中最主要是lt和execution这两个动态生成的参数,因此,实现cas自定义登录的步骤:

第一步:获取lt/execution的值

第二步:自定义界面进行登录

第三步:给出相应的提示信息,并跳转到指定界面

三.实现过程

1、修改服务器端代码,包括创建ProvideLoginTicketAction.class,用于处理获得it/execution的值,并修改login-webflow等配置文件等操作

(1)创建ProvideLoginTicketAction.java

package org.jasig.cas.util;

import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.cas.web.support.WebUtils;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;

public class ProvideLoginTicketAction extends AbstractAction {
    
    private static final String PREFIX = "LT";
    private final Log logger = LogFactory.getLog(getClass());
    
    @NotNull
    private UniqueTicketIdGenerator ticketIdGenerator;
    
    public final String generate(final RequestContext context){
        final String loginTicket = this.ticketIdGenerator.getNewTicketId(PREFIX);
        WebUtils.putLoginTicket(context, loginTicket);
        return "generated";
    }
    
    public void setTicketIdGenerator(final UniqueTicketIdGenerator generator){
        this.ticketIdGenerator = generator;
    }
    
    @Override
    protected Event doExecute(RequestContext context) throws Exception {
        
        final HttpServletRequest request = WebUtils.getHttpServletRequest(context);
        if(request.getParameter("get-lt") != null && request.getParameter("get-lt").equalsIgnoreCase("true")){
            final String loginTicket = this.ticketIdGenerator.getNewTicketId(PREFIX);
            WebUtils.putLoginTicket(context, loginTicket);
            return result("loginTicketRequested");
        }
        return result("continue");
    }

}

(2)创建AjaxLoginServiceTicketAction.java

package org.jasig.cas.util;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.jasig.cas.authentication.principal.Service;
import org.jasig.cas.web.support.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;

public class AjaxLoginServiceTicketAction extends AbstractAction {
    
    private final Logger log = LoggerFactory.getLogger(getClass()); 
    protected static final String J_CALLBACK = "feedBackUrlCallBack";
    @Override
    protected Event doExecute(final RequestContext context) {
        HttpServletRequest request = WebUtils.getHttpServletRequest(context);
        Event event = context.getCurrentEvent();
        boolean isAjax = BooleanUtils.toBoolean(request.getParameter("isajax"));
       
        if (!isAjax){  // 非 ajax/iframe 方式登录,返回当前 event.
         return event;
        }
     boolean isLoginSuccess;
     // Login Successful.
     if ("success".equals(event.getId())){ //是否登录成功
      final Service service = WebUtils.getService(context);
            final String serviceTicket = WebUtils.getServiceTicketFromRequestScope(context);
            if (service != null){  //设置登录成功之后 跳转的地址
             request.setAttribute("service", service.getId());
            }
            request.setAttribute("ticket", serviceTicket);
            isLoginSuccess = true;
     } else { // Login Fails..
      isLoginSuccess = false;
     }
        boolean isFrame = BooleanUtils.toBoolean(request.getParameter("isframe"));
        String callback = request.getParameter("callback");
        String clientUrl = request.getParameter("login-at");
        if(StringUtils.isEmpty(callback)){ // 如果未转入 callback 参数,则采用默认 callback 函数名
         callback = J_CALLBACK;
        }
        if(isFrame){ // 如果采用了 iframe ,则 concat 其 parent 。
         callback = "parent.".concat(callback);
        }
        request.setAttribute("isFrame", isFrame);
        request.setAttribute("callback", callback);
        request.setAttribute("clientUrl", clientUrl);
        request.setAttribute("isLogin", isLoginSuccess);
        return new Event(this, "ajaxView"); // 转入 ajaxLogin.jsp 页面
    }
}

(3)在cas-server.xml中声明ProvideLoginTicketAction和AjaxLoginServiceTicketAction

<bean id="provideLoginTicketAction" class="org.jasig.cas.util.ProvideLoginTicketAction"
      p:ticketIdGenerator-ref = "loginTicketUniqueIdGenerator"></bean>
  <bean id="ajaxLoginServiceTicketAction" class="org.jasig.cas.util.AjaxLoginServiceTicketAction"/>
  

(4)修改login-webflow.xml

<action-state id="generateServiceTicket">
        <evaluate expression="generateServiceTicketAction" />
        <!-- <transition on="success" to ="warn" /> 
        <transition on="error" to="generateLoginTicket" />-->
        <transition on="success" to ="loginResponse" /> 
        <transition on="error" to="loginResponse" />
        <transition on="gateway" to="gatewayServicesManagementCheck" />
    </action-state>
<action-state id="realSubmit">
  <evaluate
   expression="authenticationViaFormAction.submit(flowRequestContext, flowScope.credentials, messageContext)" />
  <transition on="warn" to="warn" />
  <transition on="success" to="sendTicketGrantingTicket" />
  <!--将 to="viewLoginForm" 修改为 to="loginResponse" -->              
  <transition on="error" to="loginResponse" />
 </action-state>

(5)在login-webflow.xml中添加

<action-state id="loginResponse">
          <evaluate expression="ajaxLoginServiceTicketAction" />
          <!--非ajax/iframe方式登录,采取原流程处理 -->
          <transition on="success" to="warn" />
          <transition on="error" to="viewLoginForm" />
          <!-- 反之,则进入 viewAjaxLoginView 页面 -->
          <transition on="ajaxView" to="viewAjaxLoginView" />
     </action-state>

在<on-start></on-start>标签后添加

<action-state id="provideLoginTicketAction">
            <evaluate expression="provideLoginTicketAction"/>
            <transition on="loginTicketRequested" to="viewResponseLoginTicketInfo"/>
            <transition on="continue" to="ticketGrantingTicketExistsCheck" />
    </action-state>
    <view-state id="viewResponseLoginTicketInfo" view="responseLoginTicketInfoView" model="credentials">
        <binder>
            <binding property="username" />
            <binding property="password" />
        </binder>
        <on-entry>
            <set name="viewScope.commandName" value="'credentials'" />
        </on-entry>
      <transition on="submit" bind="true" validate="true" to="realSubmit">
            <evaluate expression="authenticationViaFormAction.doBind(flowRequestContext, flowScope.credentials)" />
        </transition>
     </view-state>
在最后一个<end-state>标签后添加
<end-state id="viewAjaxLoginView" view="viewAjaxLoginView" />

(6)创建ProvideLoginTicketAction执行后要跳转的responseLoginTicketInfo.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<%
response.addHeader("Content-Type", "application/x-javascript");
out.print("flightHandler({'lt':'");%>${loginTicket}<%out.print("','execution':'");%>${flowExecutionKey}<%out.print("'});");%>

(7)创建AjaxLoginServiceTicketAction执行后要跳转的ajaxLogin.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<script type="text/javascript">
   <%
    Boolean isFrame = (Boolean)request.getAttribute("isFrame");
    Boolean isLogin = (Boolean)request.getAttribute("isLogin");
    // 登录成功
    if(isLogin){
     if(isFrame){%>
      parent.location.replace('${service}?ticket=${ticket}')
     <%} else{%>
      location.replace('${service}?ticket=${ticket}')
     <%}
    }else{
   %>
   parent.location.replace('${clientUrl}?msg=error')
   <%
    }
   %>
  </script>

(8)在default_views.properties(路径:/cas-server-webapp/src/main/webapp/WEB-INF/classes/default_views.properties)

中增加responseLoginTicketInfo.jsp和ajaxLogin.jsp的配置

responseLoginTicketInfoView.(class)=org.springframework.web.servlet.view.JstlView
responseLoginTicketInfoView.url=/WEB-INF/view/jsp/default/ui/responseLoginTicketInfo.jsp

viewAjaxLoginView.(class)=org.springframework.web.servlet.view.JstlView
viewAjaxLoginView.url=/WEB-INF/view/jsp/default/ui/ajaxLogin.jsp

2、创建客户端代码

(1)客户端登录界面index.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="index.aspx.cs" Inherits="login" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>Client demo</title> 
</head>
<body>
    <div></div>
    <form action="" method="post" onsubmit="return loginValidate();" target="ssoLoginFrame" id="loginFrm">
      <div class="red" style="height:12px;color:red" id="login_msg"></div>
        <ul>
       
       <li>
        <em>用户名:</em>
        <input name="username" id="username" type="text" class="required" style=" 180px" size="25"/>
       </li>
       <li>
        <em>密码:</em>
        <input name="password" type="password"  id="password" class="required" style=" 180px" size="25"/>
       </li>
       <li class="mai">
        <em>&nbsp;</em>
        <input type="checkbox" name="rememberMe" id="rememberMe" value="true"/>
        &nbsp;自动登录
        <a href="/retrieve">忘记密码?</a>
       </li>
       <li>
        <em>&nbsp;</em>
        <input type="hidden" name="isajax" value="true" />
        <input type="hidden" name="isframe" value="true" />
        <input type="hidden" name="lt" value="" id="cas_loginTicket"/>
        <input type="hidden" name="execution" value="" id="cas_execution"/>
        <input type="hidden" name="_eventId" value="submit"/>
        <input class="btn-submit" name="submit" accesskey="l" value="登录" tabindex="4" type="submit"/>
        <input class="btn-reset" name="reset" accesskey="c" value="重置" tabindex="5" type="reset"/>
        <input type="hidden" name="login-at" value ="" id="login_at"/>
       </li>
      </ul>
     </form>  
</body>
    <script type = "text/javascript" src="js/jquery-1.9.1.min.js"></script>
    <script type = "text/javascript" src="js/index.js"></script>
    <iframe style="display:none;0;height:0" id="ssoLoginFrame" name="ssoLoginFrame" src="javascript:false;"></iframe>
</html>

(2)对应的js代码index.js

var urlData;
$(function () {
    initData();
    var login_error_msg = GetQueryString("msg");
    if (login_error_msg != null && login_error_msg.toString().length > 1) {
        $("#login_msg").text("用户名或密码错误");
    }
   // $("#login_msg").text(login_error_msg.toString());
});

var flushLoginTicket = function () {
    var _service = 'service=' + encodeURIComponent(urlData._service);
    var urlOfGetLt = urlData.serverUrl + '?' + _service + '&get-lt=true&n=' + new Date().getTime();
    $.ajax({
        type: 'get',
        async: false,
        url: urlOfGetLt,
        dataType: 'jsonp',
        jsonp: 'callback',
        jsonpCallback: 'flightHandler',
        success: function (data) {
            $("#cas_loginTicket").val(data.lt);
            $("#cas_execution").val(data.execution);
        },
        error: function () {
            console.log("ERROR")
        }
    });
}

var loginValidate = function () {
    var msg;
    if ($.trim($('#username').val()).length == 0) {
        msg = "用户名不能为空。";
    } else if ($.trim($('#password').val()).length == 0) {
        msg = "密码不能为空。";
    }
    if (msg && msg.length > 0) {
        $('#login_msg').fadeOut().text(msg).fadeIn();
        return false;
        // Can't request the login ticket.
    } else if ($('#cas_loginTicket').val().length == 0) {
        $('#login_msg').text('服务器正忙,请稍后再试..');
        return false;
    } else {
        return true;
    }
}

function GetQueryString(name) {
    var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
    var r = window.location.search.substr(1).match(reg);
    if (r != null) return unescape(r[2]); return null;
}

function initData() {
    $.get('conf/url.json').done(function (data) {
        urlData = eval('(' + data + ')');
        $("#loginFrm")[0].setAttribute("action", urlData.serverUrl);
        $("#login_at").val(urlData.clientUrl);
        flushLoginTicket();
    });
}

(3)对应的json数据url.json,用于存储服务器url和客户端url

/*
    _service:成功后跳转的路径
    serverUrl:要连接的服务器路径
    clientUrl:登录失败后跳转的路径
*/
{
    "_service": "http://localhost:54872/demoweb/home.aspx",
    "serverUrl": "https://localhost:8443/cas/login",
    "clientUrl": "http://localhost:54872/demoweb/index.aspx"
}

以上只是将整个修改过程提炼出来,并没有解释详细原因,具体细节可参考上面的几篇文章。

原文地址:https://www.cnblogs.com/hzwl-2015/p/4613185.html