改造CAS单点登录 --- 自定义登陆页面(服务端)

服务端通过自定义spring-flow流程实现登录和退出功能。

一、修改cas-server-core

1.在类AuthenticationViaFormAction中重载submit和validatorCode方法

 public final String submit(final RequestContext context, final MessageContext messageContext) throws Exception {
        // Validate login ticket
      /*  final String authoritativeLoginTicket = WebUtils.getLoginTicketFromFlowScope(context);
        final String providedLoginTicket = WebUtils.getLoginTicketFromRequest(context);
        if (!authoritativeLoginTicket.equals(providedLoginTicket)) {
            this.logger.warn("Invalid login ticket " + providedLoginTicket);
            final String code = "INVALID_TICKET";
            messageContext.addMessage(new MessageBuilder().error().code(code).arg(providedLoginTicket).defaultText(code).build());
            return "error";
        }*/
        final String ticketGrantingTicketId = WebUtils.getTicketGrantingTicketId(context);
        final Service service = WebUtils.getService(context);
        final HttpServletRequest request = WebUtils.getHttpServletRequest(context);
        String username = request.getParameter("username");
        if(!StringUtils.hasText(username)){
        	 context.getFlowScope().put("errorCode", "required.username");
        	 return "error";
        }
        String password = request.getParameter("password");
        if(!StringUtils.hasText(password)){
	       	 context.getFlowScope().put("errorCode", "required.password");
	       	 return "error";
       }
        UsernamePasswordCredentials credentials = new UsernamePasswordCredentials();
        credentials.setUsername(username);
        credentials.setPassword(password);
        
        if (StringUtils.hasText(context.getRequestParameters().get("renew")) && ticketGrantingTicketId != null && service != null) {
            try {
                final String serviceTicketId = this.centralAuthenticationService.grantServiceTicket(ticketGrantingTicketId, service, credentials);
                WebUtils.putServiceTicketInRequestScope(context, serviceTicketId);
                putWarnCookieIfRequestParameterPresent(context);
                return "warn";
            } catch (final TicketException e) {
                if (e.getCause() != null && AuthenticationException.class.isAssignableFrom(e.getCause().getClass())) {
                    //populateErrorsInstance(e, messageContext);
                    context.getFlowScope().put("errorCode", e.getCode());
                    return "error";
                }
                this.centralAuthenticationService.destroyTicketGrantingTicket(ticketGrantingTicketId);
                if (logger.isDebugEnabled()) {
                    logger.debug("Attempted to generate a ServiceTicket using renew=true with different credentials", e);
                }
            }
        }
        try {
            WebUtils.putTicketGrantingTicketInRequestScope(context, this.centralAuthenticationService.createTicketGrantingTicket(credentials));
            putWarnCookieIfRequestParameterPresent(context);
            return "success";
        } catch (final TicketException e) {
            //populateErrorsInstance(e, messageContext);
            context.getFlowScope().put("errorCode", e.getCode());
            return "error";
        }
    }


 

public final String validatorCode(final RequestContext context, final MessageContext messageContext) throws Exception {  
        
    	final HttpServletRequest request = WebUtils.getHttpServletRequest(context);
    	
        HttpSession session = request.getSession();
        String authcode = (String)session.getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
		session.removeAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
        
		String submitAuthcode =request.getParameter("authcode");
        if(!StringUtils.hasText(submitAuthcode)){
        	context.getFlowScope().put("errorCode", NullAuthcodeAuthenticationException.CODE);
        	return "error";  
        }
        if(submitAuthcode.equals(authcode)){  
        	context.getFlowScope().remove("errorCode");
        	return "success";
        }
        context.getFlowScope().put("errorCode", BadAuthcodeAuthenticationException.CODE);
        return "error";  
    } 

 

2.自定义登录RemoteLoginAction

/**
 * 远程登陆票据提供Action.
 * 根据InitialFlowSetupAction修改.
 * 由于InitialFlowSetupAction为final类,因此只能将代码复制过来再进行修改.
 */

package org.jasig.cas.web.flow;

import java.util.List;

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

import org.hibernate.validator.constraints.NotEmpty;
import org.jasig.cas.authentication.principal.Service;
import org.jasig.cas.web.support.ArgumentExtractor;
import org.jasig.cas.web.support.CookieRetrievingCookieGenerator;
import org.jasig.cas.web.support.WebUtils;
import org.springframework.util.StringUtils;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;

public class RemoteLoginAction extends AbstractAction {

    /** CookieGenerator for the Warnings. */
    @NotNull
    private CookieRetrievingCookieGenerator warnCookieGenerator;

    /** CookieGenerator for the TicketGrantingTickets. */
    @NotNull
    private CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator;

    /** Extractors for finding the service. */
    @NotEmpty
    private List<ArgumentExtractor> argumentExtractors;

    /** Boolean to note whether we've set the values on the generators or not. */
    private boolean pathPopulated = false;

    protected Event doExecute(final RequestContext context) throws Exception {
        final HttpServletRequest request = WebUtils.getHttpServletRequest(context);
        if (!this.pathPopulated) {
            final String contextPath = context.getExternalContext().getContextPath();
            final String cookiePath = StringUtils.hasText(contextPath) ? contextPath : "/";
            logger.info("Setting path for cookies to: "
                + cookiePath);
            this.warnCookieGenerator.setCookiePath(cookiePath);
            this.ticketGrantingTicketCookieGenerator.setCookiePath(cookiePath);
            this.pathPopulated = true;
        }

        context.getFlowScope().put(
            "ticketGrantingTicketId", this.ticketGrantingTicketCookieGenerator.retrieveCookieValue(request));
        context.getFlowScope().put(
            "warnCookieValue",
            Boolean.valueOf(this.warnCookieGenerator.retrieveCookieValue(request)));

        final Service service = WebUtils.getService(this.argumentExtractors,
            context);

        if (service != null && logger.isDebugEnabled()) {
            logger.debug("Placing service in FlowScope: " + service.getId());
        }
        context.getFlowScope().put("contextPath", request.getContextPath());
        context.getFlowScope().put("service", service);
        // 客户端必须传递loginUrl参数过来,否则无法确定登陆目标页面
        if (StringUtils.hasText(request.getParameter("loginUrl"))) {
            context.getFlowScope().put("remoteLoginUrl", request.getParameter("loginUrl"));
        } else {
            request.setAttribute("remoteLoginMessage", "loginUrl parameter must be supported.");
            return error();
        }
        
        // 若参数包含submit则进行提交,否则进行验证
        if (StringUtils.hasText(request.getParameter("submit"))) {
            return result("submit");
        } else {
            return result("checkTicketGrantingTicket");
        }
    }

    public void setTicketGrantingTicketCookieGenerator(
        final CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator) {
        this.ticketGrantingTicketCookieGenerator = ticketGrantingTicketCookieGenerator;
    }

    public void setWarnCookieGenerator(final CookieRetrievingCookieGenerator warnCookieGenerator) {
        this.warnCookieGenerator = warnCookieGenerator;
    }

    public void setArgumentExtractors(
        final List<ArgumentExtractor> argumentExtractors) {
        this.argumentExtractors = argumentExtractors;
    }
}


 

3.自定义退出RemoteLogoutAction

package org.jasig.cas.web.flow;

import java.util.List;

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

import org.hibernate.validator.constraints.NotEmpty;
import org.jasig.cas.CentralAuthenticationService;
import org.jasig.cas.authentication.principal.Service;
import org.jasig.cas.web.support.ArgumentExtractor;
import org.jasig.cas.web.support.CookieRetrievingCookieGenerator;
import org.jasig.cas.web.support.WebUtils;
import org.springframework.util.StringUtils;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;

public class RemoteLogoutAction extends AbstractAction {
	@NotNull
	private CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator;
	@NotNull
	private CookieRetrievingCookieGenerator warnCookieGenerator;
	@NotNull
	private CentralAuthenticationService centralAuthenticationService;
	@NotEmpty
	private List<ArgumentExtractor> argumentExtractors;
	private boolean pathPopulated = false;

	@Override
	protected Event doExecute(final RequestContext context) throws Exception {
		final HttpServletRequest request = WebUtils
				.getHttpServletRequest(context);
		final HttpServletResponse response = WebUtils
				.getHttpServletResponse(context);

		if (!this.pathPopulated) {
			final String contextPath = context.getExternalContext()
					.getContextPath();
			final String cookiePath = StringUtils.hasText(contextPath) ? contextPath
					: "/";
			logger.info("Setting path for cookies to: " + cookiePath);
			this.warnCookieGenerator.setCookiePath(cookiePath);
			this.ticketGrantingTicketCookieGenerator.setCookiePath(cookiePath);
			this.pathPopulated = true;
		}
		context.getFlowScope().put(
				"ticketGrantingTicketId",
				this.ticketGrantingTicketCookieGenerator
						.retrieveCookieValue(request));
		context.getFlowScope().put(
				"warnCookieValue",
				Boolean.valueOf(this.warnCookieGenerator
						.retrieveCookieValue(request)));
		final Service service = WebUtils.getService(this.argumentExtractors,
				context);
		if (service != null && logger.isDebugEnabled()) {
			logger.debug("Placing service in FlowScope: " + service.getId());
		}
		context.getFlowScope().put("service", service);
		context.getFlowScope().put("remoteLoginUrl",request.getParameter("service"));
		final String ticketGrantingTicketId = this.ticketGrantingTicketCookieGenerator.retrieveCookieValue(request);

		if (ticketGrantingTicketId != null) {
			this.centralAuthenticationService
					.destroyTicketGrantingTicket(ticketGrantingTicketId);

			this.ticketGrantingTicketCookieGenerator.removeCookie(response);
			this.warnCookieGenerator.removeCookie(response);
		}

		return result("success");
	}

	public void setTicketGrantingTicketCookieGenerator(
			final CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator) {
		this.ticketGrantingTicketCookieGenerator = ticketGrantingTicketCookieGenerator;
	}

	public void setWarnCookieGenerator(
			final CookieRetrievingCookieGenerator warnCookieGenerator) {
		this.warnCookieGenerator = warnCookieGenerator;
	}

	public void setArgumentExtractors(
			final List<ArgumentExtractor> argumentExtractors) {
		this.argumentExtractors = argumentExtractors;
	}

	public void setCentralAuthenticationService(
			final CentralAuthenticationService centralAuthenticationService) {
		this.centralAuthenticationService = centralAuthenticationService;
	}
}


 

二、修改cas-server-webapp

1.web.xml增加登录、退出请求mapping映射的处理

  <servlet-mapping>
     <servlet-name>cas</servlet-name>
     <url-pattern>/remoteLogin</url-pattern>
 </servlet-mapping>
 
 <servlet-mapping>
     <servlet-name>cas</servlet-name>
     <url-pattern>/remoteLogout</url-pattern>
 </servlet-mapping>


2.在cas-servlet.xml增加请求流程

<!-- 增加远程控制者,允许以/remote请求启动remote控制流程 -->
 
 <bean id="handlerMappingB" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
     <property name="mappings">
         <props>
             <prop key="/remoteLogin">remoteLoginController</prop>
             <prop key="/remoteLogout">remoteLogoutController</prop>
         </props>
     </property>
     <property name="interceptors">
         <list>
             <ref bean="localeChangeInterceptor" />
         </list>
     </property>
 </bean>


 

  <!--    远程配置   -->
  
  <bean id="remoteLoginController" class="org.springframework.webflow.mvc.servlet.FlowController">
  <property name="flowExecutor" ref="remoteLoginFlowExecutor" />
  <property name="flowUrlHandler" ref="flowUrlHandler"/>
 </bean>
        
 <webflow:flow-executor id="remoteLoginFlowExecutor" flow-registry="remoteLoginFlowRegistry">
     <webflow:flow-execution-attributes>
         <webflow:always-redirect-on-pause value="false"/>
     </webflow:flow-execution-attributes>
 </webflow:flow-executor>
 
 <webflow:flow-registry id="remoteLoginFlowRegistry" flow-builder-services="builder">
     <webflow:flow-location path="/WEB-INF/remoteLogin-webflow.xml" id="remoteLogin"/>
 </webflow:flow-registry>
 
 <webflow:flow-builder-services id="flowBuilderServices" view-factory-creator="viewFactoryCreator"/>
 
 <bean id="remoteLoginAction" class="org.jasig.cas.web.flow.RemoteLoginAction"
     p:argumentExtractors-ref="argumentExtractors"
  p:warnCookieGenerator-ref="warnCookieGenerator"
  p:ticketGrantingTicketCookieGenerator-ref="ticketGrantingTicketCookieGenerator" />
  
 <bean id="remoteLogoutController" class="org.springframework.webflow.mvc.servlet.FlowController">
  <property name="flowExecutor" ref="remoteLogoutFlowExecutor" />
  <property name="flowUrlHandler" ref="flowUrlHandler"/>
 </bean>
 
 <webflow:flow-executor id="remoteLogoutFlowExecutor" flow-registry="remoteLogoutFlowRegistry">
     <webflow:flow-execution-attributes>
         <webflow:always-redirect-on-pause value="false"/>
     </webflow:flow-execution-attributes>
 </webflow:flow-executor>
 <webflow:flow-registry id="remoteLogoutFlowRegistry" flow-builder-services="builder">
     <webflow:flow-location path="/WEB-INF/remoteLogout-webflow.xml" id="remoteLogout"/>
 </webflow:flow-registry>
 
 <bean id="remoteLogoutAction" class="org.jasig.cas.web.flow.RemoteLogoutAction"
  p:argumentExtractors-ref="argumentExtractors"
  p:warnCookieGenerator-ref="warnCookieGenerator"
  p:ticketGrantingTicketCookieGenerator-ref="ticketGrantingTicketCookieGenerator"
     p:centralAuthenticationService-ref="centralAuthenticationService" />
  
 <!--    远程配置   -->


3.增加远程登录流程控制remoteLogin-webflow.xml

<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/webflow
                          http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"
      start-state="remoteLogin">
 <!-- 远程登陆主要Action -->
    <action-state id="remoteLogin">
        <evaluate expression="remoteLoginAction" />
        <transition on="error" to="remoteCallbackView" />
        <transition on="submit" to="authcodeValidate" />
        <transition on="checkTicketGrantingTicket" to="ticketGrantingTicketExistsCheck" />
    </action-state>
    
    <!-- 远程回调页面,主要以JavaScript的方式回传一些参数用 -->
    <end-state id="remoteCallbackView" view="remoteCallbackView" />
    
	<action-state id="authcodeValidate">  
        <evaluate expression="authenticationViaFormAction.validatorCode(flowRequestContext, messageContext)" />  
        <transition on="error" to="remoteCallbackView" />  
        <transition on="success" to="bindAndValidate" />  
    </action-state>  
	
    <action-state id="bindAndValidate">
        <evaluate expression="authenticationViaFormAction.doBind(flowRequestContext, flowScope.credentials)" />
        <transition on="success" to="submit" />
        <transition on="error" to="remoteCallbackView" />
    </action-state>
    
    <decision-state id="ticketGrantingTicketExistsCheck">
  <if test="flowScope.ticketGrantingTicketId != null" then="hasServiceCheck" else="gatewayRequestCheck" />
 </decision-state>
 
 <decision-state id="hasServiceCheck">
        <if test="flowScope.service != null" then="generateServiceTicket" else="remoteCallbackView" />
    </decision-state>
 <decision-state id="gatewayRequestCheck">
  <if test="externalContext.requestParameterMap['gateway'] neq '' && externalContext.requestParameterMap['gateway'] neq null && flowScope.service neq null" then="redirect" else="remoteCallbackView" />
 </decision-state>
 
 <action-state id="generateServiceTicket">
        <evaluate expression="generateServiceTicketAction" />
        <transition on="success" to ="warn" />
        <transition on="error" to="remoteCallbackView" />
        <transition on="gateway" to="redirect" />
    </action-state>
 
 <decision-state id="warn">
  <if test="flowScope.warnCookieValue" then="showWarningView" else="redirect" />
 </decision-state>
 
 <action-state id="submit">
        <evaluate expression="authenticationViaFormAction.submit(flowRequestContext,messageContext)" />
  <transition on="warn" to="warn" />
  <transition on="success" to="sendTicketGrantingTicket" />
  <transition on="error" to="remoteCallbackView" />
 </action-state> 
 
 <action-state id="sendTicketGrantingTicket">
        <evaluate expression="sendTicketGrantingTicketAction" />
  <transition to="serviceCheck" />
 </action-state>
 
 <decision-state id="serviceCheck">
  <if test="flowScope.service neq null" then="generateServiceTicket" else="remoteCallbackView" />
 </decision-state>
    
 <end-state id="showWarningView" view="casLoginConfirmView" />
 
 <action-state id="redirect">
        <evaluate expression="flowScope.service.getResponse(requestScope.serviceTicketId)" result-type="org.jasig.cas.authentication.principal.Response" result="requestScope.response" />
        <transition to="postRedirectDecision" />
    </action-state>
 
    <decision-state id="postRedirectDecision">
        <if test="requestScope.response.responseType.name() eq 'POST'" then="postView" else="redirectView" />
    </decision-state>
    <end-state id="postView" view="postResponseView">
        <on-entry>
            <set name="requestScope.parameters" value="requestScope.response.attributes" />
            <set name="requestScope.originalUrl" value="flowScope.service.id" />
        </on-entry>
    </end-state>
    <end-state id="redirectView" view="externalRedirect:${requestScope.response.url}" />
    
 <end-state id="viewServiceErrorView" view="viewServiceErrorView" />
 <end-state id="viewServiceSsoErrorView" view="viewServiceSsoErrorView" />
    
 <global-transitions>
        <transition to="viewServiceErrorView" on-exception="org.springframework.webflow.execution.repository.NoSuchFlowExecutionException" />
        <transition to="viewServiceSsoErrorView" on-exception="org.jasig.cas.services.UnauthorizedSsoServiceException" />
        <transition to="viewServiceErrorView" on-exception="org.jasig.cas.services.UnauthorizedServiceException" />
    </global-transitions>
</flow>


4.增加远程退出流程控制remoteLogout-webflow.xml

<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/webflow
                          http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"
      start-state="remoteLogout">
 
 <!-- 远程登陆主要Action -->
    <action-state id="remoteLogout">
        <evaluate expression="remoteLogoutAction" />
        <transition on="success" to="remoteCallbackView" />
    </action-state>
    <end-state id="remoteCallbackView" view="remoteCallbackView" />
 <global-transitions>
        <transition to="viewServiceErrorView" on-exception="org.springframework.webflow.execution.repository.NoSuchFlowExecutionException" />
        <transition to="viewServiceSsoErrorView" on-exception="org.jasig.cas.services.UnauthorizedSsoServiceException" />
        <transition to="viewServiceErrorView" on-exception="org.jasig.cas.services.UnauthorizedServiceException" />
    </global-transitions>
</flow>


5.配置remoteCallbackView显示节点,修改src/default_views.properties文件,增加remoteCallbackView配置

### 配置远程回调页面
remoteCallbackView.(class)=org.springframework.web.servlet.view.JstlView
remoteCallbackView.url=/WEB-INF/view/jsp/default/ui/remoteCallbackView.jsp

 

6.创建/WEB-INF/view/jsp/default/ui/remoteCallbackView.jsp文件

<%@ page language="java" contentType="text/html; charset=utf-8"
	pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript">
     var remoteLoginUrl = "${remoteLoginUrl}";
     if(remoteLoginUrl == ""){
    	 window.location.href ="${pageContext.request.contextPath}";
     }else{
    	 var remoteUrl = remoteLoginUrl+"?validated=true";
         var service = null;
         <c:if test="${service != null && service != ''}">
           service = "&service=" + encodeURIComponent("${service}");
         </c:if>
         // 构造错误消息
         var errorMessageCode = "${errorCode}";
          var errorCode = null;
          if(errorMessageCode.indexOf("required.authcode")!=-1){
        	  errorCode =501;
          }else if(errorMessageCode.indexOf("error.authentication.authcode.bad")!=-1){
        	  errorCode =502;
          }else if(errorMessageCode.indexOf("required.username")!=-1 || errorMessageCode.indexOf("required.password")!=-1 ){
        	  errorCode =503;
          }else if(errorMessageCode.indexOf("error.authentication.credentials.bad")!=-1){
        	  errorCode = 504;
          }
          if(errorCode){
        	  remoteUrl +="&errorCode="+errorCode;
          }
         // 跳转回去
         window.location.href = remoteUrl + service;
     }
</script>
</head>
<body>${remoteLoginMessage}
</body>
</html>


服务器端修改完成。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值