`

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

    博客分类:
  • cas
 
阅读更多

让CAS支持客户端自定义登陆页面——客户端篇
2010-03-23 16:06

客户端即指使用CAS中央认证服务器的应用程序,而不是指用户浏览器

在上一篇《让CAS支持客户端自定义登陆页面——服务器篇》中我们介绍了如何修改CAS服务器端来支持客户端自定义登陆页面,这一节我们开始讲述客户端实现理论和详细修改

客户端实现目标

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

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

对于case 1和case 3,普通的CAS客户端即可满足需求,但对于case 4和case 5,则需要我们定制自己的登陆页面。对于case 2,主要是需要满足部分登陆页面希望在用户未登陆状态显示登陆框,在已登陆状态显示用户欢迎信息的需求,实现这个需求我们是通过让CAS客户端认证器满足一个排除约定,即当用户请求路径为登陆页面且带有validated=true的参数时,即不进行重定向TGT认证请求

客户端修改方案

远程客户端修改,对于任何一种客户端方案都可以实现,这里为了简单起见,我们给出的修改方案基于CAS官方提供的Java客户端3.1.3。首先我们使用CAS Client 3.1.3搭建一个CAS客户端,具体搭建方法可以参考CAS官网:CAS Client for Java 3.1

根据服务器流程修改方案,我们可以知道,所有的远程请求都必须携带有loginUrl参数信息以使得服务器端知道在认证失败后转向客户端登陆页面。而在CAS客户端上,上一节的case 4和case 5,我们主要通过提交表单的方式传递loginUrl,而case 1, case 3则是依靠org.jasig.cas.client.authentication.AuthenticationFilter类进行的转向,但使用AuthenticationFilter转向时,是没有loginUrl信息的,因此我们首先需要重新实现一个自己的认证过滤器,以下是我们自己的认证过滤器的代码:

/**
* 远程认证过滤器.
* 由于AuthenticationFilter的doFilter方法被声明为final,
* 只好重新实现一个认证过滤器,支持localLoginUrl设置.

* @author GuoLin

*/
public class RemoteAuthenticationFilter extends AbstractCasFilter {
    
    public static final String CONST_CAS_GATEWAY = "_const_cas_gateway_";

    /**
     * 本地登陆页面URL.
     */
    private String localLoginUrl;
    
    /**
     * The URL to the CAS Server login.
     */
    private String casServerLoginUrl;

    /**
     * Whether to send the renew request or not.
     */
    private boolean renew = false;

    /**
     * Whether to send the gateway request or not.
     */
    private boolean gateway = false;

    protected void initInternal(final FilterConfig filterConfig) throws ServletException {
        super.initInternal(filterConfig);
        setCasServerLoginUrl(getPropertyFromInitParams(filterConfig, "casServerLoginUrl", null));
        log.trace("Loaded CasServerLoginUrl parameter: " + this.casServerLoginUrl);
        setLocalLoginUrl(getPropertyFromInitParams(filterConfig, "localLoginUrl", null));
        log.trace("Loaded LocalLoginUrl parameter: " + this.localLoginUrl);
        setRenew(Boolean.parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
        log.trace("Loaded renew parameter: " + this.renew);
        setGateway(Boolean.parseBoolean(getPropertyFromInitParams(filterConfig, "gateway", "false")));
        log.trace("Loaded gateway parameter: " + this.gateway);
    }

    public void init() {
        super.init();
        CommonUtils.assertNotNull(this.localLoginUrl, "localLoginUrl cannot be null.");
        CommonUtils.assertNotNull(this.casServerLoginUrl, "casServerLoginUrl cannot be null.");
    }

    public final void doFilter(final ServletRequest servletRequest,
            final ServletResponse servletResponse, final FilterChain filterChain)
            throws IOException, ServletException {
        final HttpServletRequest request = (HttpServletRequest) servletRequest;
        final HttpServletResponse response = (HttpServletResponse) servletResponse;
        final HttpSession session = request.getSession(false);
        final String ticket = request.getParameter(getArtifactParameterName());
        final Assertion assertion = session != null ? (Assertion) session
                .getAttribute(CONST_CAS_ASSERTION) : null;
        final boolean wasGatewayed = session != null
                && session.getAttribute(CONST_CAS_GATEWAY) != null;

        // 如果访问路径为localLoginUrl且带有validated参数则跳过
        URL url = new URL(localLoginUrl);
        final boolean isValidatedLocalLoginUrl = request.getRequestURI().endsWith(url.getPath()) &&
            CommonUtils.isNotBlank(request.getParameter("validated"));
        
        if (!isValidatedLocalLoginUrl && CommonUtils.isBlank(ticket) && assertion == null && !wasGatewayed) {
            log.debug("no ticket and no assertion found");
            if (this.gateway) {
                log.debug("setting gateway attribute in session");
                request.getSession(true).setAttribute(CONST_CAS_GATEWAY, "yes");
            }

            final String serviceUrl = constructServiceUrl(request, response);

            if (log.isDebugEnabled()) {
                log.debug("Constructed service url: " + serviceUrl);
            }

            String urlToRedirectTo = CommonUtils.constructRedirectUrl(
                    this.casServerLoginUrl, getServiceParameterName(),
                    serviceUrl, this.renew, this.gateway);

            // 加入localLoginUrl
            urlToRedirectTo += (urlToRedirectTo.contains("?") ? "&" : "?") + "loginUrl=" + URLEncoder.encode(localLoginUrl, "utf-8");

            if (log.isDebugEnabled()) {
                log.debug("redirecting to \"" + urlToRedirectTo + "\"");
            }
            
            response.sendRedirect(urlToRedirectTo);
            return;
        }

        if (session != null) {
            log.debug("removing gateway attribute from session");
            session.setAttribute(CONST_CAS_GATEWAY, null);
        }

        filterChain.doFilter(request, response);
    }

    public final void setRenew(final boolean renew) {
        this.renew = renew;
    }

    public final void setGateway(final boolean gateway) {
        this.gateway = gateway;
    }

    public final void setCasServerLoginUrl(final String casServerLoginUrl) {
        this.casServerLoginUrl = casServerLoginUrl;
    }

    public final void setLocalLoginUrl(String localLoginUrl) {
        this.localLoginUrl = localLoginUrl;
    }
    
}

以上粗体代码为修改部分,其余代码均拷贝自org.jasig.cas.client.authentication.AuthenticationFilter,可以看到我们为原有的认证过滤器增加了一个参数localLoginUrl。在WEB-INF/web.xml中配置:

<filter>
    <filter-name>CAS Authentication Filter</filter-name>
    <filter-class>com.baidu.cas.client.validation.RemoteAuthenticationFilter</filter-
class>
    <init-param>
        <param-name>localLoginUrl</param-name>
        <param-value>http://GUOLIN:9080/cas-client-java-custom-login/login.jsp</param-
value>
    </init-param>
    <init-param>
        <param-name>casServerLoginUrl</param-name>
        <param-value>https://GUOLIN/cas-server/remoteLogin</param-value>
    </init-param>
    <init-param>
        <param-name>serverName</param-name>
        <param-value>http://GUOLIN:9080</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CAS Authentication Filter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

此处我们将过滤器指向自己的过滤器并增加本地登陆页面路径设置。最后我们来看看登陆页面login.jsp:

<%@ 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">
    function getParam(name) {
        var queryString = window.location.search;
        var param = queryString.substr(1, query.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;
    }
    function init() {
        // 显示异常信息
        var error = getParam("errorMessage");
        if (error) {
            document.getElementById("errorMessage").innerHTML = decodeURIComponent(error);
        }
        // 注入service
        var service = getParam("service");
        if (service)
            document.getElementById("service").value = decodeURIComponent(service);
        else
            document.getElementById("service").value = location.href;
    }
    </script>
</head>
<body>
    <h1>远程CAS客户端登陆页面</h1>
    <% if (request.getRemoteUser() == null) { %>
        <div id="errorMessage"></div>
        <form id="myLoginForm" action="https://guolin/cas-server/remoteLogin" method="post">
            <input type="hidden" id="service" name="service" value="">
            <input type="hidden" name="loginUrl" value="http://guolin:9080/cas-client-java-custom-login/login.jsp">
            <input type="hidden" name="submit" value="true" />
            <table>
                <tr>
                    <td>用户名:</td>
                    <td><input type="text" name="username"></td>
                </tr>
                <tr>
                    <td>密&nbsp;&nbsp;码:</td>
                    <td><input type="password" name="password"></td>
                </tr>
                <tr>
                    <td colspan="2"><input type="submit" value="登陆" /></td>
                </tr>
            </table>
        </form>
        <script type="text/javascript">init()</script>
    <% } else { %>
        <div class="welcome">您好:<%= request.getRemoteUser() %></div>
        <div id="logout">
            <a href="https://GUOLIN/cas-server/remoteLogout?service=http://guolin:9080/cas-client-java-custom-login/login.jsp">单点登出</a>
        </div>
    <% } %>
</body>
</html>

login.jsp主要是为了满足上一节中的case 4和case 5,这里为了简单起见,仅有是否已登陆判断使用了服务器端代码,其他均使用客户端代码实现。以上粗体字中,我们首先将表单action指向服务器端remoteLogin,然后在里面设置了两个重要的hidden域以传递 loginUrl和submit参数,前者用于告诉服务器失败后转向何处,后者告诉服务器端webflow现在要进行提交而不是TGT认证请求

分享到:
评论

相关推荐

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

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

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

    "CAS支持客户端自定义登陆页面——服务器篇" 为了让CAS支持客户端自定义登陆页面,需要在服务器端进行修改。首先,需要在/WEB-INF/web.xml文件中增加一个/remoteLogin的映射,以便将请求指向新的登陆页面。其次,...

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

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

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

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

    CAS客户端开发配置及其所需求的最基本的jar文件

    8. **单登出(Single Logout,SLO)支持**:如果需要,还可以配置客户端支持SLO功能,这样用户在CAS服务器上注销时,客户端应用也会自动注销。 在实际项目中,可能还需要处理其他问题,比如用户会话管理、异常处理...

    JASIG CAS 3 Learning Note 1 -- getting started

    **JASIG CAS 3 学习笔记 1 —— 开始之旅** JASIG CAS(Central Authentication Service)是一款开源的身份验证系统,广泛应用于多应用系统中的单点登录(Single Sign-On, SSO)。CAS 3 是其第三个主要版本,提供了...

    基于Java的源码-单点登录系统 JA-SIG CAS.zip

    在本案例中,我们讨论的是基于Java实现的单点登录系统——JA-SIG CAS。这个系统是开源的,由教育高级网(Ja-Sig)开发,旨在提供一种简单而有效的解决方案,为校园环境或企业环境中的各种应用实现安全的单点登录功能...

    cas server

    同时,它有丰富的插件和API,可以进行自定义扩展,例如支持额外的认证机制(如LDAP、数据库等)或者添加自定义的属性解析器。 8. **监控和日志**:为了确保CAS Server的正常运行,需要对其进行监控和日志记录。这...

    spring security

    - **配置 Spring Security**: 配置客户端以支持 CAS SSO。 - **运行配置了 CAS 的子系统**: 验证 SSO 功能。 - **为 CAS 配置 SSL**: 保护 CAS 服务器通信的安全性。 ##### 12. Basic 认证 - **配置 basic 验证**:...

    spring security2 安全手册(牛人写的)

    - **修改配置文件**:更新Spring Security配置以支持自定义登录页面。 - **登录页面中的参数配置**:设置登录表单所需的参数,如用户名和密码字段名称等。 - **测试一下**:验证自定义登录页面的功能是否正常工作。 ...

    PyPI 官网下载 | ftw.casauth-1.4.1.tar.gz

    1. CAS客户端支持:它实现了与CAS服务器的通信,包括票证验证、登录和登出操作。 2. 集成简单:该库易于集成到基于Python的Web应用程序中,如Flask和Django,通过提供中间件或装饰器,可以方便地添加CAS身份验证。 3...

    Windows应用程序开发入门到精通十二:将安全隐患扼杀在摇篮之中——.pptx

    此外,开发者还可以自定义身份验证机制,通过实现`System.Security.IIdentity`接口和使用`System.Security.GenericIdentity`类来管理用户凭证。例如,`WindowsIdentity.GetCurrent().Name`可以获取当前用户的Windows...

    MCTS .NET Framework 2.0 Windows Applications

    - **MCTS_70-526_SelfPacedTrainingKit_MS.Net Framework2.0 Windows-Based Client Development.pdf**:这份文档是针对MCTS认证考试70-526的自我学习培训材料,重点在于.NET Framework 2.0下的Windows客户端开发。...

    SpringSecurity 3.0.1.RELEASE.CHM

    21.3. 配置CAS客户端 22. X.509认证 22.1. 概述 22.2. 把X.509认证添加到你的web系统中 22.3. 为tomcat配置SSL 23. 替换验证身份 23.1. 概述 23.2. 配置 A. 安全数据库表结构 A.1. User表 A.1.1. 组权限 ...

    JAVA上百实例源码以及开源项目源代码

    多人聊天室 3个目标文件 第一步:运行ServerData.java 启动服务器,然后服务器处于等待状态 第二步:运行LoginData.java 启动(客户端)登陆界面 输入用户名 ip为本机localhost 第三步:在登陆后的界面文本框输入文本...

Global site tag (gtag.js) - Google Analytics