`
yeminping
  • 浏览: 181044 次
  • 性别: Icon_minigender_1
  • 来自: 福州
社区版块
存档分类
最新评论

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

    博客分类:
  • JAVA
阅读更多

客户端即指使用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中配置:

<context-param>
        <param-name>serverName</param-name>
        <param-value>http://192.168.7.131:8080</param-value>
    </context-param>

    <filter>
        <filter-name>CAS Single Sign Out Filter</filter-name>
        <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>CAS Single Sign Out Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <listener>
        <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
    </listener>
    <filter>
        <filter-name>CAS Authentication Filter</filter-name>
        <filter-class>cn.RemoteAuthenticationFilter</filter-class>
        <init-param>
            <param-name>localLoginUrl</param-name>
            <param-value>
                http://192.168.7.131:8080/zz/login.jsp
            </param-value>
        </init-param>
        <init-param>
            <param-name>casServerLoginUrl</param-name>
            <param-value>http://192.168.7.130:8080/sso/remoteLogin</param-value>
        </init-param>
        <init-param>
            <param-name>renew</param-name>
            <param-value>false</param-value>
        </init-param>
        <init-param>
            <param-name>gateway</param-name>
            <param-value>false</param-value>
        </init-param>
        <init-param>
            <param-name>serverName</param-name>
            <param-value>http://192.168.7.131:8080</param-value>
        </init-param>
    </filter>

    <filter>
        <filter-name>CAS Validation Filter</filter-name>
        <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>
        <init-param>
            <param-name>casServerUrlPrefix</param-name>
            <param-value>http://192.168.7.130:8080</param-value>
        </init-param>
        <init-param>
            <param-name>casServerUrlPrefix</param-name>
            <param-value>http://192.168.7.130:8080/sso</param-value>
        </init-param>
        <init-param>
            <param-name>useSession</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>redirectAfterValidation</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>


    <filter>
        <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
        <filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>
    </filter>


    <filter>
        <filter-name>CAS Assertion Thread Local Filter</filter-name>
        <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class>
    </filter>


    <filter-mapping>
        <filter-name>CAS Authentication Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter-mapping>
        <filter-name>CAS Validation Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter-mapping>
        <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter-mapping>
        <filter-name>CAS Assertion Thread Local 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支持客户端自定义登陆页面——服务器篇.docx

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

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

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

    让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 是其第三个主要版本,提供了...

    cas server

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

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

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

    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. 组权限 ...

    机顶盒功能介绍手册

    - **自定义**: 用户可以根据个人喜好进行排序。 - **6.2 频道排序的实现过程** - **获取列表**: 获取所有频道的信息列表。 - **排序算法**: 应用适当的排序算法对列表进行排序。 - **更新界面**: 更新用户界面...

Global site tag (gtag.js) - Google Analytics