Yale CAS实现原理及其基础协议

CAS 的基本原理 

CAS(Central Authentication Service) 是 Yale 大学发起的一个开源项目,据统计,大概每 10 个采用开源构建 Web SSO 的 Java 项目,就有 8 个使用 CAS 。对这些统计,我虽然不以为然,但有一点可以肯定的是, CAS 是我认为最简单实效,而且足够安全的 SSO 选择。

       本节主要分析 CAS 的安全性,以及为什么 CAS 被这样设计,带着少许密码学的基础知识,我希望有助于读者对 CAS 的协议有更深层次的理解。
从结构体系看, CAS 包含两部分:
l         CAS Server
CAS Server 负责完成对用户的认证工作, CAS Server 需要独立部署,有不止一种 CAS Server 的实现, Yale CAS Server 和 ESUP CAS Server 都是很不错的选择。
CAS Server 会处理用户名 / 密码等凭证 (Credentials) ,它可能会到数据库检索一条用户帐号信息,也可能在 XML 文件中检索用户密码,对这种方式, CAS 均提供一种灵活但同一的接口 / 实现分离的方式, CAS 究竟是用何种认证方式,跟 CAS 协议是分离的,也就是,这个认证的实现细节可以自己定制和扩展。
l         CAS Client
CAS Client 负责部署在客户端(注意,我是指 Web 应用),原则上, CAS Client 的部署意味着,当有对本地 Web 应用的受保护资源的访问请求,并且需要对请求方进行身份认证, Web 应用不再接受任何的用户名密码等类似的 Credentials ,而是重定向到 CAS Server 进行认证。
目前, CAS Client 支持(某些在完善中)非常多的客户端,包括 Java 、 .Net 、 ISAPI 、 Php 、 Perl 、 uPortal 、 Acegi 、 Ruby 、 VBScript 等客户端,几乎可以这样说, CAS 协议能够适合任何语言编写的客户端应用。
 剖析协议就像剖析设计模式,有些时候,协议让人摸不着头脑。 CAS 的代理模式要相对复杂一些,它引入了一些新的概念,我希望能够在这里描述一下其原理,有助于读者在配置和调试 CAS SSO 有更清晰的思路。
如果没记错, CAS 协议应该是由  Drew Mazurek 负责可开发的,从 CAS v1 到现在的 CAS v3 ,整个协议的基础思想都是基于 Kerberos 的票据方式。
       CAS v1 非常原始,传送一个用户名居然是 ”yes/ndavid.turing” 的方式, CAS v2 开始使用了 XML 规范,大大增强了可扩展性, CAS v3 开始使用 AOP 技术,让 Spring 爱好者可以轻松配置 CAS Server 到现有的应用环境中。
CAS 是通过 TGT(Ticket Granting Ticket) 来获取 ST(Service Ticket) ,通过 ST 来访问服务,而 CAS 也有对应 TGT , ST 的实体,而且他们在保护 TGT 的方法上虽然有所区别,但是,最终都可以实现这样一个目的——免去多次登录的麻烦。
       下面,我们看看 CAS 的基本协议框架:
基础协议
 
                                                 CAS 基础模式
       上图是一个最基础的 CAS 协议, CAS Client 以 Filter 方式保护 Web 应用的受保护资源,过滤从客户端过来的每一个 Web 请求,同时, CAS Client 会分析 HTTP 请求中是否包请求 Service Ticket( 上图中的 Ticket) ,如果没有,则说明该用户是没有经过认证的,于是, CAS Client 会重定向用户请求到 CAS Server ( Step 2 )。 Step 3 是用户认证过程,如果用户提供了正确的 Credentials , CAS Server 会产生一个随机的 Service Ticket ,然后,缓存该 Ticket ,并且重定向用户到 CAS Client (附带刚才产生的 Service Ticket ), Service Ticket 是不可以伪造的,最后, Step 5 和 Step6 是 CAS Client 和 CAS Server 之间完成了一个对用户的身份核实,用 Ticket 查到 Username ,因为 Ticket 是 CAS Server 产生的,因此,所以 CAS Server 的判断是毋庸置疑的。
       该协议完成了一个很简单的任务,就是 User(david.turing) 打开 IE ,直接访问 helloservice 应用,它被立即重定向到 CAS Server 进行认证, User 可能感觉到浏览器在 helloservcie 和 casserver 之间重定向,但 User 是看不到, CAS Client 和 CAS Server 相互间的 Service Ticket 核实 (Validation) 过程。当 CAS Server 告知 CAS Client 用户 Service Ticket 对应确凿身份, CAS Client 才会对当前 Request 的用户进行服务。
CAS 如何实现 SSO
       当我们的 Web 时代还处于初级阶段的时候, SSO 是通过共享 cookies 来实现,比如,下面三个域名要做 SSO :
如果通过 CAS 来集成这三个应用,那么,这三个域名都要做一些域名映射,
因为是同一个域,所以每个站点都能够共享基于 cas.org 的 cookies 。这种方法原始,不灵活而且有不少安全隐患,已经被抛弃了。
CAS 可以很简单的实现跨域的 SSO ,因为,单点被控制在 CAS Server ,用户最有价值的 TGC-Cookie 只是跟 CAS Server 相关, CAS Server 就只有一个,因此,解决了 cookies 不能跨域的问题。
回到 CAS 的基础协议图,当 Step3 完成之后, CAS Server 会向 User 发送一个 Ticket granting cookie (TGC) 给 User 的浏览器,这个 Cookie 就类似 Kerberos 的 TGT ,下次当用户被 Helloservice2 重定向到 CAS Server 的时候, CAS Server 会主动 Get 到这个 TGC cookie ,然后做下面的事情:
1,               如果 User 的持有 TGC 且其还没失效,那么就走基础协议图的 Step4 ,达到了 SSO 的效果。
2,               如果 TGC 失效,那么用户还是要重新认证 ( 走基础协议图的 Step3) 。
 CAS 的代理模式
 模式 1 已经能够满足大部分简单的 SSO 应用,现在,我们探讨一种更复杂的情况,即用户访问 helloservice , helloservice 又依赖于 helloservice2 来获取一些信息,如同:
User à helloservice à helloservice2
这种情况下,假设 helloservice2 也是需要对 User 进行身份验证才能访问,那么,为了不影响用户体验(过多的重定向导致 User 的 IE 窗口不停地 闪动 ) , CAS 引入了一种 Proxy 认证机制,即 CAS Client 可以代理用户去访问其它 Web 应用。
代理的前提是需要 CAS Client 拥有用户的身份信息 ( 类似凭据 ) 。 与其说之前我们提到的 TGC 是用户持有对自己身份信息的一种凭据,则这里的 PGT 就是 CAS Client 端持有的对用户身份信息的一种凭据。凭借 TGC , User 可以免去输入密码以获取访问其它服务的 Service Ticket ,所以,这里,凭借 PGT , Web 应用可以代理用户去实现后端的认证,而无需前端用户的参与。
如下面的 CAS Proxy 图所示, CAS Client 在基础协议之上,提供了一个额外的 PGT URL 给 CAS Server, 于是, CAS Server 可以通过 PGT URL 提供一个 PGT 给 CAS Client 。 
 
       初学者可能会对上图的 PGT URL 感到迷惑,或者会问,为什么要这么麻烦,要通过一个额外的 URL( 而且是 SSL 的入口 ) 去传递 PGT ?如果直接在 Step 6 返回,则连用来做对应关系的 PGTIOU 都可以省掉。 PGTIOU 设计是从安全性考虑的,非常必要, CAS 协议安全性问题我会在后面一节介绍。
于是, CAS Client 拿到了 PGT(  PGTIOU-85…..ti2td ) ,这个 PGT 跟 TGC 同样地关键, CAS Client 可以通过 PGT 向后端 Web 应用进行认证。如下图所示, Proxy 认证与普通的认证其实差别不大, Step1, 2 与基础模式的 Step 1,2 几乎一样,唯一不同的是, Proxy 模式用的是 PGT 而不是 TGC ,是 Proxy Ticket ( PT )而不是 Service Ticket 。
最终的结果是, helloservice2 明白 helloservice 所代理的客户是 David. Turing 同学,同时,根据本地策略, helloservice2 有义务为 PGTURL=http://helloservice/proxy 服务 (PGTURL 用于表示一个 Proxy 服务 ) ,于是它传递数据给 helloservice 。这样, helloservice 便完成一个代理者的角色,协助 User 返回他想要的数据。

 
   代理认证模式非常有用,它也是 CAS 协议 v2 的一个最大的变化,这种模式非常适合在复杂的业务领域中应用 SSO 。因为,以前我们实施 SSO 的时候,都是假定以 IE User 为 SSO 的访问者,忽视了业务系统作为 SSO 的访问者角色。
 

2 CAS安全性介绍

  CAS 的安全性是一个非常重要的 Topic  CAS  v1  v3 ,都很依赖于 SSL ,它假定了这样一个事实,用户在一个非常不安全的网络环境中使用 SSO  Hacker  Sniffer 会很容易抓住所有的 Http Traffic ,包括通过 Http 传送的密码甚至 Ticket 票据。

TGC/PGT 安全性

       对于一个 CAS 用户来说,最重要是要保护它的 TGC ,如果 TGC 不慎被 CAS Server 以外的实体获得, Hacker 能够找到该 TGC ,然后冒充 CAS 用户访问所有授权资源。
       SSO 的安全性问题比普通应用的安全性还要严重,因为 SSO 存在一种门槛效应。以前即使 Hacker 能够截获用户在 Web 应用 A 的密码,它也未必能知道用户在 Web 应用 B 的密码,但 SSO 让 Hacker 只需要截获 TGC( 突破了门槛 ) ,即能访问所有与该用户相关的所有应用系统。
       PGT 跟 TGC 的角色是一样的,如果被 Hacker 获得,后果可想而知。
       从基础模式可以看出, TGC 是 CAS Server 通过 SSL 方式发送给终端用户,因此,要截取 TGC 难度非常大,从而确保 CAS 的安全性。
       因此,某些人认为 CAS 可以不使用 SSL 的想法需要更正一下, CAS 的传输安全性仅仅依赖与 SSL 。
       跟 Kerberos 一样 TGT , TGC 也有自己的存活周期。下面是 CAS 的 web.xml 中,通过 grantingTimeout 来设置 CAS TGC 存活周期的参数,参数默认是 120 分钟,在合适的范围内设置最小值,太短,会影响 SSO 体验,太长,会增加安全性风险。
    <context-param>
        <param-name>edu.yale.its.tp.cas.grantingTimeout</param-name>
        <param-value>7200</param-value>
    </context-param>
TGC 面临的风险主要并非传输窃取。比如你登陆了之后,没有 Logout ,离开了电脑,别人就可以打开你的浏览器,直接访问你授权访问的应用 ) ,设置一个 TGC 的有效期,可以减少被别人盗用,或者被 Hacker 入侵你的电脑直接获取你系统目录下的 TGC Cookie 。
Service Ticket/Proxy Ticket 安全性
       首要明白, Service Ticket 是通过 Http 传送的,以为着所网络中的其他人可以 Sniffer 到其他人的 Ticket 。
CAS 协议从几个方面让 Service Ticket 变得更加安全。
l         Service Ticket 只能使用一次。
CAS 协议规定,无论 Service Ticket 验证是否成功, CAS Server 都会将服务端的缓存中清除该 Ticket ,从而可以确保一个 Service Ticket 被使用两次。
l         Service Ticket 在一段时间内失效。
假设用户拿到 Service Ticket 之后,他请求 helloservice 的过程又被中断了, Service Ticket 就被空置了,事实上,此时, Service Ticket 仍然有效。 CAS 规定 Service Ticket 只能存活一定的时间,然后 CAS Server 会让它失效。通过在 web.xml 中配置下面的参数,可以让 Service Ticket 在多少秒内失效。
<context-param>
<param-name>edu.yale.its.tp.cas.serviceTimeout</param-name>
<param-value>300</param-value>
</context-param>
       该参数在业务应用的条件范围内,越小越安全。
l         Service Ticket 是基于随机数生成的。
Service Ticket 必须足够随机,如果 Service Ticket 生成规则被猜出(如果你使用了 ST+Helloservice+ 自增序列的方式, Hacker 就可以构造下一个 Ticket ), Hacker 就等于绕过 CAS 认证,直接访问所有服务。

3 Yale cas安装

看了网上很多CAS安装的步骤,结合自己的经验总结如下:

一、配置Tomcat,启用SSL协议。

1.在CAS要安装的机器上(也就是服务端)为Tomcat生成用于SSL通讯的密钥:keytool -genkey -alias tomcat -keyalg RSA,输入密钥密码和相应参数,(注意:第一个参数CN一定要输入CAS安装机器名,其他参数就随便了),结果是在用户目录中创建了名为.keystore的密钥文件。

2.从服务端导出密钥文件:keytool -export -file server.crt -alias tomcat,输入上一步中的密码,结果在当前目录生成server.crt密钥文件。(注意:这个文件是要导入客户端的JVM上的)

3.为客户端的JVM导入密钥:keytool -import -keystore $JAVA_HOME/jre/lib/security/cacerts -file server.crt -alias tomcat,输入密码(注意:这里的密码不是上面设定的密码,而是changeit),将创建cacerts文件。

4.修改服务端的Tomcat配置文件server.xml,去掉对于SSL的注释,即开放8443端口,注意这里需要在connector字段中加入keystorePass="password"参数,password即为上面几步中涉及到的密码,keystoreFile=".keystorePath",.keystorePath即为在第一步中生成的文件.keystore的全路径,如/usr/java/bin/.keystore。

5.启动Tomcat,测试https://server:8443/是否是需经过验证方可访问(注意:server为服务端的IP地址或机器名)

二、部署CAS Server 2.0.12到Tomcat

1.一种简单的方法是将下载包中的cas.war文件直接复制到Tomcat的webapps目录下。

2.另外一种方法,从sourceforge上找到ESUP-Portail CAS Generic Handler项目,利用esup-cas-quick-start生成一个最简的TOMCAT,详见我的下一篇文章。

3.启动Tomcat,测试https://server:8443/cas,是否可访问CAS主页面(注意:server为服务端的IP地址或机器名)

三、部署CAS Client 2.0.11到Servlet-Examples

1.利用Servlet-Examples实例进行测试,将下载包中的casclient.jar文件复制到Servlet-Examples中WEB-INF目录的lib下,这里需要手工建立lib目录。

2.修改Servlet-Examples的配置文件web.xml,加入以下的过滤器:

<!-- CAS Filters -->
    <filter>
        <filter-name>CASFilter</filter-name>
        <filter-class>edu.yale.its.tp.cas.client.filter.CASFilter</filter-class>
        <init-param>
            <param-name>edu.yale.its.tp.cas.client.filter.loginUrl</param-name>
            <param-value>https://server:8443/cas/login</param-value>
        </init-param><!--这里的server是服务端的IP-->
        <init-param>
            <param-name>edu.yale.its.tp.cas.client.filter.validateUrl</param-name>
            <param-value>https://serName:8443/cas/proxyValidate</param-value>
        </init-param><!--这里的serName是服务端的主机名,而且必须是-->
        <init-param>
          <param-name>edu.yale.its.tp.cas.client.filter.serverName</param-name>
          <param-value>client:port</param-value><!--client:port就是需要CAS需要拦截的地址和端口,一般就是这个TOMCAT所启动的IP和port-->
        </init-param>
    </filter>
    
    <filter-mapping>
        <filter-name>CASFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

四、测试

1.启动Tomcat,定位到Servlet-Examples应用,点击Execute;

2.浏览器跳转至CAS登录首页,注意在URL中已经附上了Servlet-Examples的Service名

3.输入用户名和密码,这里没有对其验证条件做修改,因此只要用户名和密码相同即可通过验证。

4.验证通过后浏览器又重新定位至Servlet-Examples并显示该Servlet的内容。

5.点击Http Header的Servlet应用,可以看到里面对当前用户的用户名信息做了记录。

6.以后访问Servlet-Examples应用都无需再次输入用户名和密码了。

至此,CAS Server和Client已经在Tomcat上成功部署与配置,并达到了预期的SSO效果。

 

4  yale cas 配置谈

在配置YALE 的CAS里面,走了不少弯路,到最后,终于搞好了.因此写了一个教程.希望再次配置的人能少走弯路.
TOMCAT :tomcat-5.5.15版~~~忘记了,反正是当前最新的版本
JDK:1.5.06
环境变量要设好.
第一次发帖~~~~


1.        启用TOMCAT的SSL
把.keystore文件复制到TOMCAT的CONF目录下面。
在TOMCAT的主目录的CONF目录下面,修改server.xml文件,加上以下代码
<Connector port="8443" maxHttpHeaderSize="8192"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" disableUploadTimeout="true"
               acceptCount="100" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS"
               keystoreFile="conf/.keystore"
               keystorePass="changeit"
               />
其中的keystoreFile是证书库文件,keystorePass是访问此证书库文件的密码。
注:keystore文件可以用以下方法生成。
Keytool –genkey –alias hostname –keyalg RSA(在接下来的第一项是名称,记住最好是和hostname同一名称)执行此操作会在用户的当前目录(user.home)下产生一个名为.keystore的文件。如果已经有了,将自动把新产生的KEY放进文件里面(此次的hostname是运行CAS服务器的名字.不要搞错,否则会在以后验证出错的.如果你是在本地测试,则用localhost就OK了)
2.        导入证书文件到各个应用的JRE的JVM里面
首先产生一个证书文件,用以下方法:
Keytool –export –alias hostname –file filename.cer
这样就在用户当前产生了一个名为filename.cer的文件
接下来就把此文件导入到各应用的的JVM里面
Keytool –import –alias hostname –file filename –keystore {java_home}/jre/lib/security/cacerts
注:如果你的JAVA_HOME里面有空格,请用引号括住。
3.        把cas.war包复制到TOMCAT的WEBAPPS下面,然后用http://localhost:8080/cas/login就可以访问登陆了

改写验证方法。CAS的默认方验证方法是用户名和密码相同,如果想改为自己的验证方式,如何做呢?你只需复制以下代码,然后在适当的地方插入你的验证代码就OK了。
package org.jasig.cas.authentication.handler.support;

import org.jasig.cas.authentication.principal.UsernamePasswordCredentials;
import org.springframework.util.StringUtils;

public final class classname extends
    AbstractUsernamePasswordAuthenticationHandler {

    public boolean authenticateUsernamePasswordInternal(
        final UsernamePasswordCredentials credentials) {
        final String username = credentials.getUsername();
        final String password = credentials.getPassword();

        if (在此插入你的验证代码) {
            getLog().debug(
                "User [" + username + "] was successfully authenticated.");
            return true;
        }

        getLog().debug("User [" + username + "] failed authentication");

        return false;
    }

    protected void afterPropertiesSetInternal() throws Exception {
        super.afterPropertiesSetInternal();
        getLog()
            .warn(
                this.getClass().getName()
                    + " is only to be used in a testing environment.  NEVER enable this in a production environment.");
    }
}

然后,修改deployerConfigContext.xml(在CAS的WEB-INF目录下面)
找到
<bean                        class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" />
把class改为你自己写的验证的类就OK了。
到此,服务器端的配置就完成了。
接下来是各个应用的配置:

4.(以JAVA的配置为例子)把casclient.jar包复制到应用的lib目录下面,如果没有就创建它。然后再在应用的部署描述文件里面(web.xml)加上filter。如下:
<filter>
    <filter-name>CAS Filter</filter-name>
    <filter-class>edu.yale.its.tp.cas.client.filter.CASFilter</filter-class>
    <init-param>
      <param-name>edu.yale.its.tp.cas.client.filter.loginUrl</param-name>
      <param-value>https://casServerhost:8443/cas/login</param-value>
    </init-param>
    <init-param>
      <param-name>edu.yale.its.tp.cas.client.filter.validateUrl</param-name>
      <param-value>https:// casServerhost:8443/cas/proxyValidate</param-value>
    </init-param>
    <init-param>
      <param-name>edu.yale.its.tp.cas.client.filter.serverName</param-name>
      <param-value>localhost:8080</param-value>
    </init-param>
  </filter>
<filter-mapping>
  <filter-name>CAS Filter</filter-name>
  <url-pattern>/servlet/*</url-pattern>
</filter-mapping>

Localhost是指各个应用的服务器的名字
casServerHost是指cas服务器的名字
其中的filter-mapping就是配置哪些资源是需要通过CAS验证的。可以配置多个。

4.        配置语言包
在cas里面的WEB-INF/classes下面添加不同的语言包,然后再在/WEB-INF/view/jsp/default/ui/includes的top.jsp文件顶部加入<%@ page contentType="text/html; charset=gbk" language="java" %>便可以了。

一些错误信息:
1.        keytool 认证未输入别名 <mykey> 已经存在
这是因为你已经导入了一信任证书。在进行keytool –import的时候,如果没有指定别名,则系统默任导入的证书的名字为mykey,所以,可以先删除此证书keytool –delete –alias mykey –keystore {java_home}/jre/lib/security/cacerts
然后再从新导入,或者指定别名导入keytool –import –alias name –keystore {java_home}/jre/lib/security/cacerts

2.        java.io.IOException: Keystore was tampered with, or password was incorrect
这个很可能你的keystore文件已经被修改了,密码已经更改,直接删除这个文件,再从新生成就可以了
3.        javax.servlet.ServletException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
这是因为你没有在应用端导入证书。在应用端执行keytool –import –alias name –file filename.cer便可以。其中的name.cer是在前面的用keytool –export导出的cer文件

你可能感兴趣的:(cas)