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

CAS 3.x代理配置

    博客分类:
  • JAVA
阅读更多

http://fallenlord.blogbus.com/logs/57175888.html

前阵子改造系统,需要做CAS的Proxy,上网找了很久,找到了一些讲述原理的文章,但几乎没有讲述配置的文章,有讲述配置的也是错误的或是不完整的或是针对2.x版本的,还有很多是直接把代理配置当做普通配置使用-_-

那么,既然网上没有,我就在这里写一下吧

1. 什么情况下需要使用代理

显然这是最需要首先搞清楚的一个问题

假设现在有服务A使用了CAS,我们作为用户zhangsan去访问服务A,这没有什么问题
然后出现了一个新的需求,有服务B也实现了CAS,要求我们仍然只是访问A,但是A会以我们相同的用户(zhangsan)去访问服务B获取我们所需的内容并呈现出来。
此时A就被我们称为代理(Proxy),B被称为后台服务(back-end service)或被代理应用(Proxied Application)

一个简单的应用场景就是Portal,我们只访问Portal,由Portal去各个子系统抓取我们所需的内容并呈现出来
如果有兄弟没有玩过Portal,那么iGoogle也是个很好的场景,你在iGoogle上登录后就可以直接查看到GMail的内容了,这就是用了Proxy技术

 

2. 代理运转原理

运转原理写起来比较麻烦,这里推荐三篇文章,均是介绍CAS的代理运转原理的:

相信看完以上三篇神来之笔后,可以对CAS代理这块的原理有个简单的认识
但需要深入理解,显然光靠神来之笔是靠不住的

 

3. 代理相关配置

OK,直接就到配置了,首先我们需要分为两部分来描述,第一部分是代理应用(Proxying Applicatiion)也就是前端的Portal之类的东东:

<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://www.cas-server.com</param-value>
    </init-param>
    <init-param>
        <param-name>redirectAfterValidation</param-name>
        <param-value>true</param-value>
    </init-param>
    <init-param>
        <param-name>exceptionOnValidationFailure</param-name>
        <param-value>false</param-value>
    </init-param>
    <init-param>
        <param-name>serverName</param-name>
        <param-value>http://www.local-app.com</param-value>
    </init-param>
    <init-param>
        <param-name>proxyCallbackUrl</param-name>
        <param-value>http://www.local-app.com/cas-proxy/proxyCallback</param-value>
    </init-param>
    <init-param>
        <param-name>proxyReceptorUrl</param-name>
        <param-value>/proxyCallback</param-value>
    </init-param>
</filter>

高亮的两个参数配置应该指向实际同一个地址(只是proxyReceptorUrl用相对路径描述,而proxyCallbackUrl用绝对路径 描述),它们将为这个服务提供一个callback的拦截,使得在每次validator去校验ST的时候,CAS服务器都会发起一个回调请求来访问 proxyCallbackUrl的位置(因此这个地址必须是绝对地址且从CAS服务器发起请求可以访问到的),将PGT(及PGTIou)告诉这个代理 应用,而代理应用会保存这个PGT到Session中

这里有个需要特别注意的问题,这个问题在所有的帖子甚至官方的文档中都没有提及,那就是回调的地址,必须在AuthenticationFilter的url-pattern之前单独的配出来:

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

<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>

否则callback调用会被AuthenticationFilter给拦截而无法到达ValidationFilter

第二部分是后端服务: 

<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://www.cas-server.com</param-value>
    </init-param>
    <init-param>
        <param-name>redirectAfterValidation</param-name>
        <param-value>false</param-value>
    </init-param>
    <init-param>
        <param-name>exceptionOnValidationFailure</param-name>
        <param-value>false</param-value>
    </init-param>
    <init-param>
        <param-name>serverName</param-name>
        <param-value>http://www.local-app.com</param-value>
    </init-param>
    <init-param>
        <param-name>acceptAnyProxy</param-name>
        <param-value>true</param-value>
    </init-param>

    <init-param>
        <param-name>proxyCallbackUrl</param-name>
        <param-value>http://www.local-app.com/cas-backend/proxyCallback</param-value>
    </init-param>
    <init-param>
        <param-name>proxyReceptorUrl</param-name>
        <param-value>/proxyCallback</param-value>
    </init-param>
</filter> 

以上高亮代码的acceptAnyProxy是后端服务所必须的(它与allowedProxyChains两个配置二选一),若没有配置acceptAnyProxy或allowedProxyChains,将无法校验代理票据(Proxy Ticket)

而proxyCallbackUrl和proxyReceptorUrl是可选的,如果给出则在校验代理票据(Proxy Ticket)时同时CAS会再次执行回调颁发PGT给此后台服务,使之可以同时成为代理用于代理另一个后台服务。若不配置则CAS不会告诉此应用PGT

 

4. 如何使用代理

看起来好像大功告成了,但是实际上还有最重要的一点我们还没做,就Server to Server的这个请求要怎么来发的问题

首先,既然要发送请求,一定是我们的代理端已经完成了到CAS的认证,那么我们首先获取到Assertion:

final Assertion assertion = (Assertion) (session == null ? request.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION) : session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION));

然后从assertion中获取到proxy ticket:

final String proxyTicket = assertion.getPrincipal().getProxyTicketFor(serviceUrl);

最后将proxyTicket以参数的形式绑定到serviceUrl(待访问的backend service地址)之后并发送请求,参数名必须是ticket:

URL url = new URL(serviceUrl + "?ticket=" + proxyTicket);
HttpURLConnection conn = null;
try {
    conn = (HttpURLConnection) url.openConnection();
    // 业务处理
    ...
} catch (final Exception ex) {
    ...
} finally {
    if (conn != null) {
        conn.disconnect();
    }
}
...

这样,当后台服务(back-end service)获取到这条请求后,会到CAS上去校验ticket是否为合法的PT,若合法则允许通过(此时redirectAfterValidation应该为false以避免直接响应一个302 Redirect请求)

 

5. 代理使用方案的一点点讨论 

按照上面的做法,应该说功能都已经实现了,那么现在的问题是什么呢?——性能与压力
这里面有两次请求是我们看起来多余的:

  • 1. 从代理端Assertion中获取PT需要发送一次请求给CAS(PT由CAS颁发)
  • 2. 从被代理端获取到PT后发送请求给CAS以求校验

这些请求触发频率是每次代理端向被代理端发送请求时,不光消耗了代理端和被代理端的性能,也对CAS产生了多余的压力。

那么如何解决这个问题呢?有两种方案:

  • 1. 缓存,在代理和被代理两端将PT缓存起来,这样只有第一次双方需要去访问CAS请求颁发PT和校验PT,以后可以直接使用这个PT即可
  • 2. 颁发自己的票据,这与前端Browser-CAS客户端的模式很相似,CAS客户端为浏览器颁发SessionID作为自己的认证标识,从而撇开了CAS服务器,那么这里我们也可以由被代理端向代理端颁发一个唯一的标识来作为自己的认证标识

这两种方法如出一辙,其实本质上没什么区别。在我们的系统改造中,最后使用的是后者,这是由于它本身就存在一个自己的票据的功能

OK,接下来我们看下使用第二种方案的改造细节吧

方案决定了我们只是需要在第一次代理端到被代理端认证的时候发起与CAS的认证,因此我们继承了 org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter 类,实现了自己的ValidationFilter只扩展了它的一个方法:

protected void onSuccessfulValidation(final HttpServletRequest request, final HttpServletResponse response, final Assertion assertion) {
    // 认证服务地址
    final String loginServiceUrl = "http://somewhere.com/loginService ";
   
    // serviceUrl为要发送的目标地址——被代理端上的一个URL   
    final String proxyTicket = assertion.getPrincipal().getProxyTicketFor(loginServiceUrl);
    final String username = assertion.getPrincipal().getName();

    // 获取到URLConnection或是HttpClient
    URL url = new URL(loginServiceUrl + "?ticket=" + proxyTicket);
    HttpURLConnection conn = null;
    try {
        conn = (HttpURLConnection) url.openConnection();
        // 业务处理
        // 将响应回来的myticket存储到Session中,以后每次发送请求都将携带这个myticket
        // 而被代理端也将myticket存储到Session中,以后每次互相访问都只需校验此值即可
        ...
    } catch (final Exception ex) {
        ...
    } finally {
        if (conn != null) {
            conn.disconnect();
        }
    }
    ...
}

这个方法将在票据第一次被校验成功的时候触发

在被代理端,我们新建一个地址为/loginService的服务,大致逻辑如下:

// 获取CAS票据
String proxyTicket = req.getParameter("ticket");
if (proxyTicket == null) {
    throw new WebScriptException(HttpServletResponse.SC_BAD_REQUEST, "Proxy ticket not specified");
}

try {

    // 使用CAS代理票据校验器校验PT
    final Cas20ProxyTicketValidator validator = new Cas20ProxyTicketValidator(casServerUrlPrefix);
    validator.setAcceptAnyProxy(true);
    final String serviceUrl = req.getServerPath() + req.getServicePath();
    final Assertion assertion = validator.validate(proxyTicket, serviceUrl);

    // 完成认证并从Session中获取myticket
    authenticationComponent.setCurrentUser(username);
    final String myTicket = authenticationService.getCurrentTicket();
   
    render(myTicket);
   
} catch (AuthenticationException ex) {
    logger.error("Login failed", ex);
    throw new WebScriptException(HttpServletResponse.SC_FORBIDDEN, "Login failed");

} catch (TicketValidationException ex) {
    logger.error("Ticket validate failred.", ex);
    throw new WebScriptException(HttpServletResponse.SC_FORBIDDEN, "Ticket validate failred");
   
} finally {
    // ...
}

可以看到,我们自己实例化了一个CAS的Validator来完成校验而不是通过那些CAS的Filter,这使得整个后端服务可以无需被CAS Filter拦截(当然,被拦截了也没啥关系)

OK,至此,一个较为完整的CAS代理就完成了,have fun

分享到:
评论

相关推荐

    cas4.1.x集成.zip

    3. **配置服务器**:修改`cas.properties`配置文件,设置数据库连接、服务器端口等信息。 4. **部署CAS**:将CAS服务器打包成WAR文件,部署到应用服务器(如Tomcat)。 5. **客户端集成**:在每个服务提供商项目中...

    CAS Protocol 3.0 Specification.docx 官方中文版教程详解

    Apereo CAS Server 4.x/5.x版本可处理协议3.0的所有功能。 **CAS URIs** CAS协议基于HTTP,各个组件通过特定的URI进行通信: - **/login**:凭证请求和接受的入口,如果Web浏览器携带有效的TGC(Ticket-Granting ...

    cas-client-core-3_asleepb5x_CAS_源码.zip

    "cas-client-core-3_asleepb5x_CAS_源码.zip"是一个包含CAS客户端核心库的源码压缩包,版本号为3.x。这个压缩包对于理解CAS客户端的工作原理和进行二次开发非常有帮助。 CAS客户端的核心功能在于提供与CAS服务器的...

    cas-client-core-3_asleepb5x_CAS_源码.rar

    2. **代理认证**:如果需要代理服务,CAS客户端能够通过获取PT来代表用户访问受保护的服务。`ProxyGrantingTicketStorage`接口和`ProxyGrantingTicketIouStorage`接口分别用于存储和管理PGT(Proxy Granting Ticket...

    Laravel开发-cas

    如果你的应用需要通过CAS代理其他服务,可以启用`proxy`配置并实现相应的逻辑。代理认证允许用户通过CAS服务器访问需要身份验证的外部资源。 **7. 错误处理与日志** 在集成过程中,确保处理可能出现的错误,如网络...

    cas-3.4.1_单点登录_CAS_

    5. **代理认证**:CAS支持代理认证,允许一个受信任的代理代表用户进行身份验证,这在多层应用架构中很有用。 6. **安全性**:CAS通过HTTPS等安全协议传输票证,确保了通信的安全性。同时,它还支持票证的过期和...

    CAS整合LDAP实现单点登录学习笔记.pdf

    3. **启用代理认证**:如果需要的话,设置代理认证功能。 ##### 4.5 关于CASTestClient和cas的说明 CASTestClient是一个用于测试CAS认证过程的工具。它可以帮助开发者测试CAS服务器的功能,比如: - **验证CAS...

    单点登录资料(含客户端jar)

    2. CAS服务器和客户端应用之间需要能够通信,可能需要配置防火墙规则或代理设置。 3. 使用SSL/TLS加密传输,提高安全性。 在实践中,除了基本的部署和配置,还可能涉及到自定义主题、自定义认证处理器、单点登出等...

    Apache+多个Tomcat 服务器集群配置

    将以下Module的注释去掉,这里并没有使用mod_jk.so进行apache和tomcat的链接,从2.X以后apache自身已集成了mod_jk.so的功能。只需简单的把下面几行去掉注释,就相当于以前用mod_jk.so比较繁琐的配置了。这里主要采用...

    spring security 参考手册中文版

    32.3.3使用CAS认证无状态服务 249 配置CAS以获取代理授予票证 249 使用代理票证调用无状态服务 250 32.3.4代理票证认证 251 33. X.509认证 253 33.1概述 253 33.2将X.509身份验证添加到您的Web应用程序 253 33.3在...

    单点登录分析报告.pdf

    6. 使用反向代理模块可实现多层SSO认证,每层可配置不同验证模式。 7. 支持多种存储用户信息的方式,如数据库、LDAP和XML。 8. 提供PHP和ASP的API接口。 9. 兼容JBoss 3.2.6和Jakarta Tomcat 5.0.27以上版本。 然而...

    acegi的详细配置实现

    配置Run-As认证替换涉及到指定代理用户和目标用户之间的关系。 #### 十一、表单认证机制 ##### 11.1 概览 表单认证是最常见的认证方式之一,用户通过提交用户名和密码进行身份验证。 ##### 11.2 配置 配置表单...

    springsecurity.pdf

    配置SiteMinder认证机制需要与SiteMinder服务器进行交互,设置相应的认证代理和会话管理参数。 #### 九、Run-As认证替代方案 ##### 10.1 概览 Run-As认证替代方案允许开发者指定一个固定的角色,用于在执行某些...

    nginx集群配置-测试通过

    # 其他配置... } } } ``` 此外,还可以结合使用Nginx与Memcached进行session共享: ```nginx http { upstream backend { server backend1.example.com; server backend2.example.com; } upstream ...

    Spring Security 中文教程.pdf

    - **FilterChainProxy**:代理过滤器链,根据配置执行过滤器。 - **核心安全过滤器**: - **FilterSecurityInterceptor**:主要的安全拦截器,负责执行访问控制决策。 - **ExceptionTranslationFilter**:异常...

    axios 处理 302 状态码的解决方法

    在前端,我们可以配置Axios的响应拦截器来处理401状态码。以下是一个示例: ```javascript axios.interceptors.response.use((response) =&gt; { return response; }, function (error) { if (401 === error....

Global site tag (gtag.js) - Google Analytics