`

让CAS支持客户端自定义登陆页面——实战(转)

    博客分类:
  • cas
cas 
阅读更多

上篇《让CAS支持客户端自定义登陆页面——原理篇》讲述了一些修改的理论基础,这篇讲解如何对CAS服务器端进行修改。

修改需要基于几个基本原则:

  1. 不影响原有统一登陆界面功能
  2. 客户端应尽量保持简单
  3. 尽量保证原有功能的完整性和安全性

对于第三点,必须事先说明:将登陆页面放到客户端本身就是降低了CAS安全性,这意味着作为服务向外发布的CAS服务器中的用户密码有可能由于客户端的不安全性而导致泄露,整个CAS系统成为了一个“水桶形态”,整个CAS体系的安全性将取决于所有客户端中安全性最低的一个。这也是CAS官方一直不推荐的方式。

接下来我们讲解服务器端修改的详细过程:

首先,修改/WEB-INF/web.xml,为cas增加一个/remoteLogin的映射:

 

<servlet-mapping>
    <servlet-name>cas</servlet-name>
    <url-pattern>/remoteLogin</url-pattern><!--add servlet-mapping-->
</servlet-mapping>
 

然后修改cas-servlet.xml文件,增加我们对/remoteLogin映射的处理,需要增加一个新流程:

 

<bean id="handlerMappingB" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <props>
            <prop key="/login">loginController</prop>
            <prop key="/remoteLogin">remoteController</prop><!--add  -->

        </props>
    </property>
    <property name="interceptors">
        <list>
            <ref bean="localeChangeInterceptor" />
        </list>
    </property>
</bean>
 

然后在cas-servlet.xml文件中添加我们上面所配置的remoteController的bean:

<!-- 增加远程控制者,允许以/remote请求启动remote控制流程 -->

<bean id="remoteLoginController" 
    class="org.springframework.webflow.executor.mvc.FlowController"
    p:flowExecutor-ref="remoteLoginFlowExecutor"
    p:defaultFlowId="remoteLogin-webflow">
    <property name="argumentHandler">
        <bean
class="org.springframework.webflow.executor.support.RequestParameterFlowExecutorArgumentHandler"
            p:flowExecutionKeyArgumentName="lt"
            p:defaultFlowId="remoteLogin-webflow" />
    </property>
</bean>
<flow:executor id="remoteLoginFlowExecutor" registry-ref="remoteLoginFlowRegistry">
    <flow:execution-attributes>
        <flow:alwaysRedirectOnPause value="false"/>
    </flow:execution-attributes>
</flow:executor>
<!--update-->
<flow:registry id="remoteLoginFlowRegistry">
    <flow:location path="/WEB-INF/remoteLogin-webflow.xml"/>
</flow:registry>
 

可以看到上面将请求指向了webflow配置文件/WEB-INF/remoteLogin-webflow.xml文件,我们需要创建此文件并配置其成为我们所需的流程,以下是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-1.0.xsd">
    <start-state idref="remoteLogin"/>
    <!-- 远程登陆主要Action -->
    <action-state id="remoteLogin">
        <action bean="remoteLoginAction" />
        <transition on="error" to="remoteCallbackView" />
        <transition on="submit" to="bindAndValidate" />
        <transition on="checkTicketGrantingTicket" to="ticketGrantingTicketExistsCheck" />
    </action-state>
    <!-- 远程回调页面,主要以JavaScript的方式回传一些参数用 -->
    <end-state id="remoteCallbackView" view="remoteCallbackView" />
    
    <decision-state id="ticketGrantingTicketExistsCheck">
        <if test="${flowScope.ticketGrantingTicketId != null}" then="hasServiceCheck"
else="gatewayRequestCheck" />
    </decision-state>
    <decision-state id="gatewayRequestCheck">
        <if test="${externalContext.requestParameterMap['gateway'] != ''   externalContext.requestParameterMap['gateway'] != null &amp;&amp; flowScope.service != null}" then="redirect" else="remoteCallbackView" />
    </decision-state><!--update-->    
    <decision-state id="hasServiceCheck">
        <if test="${flowScope.service != null}" then="generateServiceTicket" else="remoteCallbackView" />
    </decision-state><!--update-->

    
    <!-- 
        The "warn" action makes the determination of whether to redirect directly to the
requested
        service or display the "confirmation" page to go back to the server.
    -->
    <decision-state id="warn">
        <if test="${flowScope.warnCookieValue}" then="showWarningView" else="redirect" />
    </decision-state>
    <action-state id="bindAndValidate">
        <action bean="authenticationViaFormAction" />
        <transition on="success" to="submit" />
        <transition on="error" to="remoteCallbackView" /><!--update-->
    </action-state>
    
    <action-state id="submit">
        <action bean="authenticationViaFormAction" method="submit" />
        <transition on="warn" to="warn" />
        <transition on="success" to="sendTicketGrantingTicket" />
        <transition on="error" to="remoteCallbackView" /><!--update-->

    </action-state>
    
    <action-state id="sendTicketGrantingTicket">
        <action bean="sendTicketGrantingTicketAction" />
        <transition on="success" to="serviceCheck" />
    </action-state>
    <decision-state id="serviceCheck">
        <if test="${flowScope.service != null}" then="generateServiceTicket"
else="remoteCallbackView" /><!--update-->

    </decision-state>
    
    <action-state id="generateServiceTicket">
        <action bean="generateServiceTicketAction" />
        <transition on="success" to ="warn" />
        <transition on="error" to="remoteCallbackView" /><!--update-->

        <transition on="gateway" to="redirect" />
    </action-state>
    <!-- 
        The "showWarningView" end state is the end state for when the user has requested
privacy settings (to be "warned") to be turned on. It delegates to a 
        view defines in default_views.properties that display the "Please click here to go
to the service." message.
    -->
    <end-state id="showWarningView" view="casLoginConfirmView" />
    <!-- 
        The "redirect" end state allows CAS to properly end the workflow while still
redirecting
        the user back to the service required.
    -->
    <end-state id="redirect" view="bean:dynamicRedirectViewSelector" />
    
    <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>
 

以上文件根据原login-webflow.xml文件修改,粗体为修改部分。可以看到,我们在流程中增加了remoteLogin Action节点和remoteCallback View节点,下面我们配置remoteLogin节点:
在/WEB-INF/cas-servlet.xml文件中增加remoteLoginAction配置:

 

<bean id="remoteLoginAction"
    class="com.baidu.cas.web.flow.RemoteLoginAction"
    p:argumentExtractors-ref="argumentExtractors"
    p:warnCookieGenerator-ref="warnCookieGenerator"
    p:ticketGrantingTicketCookieGenerator-ref="ticketGrantingTicketCookieGenerator" />
 

同时创建com.baidu.cas.web.flow.RemoteLoginAction类:

 

/**
* 远程登陆票据提供Action.
* 根据InitialFlowSetupAction修改.
* 由于InitialFlowSetupAction为final类,因此只能将代码复制过来再进行修改.
* 
* @author GuoLin
*/
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("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;
    }
}
 

以上粗体为修改部分,要求客户端必须传入loginUrl参数,且当客户端传入submit参数时,直接为其提交用户名密码
然后再配置remoteCallbackView显示节点,修改src/default_views.properties文件,增加remoteCallbackView配置:

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

创建/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"%>
<html>
<head>
    <script type="text/javascript">
        var remoteUrl = "${remoteLoginUrl}?validated=true";<!--update 加参数validated=true-->

        // 构造错误消息
        var errorMessage = "";
        <spring:hasBindErrors name="credentials">
        errorMessage = "&errorMessage=" + encodeURIComponent('<c:forEach
var="error" items="${errors.allErrors}"><spring:message code="${error.code}"
text="${error.defaultMessage}" /></c:forEach>');
        </spring:hasBindErrors>
        
        // 构造service
        var serivce = "";
        <c:if test="${service != null && service != ''}">
        service = "&service=" + encodeURIComponent("${service}");
        </c:if>
        // 跳转回去
        window.location.href = remoteUrl + errorMessage + service;
    </script>
</head>
<body>
    ${remoteLoginMessage}
</body>
</html>
 

以上文件注意粗体部分validated=true,这里我们与客户端约定,当客户端登陆页面后带有参数validated=true时,不进行票据认证请求。这是因为,客户端登陆页面为http://clienthost/login.jsp,那么当用户访问URL http://clienthost/login.jsp时,客户端会重定向到CAS中央服务器请求TGT认证,但认证失败后CAS中央认证服务器会重定向到客户端登陆页面并显示登陆框,此时客户端必须以某种规则避免重新请求中央认证服务器认证, 在这里我们与客户端约定,当回发的请求为登陆页面且带有参数validated=true时即不转发TGT认证请求,即 http://
clienthost/login.jsp?validated=true 请求客户端不会重新发送TGT认证请求给中央认证服务器

到此,服务器端修改完成,下一篇介绍客户端如何构建

 

分享到:
评论

相关推荐

    让CAS支持客户端自定义登陆页面——服务器篇.docx

    ### 让CAS支持客户端自定义登录页面——服务器篇 #### 概述 本文档主要介绍如何在CAS(Central Authentication Service)系统中实现客户端自定义登录页面的功能,并且着重讲解服务器端的修改步骤与注意事项。CAS...

    让CAS支持客户端自定义登陆页面——客户端篇

    标题中的“让CAS支持客户端自定义登陆页面——客户端篇”表明了本文主要探讨的是如何在中央认证服务(Central Authentication Service, CAS)系统中,允许客户端应用程序实现自定义登录页面的配置与实现。CAS是一个...

    让CAS支持客户端自定义登陆页面——服务器篇[参考].pdf

    本文将详细介绍如何通过服务器端的配置,实现让CAS(Central Authentication Service,中央认证服务)支持客户端自定义登录页面的过程。这一过程不仅可以提升用户体验,还能够满足特定场景下的定制化需求。 ### ...

    让CAS支持客户端自定义登陆页面----服务器篇--.doc编程资料

    让CAS支持客户端自定义登陆页面----服务器篇--.doc

    让CAS支持客户端自定义登陆页面----服务器篇.doc

    让CAS支持客户端自定义登陆页面----服务器篇.doc

    让CAS支持客户端自定义登陆页面----服务器篇--.doc

    【让CAS支持客户端自定义登陆页面——服务器篇】 CAS(Central Authentication Service)是一个开源的身份验证框架,它允许用户通过单一登录(Single Sign-On, SSO)访问多个应用系统。在某些场景下,用户可能希望...

    让CAS支持客户端自定义登陆页面----服务器篇-.pdf

    本文主要介绍如何让CAS(Central Authentication Service)支持客户端自定义登录页面,主要集中在服务器端的修改。CAS是一种开源的身份验证框架,它提供单点登录(SSO)功能,通常用于集中管理多个应用系统的用户...

    CAS客户端自定义核心过滤器

    CAS客户端自定义核心过滤器,继承CAS的AbstractCasFilter自定义AuthenticationFilter

    cas 自定义登录页面

    为了让自定义登录页面能够与CAS服务器进行SSO交互,你需要确保页面中包含一个隐藏的IFRAME元素,用于接收来自CAS的重定向。此外,还需要监听IFRAME的加载事件,以便处理服务票据。 五、安全考虑 在实现自定义登录...

    CAS单点登录(SSO)服务端自定义认证+CAS客户端配置+CAS完整使用文档+CAS4.2.7 cas-serv服务端源码 cas-client客户端源码

    本压缩包提供了CAS服务端自定义认证的实现,以及CAS客户端的配置示例,适用于需要构建安全认证系统的IT专业人员。以下是对压缩包中内容的详细解释: 1. **CAS服务器端自定义认证**: CAS服务端的核心功能在于验证...

    cas客户端登陆配置文档

    4. **自定义登录界面**:如果你想自定义客户端的登录界面,可以在应用中创建一个定制的登录页面,然后配置CAS客户端库以使用该页面。你需要确保页面能够收集用户的凭证(通常是用户名和密码)并提交到CAS服务器进行...

    cas .net客户端的配置代码

    4. **处理重定向**:当用户访问受保护的资源时,如果未经过CAS认证,客户端会自动重定向到CAS登录页面。但有时可能会出现“循环重定向”问题,这通常是由于配置错误或票证验证失败导致的。为解决这个问题,可以检查...

    cas自定义登录页面

    2. **自定义登录页面**:默认情况下,CAS提供了一个标准的登录界面,但为了满足个性化需求,你可以自定义这个页面。在`cas-server-3.5.2`的`WEB-INF`目录下,找到`view`文件夹,里面包含了HTML模板文件,如`login....

    cas 服务端和客户端 main是自定义服务端登陆页面 还有一个是5.2.3原版

    在这个整合项目中,CAS服务端与客户端的角色得到了清晰的划分,同时允许自定义登录页面和数据库认证方式。 **CAS服务端** CAS服务端是整个SSO架构的核心,它负责处理所有认证请求。在这个项目中,使用的是CAS 5.2.3...

    CAS-3.2.1自定义客户端登录界面----完整篇

    - 在CAS服务器的配置文件(如`cas.properties`或`application.yml`)中,可能需要更新登录页面路径或启用自定义主题支持。 3. **客户端集成**: - CAS客户端通常是一个库,集成到需要SSO的应用中,如Spring ...

    CAS自定义加密和登录验证

    3. **协议消息加密**:CAS支持多种协议,如CAS Protocol、SAML 1.1等,这些协议的消息也需要加密。自定义加密机制可以确保通信过程中的数据安全。 自定义登录验证则涉及到用户身份验证的实现方式。CAS提供多种内置...

    cas-server-webapp

    改造cas,实现自定义登录页面,及错误跳转。具体参考我的博客 http://blog.csdn.net/just_lion/article/details/17204979

Global site tag (gtag.js) - Google Analytics