使用 CAS 在 Tomcat 中实现单点登陆

简介: 单点登陆(Single Sign On , 简称 SSO )是目前比较流行的服务于企业业务整合的解决方案之一, SSO 使得在多个应用系统中,用户只须要登陆一次就能够访问全部相互信任的应用系统。CAS(Central Authentication Service)是一款不错的针对 Web 应用的单点登陆框架,本文介绍了 CAS 的原理、协议、在 Tomcat 中的配置和使用,对于采用 CAS 实现轻量级单点登陆解决方案的入门读者具备必定指导做用。html

本文的标签:  单点登录安全访问控制java

发布日期: 2008 年 4 月 10 日 
级别: 初级 
访问状况 22519 次浏览 
建议: 1 (查看或添加评论)web

1 star 2 stars 3 stars 4 stars 5 stars  平均分 (共 17 个评分 )

CAS 介绍spring

CAS 是 Yale 大学发起的一个开源项目,旨在为 Web 应用系统提供一种可靠的单点登陆方法,CAS 在 2004 年 12 月正式成为 JA-SIG 的一个项目。CAS 具备如下特色:sql

  • 开源的企业级单点登陆解决方案。
  • CAS Server 为须要独立部署的 Web 应用。
  • CAS Client 支持很是多的客户端(这里指单点登陆系统中的各个 Web 应用),包括 Java, .Net, PHP, Perl, Apache, uPortal, Ruby 等。

CAS 原理和协议数据库

从结构上看,CAS 包含两个部分: CAS Server 和 CAS Client。CAS Server 须要独立部署,主要负责对用户的认证工做;CAS Client 负责处理对客户端受保护资源的访问请求,须要登陆时,重定向到 CAS Server。图1 是 CAS 最基本的协议过程:apache


图 1. CAS 基础协议
CAS 基础协议 

CAS Client 与受保护的客户端应用部署在一块儿,以 Filter 方式保护受保护的资源。对于访问受保护资源的每一个 Web 请求,CAS Client 会分析该请求的 Http 请求中是否包含 Service Ticket,若是没有,则说明当前用户还没有登陆,因而将请求重定向到指定好的 CAS Server 登陆地址,并传递 Service (也就是要访问的目的资源地址),以便登陆成功事后转回该地址。用户在第 3 步中输入认证信息,若是登陆成功,CAS Server 随机产生一个至关长度、惟1、不可伪造的 Service Ticket,并缓存以待未来验证,以后系统自动重定向到 Service 所在地址,并为客户端浏览器设置一个 Ticket Granted Cookie(TGC),CAS Client 在拿到 Service 和新产生的 Ticket 事后,在第 5,6 步中与 CAS Server 进行身份合适,以确保 Service Ticket 的合法性。浏览器

在该协议中,全部与 CAS 的交互均采用 SSL 协议,确保,ST 和 TGC 的安全性。协议工做过程当中会有 2 次重定向的过程,可是 CAS Client 与 CAS Server 之间进行 Ticket 验证的过程对于用户是透明的。缓存

另外,CAS 协议中还提供了 Proxy (代理)模式,以适应更加高级、复杂的应用场景,具体介绍能够参考 CAS 官方网站上的相关文档。tomcat

准备工做

本文中的例子以 tomcat5.5 为例进行讲解,下载地址:

http://tomcat.apache.org/download-55.cgi

到 CAS 官方网站下载 CAS Server 和 Client,地址分别为:

http://www.ja-sig.org/downloads/cas/cas-server-3.1.1-release.zip

http://www.ja-sig.org/downloads/cas-clients/cas-client-java-2.1.1.zip

部署 CAS Server

CAS Server 是一套基于 Java 实现的服务,该服务以一个 Java Web Application 单独部署在与 servlet2.3 兼容的 Web 服务器上,另外,因为 Client 与 CAS Server 之间的交互采用 Https 协议,所以部署 CAS Server 的服务器还须要支持 SSL 协议。当 SSL 配置成功事后,像普通 Web 应用同样将 CAS Server 部署在服务器上就能正常运行了,不过,在真正使用以前,还须要扩展验证用户的接口。

在 Tomcat 上部署一个完整的 CAS Server 主要按照如下几个步骤:

配置 Tomcat 使用 Https 协议

若是但愿 Tomcat 支持 Https,主要的工做是配置 SSL 协议,其配置过程和配置方法能够参考 Tomcat 的相关文档。不过在生成证书的过程当中,会有须要用到主机名的地方,CAS 建议不要使用 IP 地址,而要使用机器名或域名。

部署 CAS Server

CAS Server 是一个 Web 应用包,将前面下载的 cas-server-3.1.1-release.zip 解开,把其中的 cas-server-webapp-3.1.1.war 拷贝到 tomcat的 webapps 目录,并改名为 cas.war。因为前面已配置好 tomcat 的 https 协议,能够从新启动 tomcat,而后访问:https://localhost:8443/cas ,若是能出现正常的 CAS 登陆页面,则说明 CAS Server 已经部署成功。

虽然 CAS Server 已经部署成功,但这只是一个缺省的实现,在实际使用的时候,还须要根据实际概况作扩展和定制,最主要的是扩展认证 (Authentication) 接口和 CAS Server 的界面。

扩展认证接口

CAS Server 负责完成对用户的认证工做,它会处理登陆时的用户凭证 (Credentials) 信息,用户名/密码对是最多见的凭证信息。CAS Server 可能须要到数据库检索一条用户账号信息,也可能在 XML 文件中检索用户名/密码,还可能经过 LDAP Server 获取等,在这种状况下,CAS 提供了一种灵活但统一的接口和实现分离的方式,实际使用中 CAS 采用哪一种方式认证是与 CAS 的基本协议分离开的,用户能够根据认证的接口去定制和扩展。

扩展 AuthenticationHandler

CAS 提供扩展认证的核心是 AuthenticationHandler 接口,该接口定义如清单 1 下:


清单 1. AuthenticationHandler定义
public interface AuthenticationHandler {
    /**
     * Method to determine if the credentials supplied are valid.
     * @param credentials The credentials to validate.
     * @return true if valid, return false otherwise.
     * @throws AuthenticationException An AuthenticationException can contain
     * details about why a particular authentication request failed.
     */
    boolean authenticate(Credentials credentials) throws AuthenticationException;
/**
     * Method to check if the handler knows how to handle the credentials
     * provided. It may be a simple check of the Credentials class or something
     * more complicated such as scanning the information contained in the
     * Credentials object. 
     * @param credentials The credentials to check.
     * @return true if the handler supports the Credentials, false othewrise.
     */
    boolean supports(Credentials credentials);
}

该接口定义了 2 个须要实现的方法,supports ()方法用于检查所给的包含认证信息的Credentials 是否受当前 AuthenticationHandler 支持;而 authenticate() 方法则担当验证认证信息的任务,这也是须要扩展的主要方法,根据状况与存储合法认证信息的介质进行交互,返回 boolean 类型的值,true 表示验证经过,false 表示验证失败。

CAS3中还提供了对AuthenticationHandler 接口的一些抽象实现,好比,可能须要在执行authenticate() 方法先后执行某些其余操做,那么可让本身的认证类扩展自清单 2 中的抽象类:


清单 2. AbstractPreAndPostProcessingAuthenticationHandler定义
public abstract class AbstractPreAndPostProcessingAuthenticationHandler 
                                           implements AuthenticateHandler{
    protected Log log = LogFactory.getLog(this.getClass());
    protected boolean preAuthenticate(final Credentials credentials) {
        return true;
    }
    protected boolean postAuthenticate(final Credentials credentials,
        final boolean authenticated) {
        return authenticated;
    }
    public final boolean authenticate(final Credentials credentials)
        throws AuthenticationException {
        if (!preAuthenticate(credentials)) {
            return false;
        }
        final boolean authenticated = doAuthentication(credentials);
        return postAuthenticate(credentials, authenticated);
    }
    protected abstract boolean doAuthentication(final Credentials credentials) 
throws AuthenticationException;
}

AbstractPreAndPostProcessingAuthenticationHandler 类新定义了 preAuthenticate() 方法和 postAuthenticate() 方法,而实际的认证工做交由 doAuthentication() 方法来执行。所以,若是须要在认证先后执行一些额外的操做,能够分别扩展 preAuthenticate()和 ppstAuthenticate() 方法,而 doAuthentication() 取代 authenticate() 成为了子类必需要实现的方法。

因为实际运用中,最经常使用的是用户名和密码方式的认证,CAS3 提供了针对该方式的实现,如清单 3 所示:


清单 3. AbstractUsernamePasswordAuthenticationHandler 定义
public abstract class AbstractUsernamePasswordAuthenticationHandler extends 
                       AbstractPreAndPostProcessingAuthenticationHandler{
...
 protected final boolean doAuthentication(final Credentials credentials)
 throws AuthenticationException {
 return authenticateUsernamePasswordInternal((UsernamePasswordCredentials) credentials);
 }
 protected abstract boolean authenticateUsernamePasswordInternal(
        final UsernamePasswordCredentials credentials) throws AuthenticationException;   
protected final PasswordEncoder getPasswordEncoder() {
 return this.passwordEncoder;
 }
public final void setPasswordEncoder(final PasswordEncoder passwordEncoder) {
 this.passwordEncoder = passwordEncoder;
    }
...
}

基于用户名密码的认证方式可直接扩展自 AbstractUsernamePasswordAuthenticationHandler,验证用户名密码的具体操做经过实现 authenticateUsernamePasswordInternal() 方法达到,另外,一般状况下密码会是加密过的,setPasswordEncoder() 方法就是用于指定适当的加密器。

从以上清单中能够看到,doAuthentication() 方法的参数是 Credentials 类型,这是包含用户认证信息的一个接口,对于用户名密码类型的认证信息,能够直接使用 UsernamePasswordCredentials,若是须要扩展其余类型的认证信息,须要实现Credentials接口,而且实现相应的 CredentialsToPrincipalResolver 接口,其具体方法能够借鉴 UsernamePasswordCredentials 和 UsernamePasswordCredentialsToPrincipalResolver。

JDBC 认证方法

用户的认证信息一般保存在数据库中,所以本文就选用这种状况来介绍。将前面下载的 cas-server-3.1.1-release.zip 包解开后,在 modules 目录下能够找到包 cas-server-support-jdbc-3.1.1.jar,其提供了经过 JDBC 链接数据库进行验证的缺省实现,基于该包的支持,咱们只须要作一些配置工做便可实现 JDBC 认证。

JDBC 认证方法支持多种数据库,DB2, Oracle, MySql, Microsoft SQL Server 等都可,这里以 DB2 做为例子介绍。而且假设DB2数据库名: CASTest,数据库登陆用户名: db2user,数据库登陆密码: db2password,用户信息表为: userTable,该表包含用户名和密码的两个数据项分别为 userName 和 password。

1. 配置 DataStore

打开文件 %CATALINA_HOME%/webapps/cas/WEB-INF/deployerConfigContext.xml,添加一个新的 bean 标签,对于 DB2,内容如清单 4 所示:


清单 4. 配置 DataStore
<bean id="casDataSource" class="org.apache.commons.dbcp.BasicDataSource">
     <property name="driverClassName">
          <value>com.ibm.db2.jcc.DB2Driver</value>
     </property>
     <property name="url">
          <value>jdbc:db2://9.125.65.134:50000/CASTest</value>
     </property>
     <property name="username">
          <value>db2user</value>
     </property>
     <property name="password">
          <value>db2password</value>
     </property>
</bean>

其中 id 属性为该 DataStore 的标识,在后面配置 AuthenticationHandler 会被引用,另外,须要提供 DataStore 所必需的数据库驱动程序、链接地址、数据库登陆用户名以及登陆密码。

2. 配置 AuthenticationHandler

在 cas-server-support-jdbc-3.1.1.jar 包中,提供了 3 个基于 JDBC 的 AuthenticationHandler,分别为 BindModeSearchDatabaseAuthenticationHandler, QueryDatabaseAuthenticationHandler, SearchModeSearchDatabaseAuthenticationHandler。其中 BindModeSearchDatabaseAuthenticationHandler 是用所给的用户名和密码去创建数据库链接,根据链接创建是否成功来判断验证成功与否;QueryDatabaseAuthenticationHandler 经过配置一个 SQL 语句查出密码,与所给密码匹配;SearchModeSearchDatabaseAuthenticationHandler 经过配置存放用户验证信息的表、用户名字段和密码字段,构造查询语句来验证。

使用哪一个 AuthenticationHandler,须要在 deployerConfigContext.xml 中设置,默认状况下,CAS 使用一个简单的 username=password 的 AuthenticationHandler,在文件中能够找到以下一行:<bean class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePassword
AuthenticationHandler" />,咱们能够将其注释掉,换成咱们但愿的一个 AuthenticationHandler,好比,使用QueryDatabaseAuthenticationHandler 或 SearchModeSearchDatabaseAuthenticationHandler 能够分别选取清单 5 或清单 6 的配置。


清单 5. 使用 QueryDatabaseAuthenticationHandler
<bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
 <property name="dataSource" ref=" casDataSource " />
 <property name="sql" 
       value="select password from userTable where lower(userName) = lower(?)" />
</bean>


清单 6. 使用 SearchModeSearchDatabaseAuthenticationHandler
<bean id="SearchModeSearchDatabaseAuthenticationHandler"
      class="org.jasig.cas.adaptors.jdbc.SearchModeSearchDatabaseAuthenticationHandler"
      abstract="false" singleton="true" lazy-init="default" 
                       autowire="default" dependency-check="default">
  <property  name="tableUsers">
   <value>userTable</value>
  </property>
  <property name="fieldUser">
   <value>userName</value>
  </property>
  <property name="fieldPassword">
   <value>password</value>
  </property>
  <property name="dataSource" ref=" casDataSource " />
</bean>

另外,因为存放在数据库中的密码一般是加密过的,因此 AuthenticationHandler 在匹配时须要知道使用的加密方法,在 deployerConfigContext.xml 文件中咱们能够为具体的 AuthenticationHandler 类配置一个 property,指定加密器类,好比对于 QueryDatabaseAuthenticationHandler,能够修改如清单7所示:


清单 7. 添加 passwordEncoder
<bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
  <property name="dataSource" ref=" casDataSource " />
  <property name="sql" 
           value="select password from userTable where lower(userName) = lower(?)" />
  <property  name="passwordEncoder"  ref="myPasswordEncoder"/>
</bean>

其中 myPasswordEncoder 是对清单 8 中设置的实际加密器类的引用:


清单 8. 指定具体加密器类
<bean id="passwordEncoder" 
            class="org.jasig.cas.authentication.handler.MyPasswordEncoder"/>

这里 MyPasswordEncoder 是根据实际状况本身定义的加密器,实现 PasswordEncoder 接口及其 encode() 方法。

3. 部署依赖包

在以上配置完成之后,须要拷贝几个依赖的包到 cas 应用下,包括:

  • 将 cas-server-support-jdbc-3.1.1.jar 拷贝到 %CATALINA_HOME%/webapps/cas/ WEB-INF/lib 目录。
  • 数据库驱动,因为这里使用 DB2,将 %DB2_HOME%/java 目录下的 db2java.zip (改名为 db2java.jar), db2jcc.jar, db2jcc_license_cu.jar 拷贝到 %CATALINA_HOME%/webapps/cas/WEB-INF/lib 目录。对于其余数据库,一样将相应数据库驱动程序拷贝到该目录。
  • DataStore 依赖于 commons-collections-3.2.jar, commons-dbcp-1.2.1.jar, commons-pool-1.3.jar,须要到 apache 网站的 Commons 项目下载以上 3 个包放进 %CATALINA_HOME%/webapps/cas/WEB-INF/lib 目录。

扩展 CAS Server 界面

CAS 提供了 2 套默认的页面,分别为“ default ”和“ simple ”,分别在目录“ cas/WEB-INF/view/jsp/default ”和“ cas/WEB-INF/view/jsp/simple ”下。其中 default 是一个稍微复杂一些的页面,使用 CSS,而 simple 则是能让 CAS 正常工做的最简化的页面。

在部署 CAS 以前,咱们可能须要定制一套新的 CAS Server 页面,添加一些个性化的内容。最简单的方法就是拷贝一份 default 或 simple 文件到“ cas/WEB-INF/view/jsp ”目录下,好比命名为 newUI,接下来是实现和修改必要的页面,有 4 个页面是必须的:

  • casConfirmView.jsp: 当用户选择了“ warn ”时会看到的确认界面
  • casGenericSuccess.jsp: 在用户成功经过认证而没有目的Service时会看到的界面
  • casLoginView.jsp: 当须要用户提供认证信息时会出现的界面
  • casLogoutView.jsp: 当用户结束 CAS 单点登陆系统会话时出现的界面

CAS 的页面采用 Spring 框架编写,对于不熟悉 Spring 的使用者,在修改以前须要熟悉该框架。

页面定制完事后,还须要作一些配置从而让 CAS 找到新的页面,拷贝“ cas/WEB-INF/classes/default_views.properties ”,重命名为“ cas/WEB-INF/classes/ newUI_views.properties ”,并修改其中全部的值到相应新页面。最后是更新“ cas/WEB-INF/cas-servlet.xml ”文件中的 viewResolver,将其修改成如清单 9 中的内容。


清单 9. 指定 CAS 页面
<bean id="viewResolver" 
     class="org.springframework.web.servlet.view.ResourceBundleViewResolver" p:order="0">
    <property name="basenames">
        <list>
            <value>${cas.viewResolver.basename}</value>
            <value> newUI_views</value>
        </list>
    </property>
</bean>

部署客户端应用

单点登陆的目的是为了让多个相关联的应用使用相同的登陆过程,本文在讲解过程当中构造 2个简单的应用,分别以 casTest1 和 casTest2 来做为示例,它们均只有一个页面,显示欢迎信息和当前登陆用户名。这 2 个应用使用同一套登陆信息,而且只有登陆过的用户才能访问,经过本文的配置,实现单点登陆,即只需登陆一次就能够访问这两个应用。

与 CAS Server 创建信任关系

假设 CAS Server 单独部署在一台机器 A,而客户端应用部署在机器 B 上,因为客户端应用与 CAS Server 的通讯采用 SSL,所以,须要在 A 与 B 的 JRE 之间创建信任关系。

首先与 A 机器同样,要生成 B 机器上的证书,配置 Tomcat 的 SSL 协议。其次,下载http://blogs.sun.com/andreas/entry/no_more_unable_to_find 的 InstallCert.java,运行“ java InstallCert compA:8443 ”命令,而且在接下来出现的询问中输入 1。这样,就将 A 添加到了 B 的 trust store 中。若是多个客户端应用分别部署在不一样机器上,那么每一个机器都须要与 CAS Server 所在机器创建信任关系。

配置 CAS Filter

准备好应用 casTest1 和 casTest2 事后,分别部署在 B 和 C 机器上,因为 casTest1 和casTest2,B 和 C 彻底等同,咱们以 casTest1 在 B 机器上的配置作介绍,假设 A 和 B 的域名分别为 domainA 和 domainB。

将 cas-client-java-2.1.1.zip 更名为 cas-client-java-2.1.1.jar 并拷贝到 casTest1/WEB-INF/lib目录下,修改 web.xml 文件,添加 CAS Filter,如清单 10 所示:


清单 10. 添加 CAS Filter
<web-app>
  ...
  <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://domainA:8443/cas/login</param-value>
    </init-param>
    <init-param>
      <param-name>edu.yale.its.tp.cas.client.filter.validateUrl</param-name>
      <param-value>https://domainA:8443/cas/serviceValidate</param-value>
    </init-param>
    <init-param>
      <param-name>edu.yale.its.tp.cas.client.filter.serverName</param-name>
      <param-value>domainB:8080</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>CAS Filter</filter-name>
    <url-pattern>/protected-pattern/*</url-pattern>
  </filter-mapping>
  ...
</web-app>

对于全部访问知足 casTest1/protected-pattern/ 路径的资源时,都要求到 CAS Server 登陆,若是须要整个 casTest1 均受保护,能够将 url-pattern 指定为“/*”。

从清单 10 能够看到,咱们能够为 CASFilter 指定一些参数,而且有些是必须的,表格 1 和表格 2 中分别是必需和可选的参数:


表格 1. CASFilter 必需的参数
参数名 做用
edu.yale.its.tp.cas.client.filter.loginUrl 指定 CAS 提供登陆页面的 URL
edu.yale.its.tp.cas.client.filter.validateUrl 指定 CAS 提供 service ticket 或 proxy ticket 验证服务的 URL
edu.yale.its.tp.cas.client.filter.serverName 指定客户端的域名和端口,是指客户端应用所在机器而不是 CAS Server 所在机器,该参数或 serviceUrl 至少有一个必须指定
edu.yale.its.tp.cas.client.filter.serviceUrl 该参数指定事后将覆盖 serverName 参数,成为登陆成功事后重定向的目的地址


表格 2. CASFilter 可选参数
参数名 做用
edu.yale.its.tp.cas.client.filter.proxyCallbackUrl 用于当前应用须要做为其余服务的代理(proxy)时获取 Proxy Granting Ticket 的地址
edu.yale.its.tp.cas.client.filter.authorizedProxy 用于容许当前应用从代理处获取 proxy tickets,该参数接受以空格分隔开的多个 proxy URLs,但实际使用只须要一个成功便可。当指定该参数事后,须要修改 validateUrl 到 proxyValidate,而再也不是 serviceValidate
edu.yale.its.tp.cas.client.filter.renew 若是指定为 true,那么受保护的资源每次被访问时均要求用户从新进行验证,而无论以前是否已经经过
edu.yale.its.tp.cas.client.filter.wrapRequest 若是指定为 true,那么 CASFilter 将从新包装 HttpRequest,而且使 getRemoteUser() 方法返回当前登陆用户的用户名
edu.yale.its.tp.cas.client.filter.gateway 指定 gateway 属性

传递登陆用户名

CAS 在登陆成功事后,会给浏览器回传 Cookie,设置新的到的 Service Ticket。但客户端应用拥有各自的 Session,咱们要怎么在各个应用中获取当前登陆用户的用户名呢?CAS Client 的 Filter 已经作好了处理,在登陆成功后,就能够直接从 Session 的属性中获取,如清单 11 所示:


清单 11. 在 Java 中经过 Session 获取登陆用户名
// 如下二者均可以
session.getAttribute(CASFilter.CAS_FILTER_USER);
session.getAttribute("edu.yale.its.tp.cas.client.filter.user");

在 JSTL 中获取用户名的方法如清单 12 所示:


清单 12. 经过 JSTL 获取登陆用户名
<c:out value="${sessionScope[CAS:'edu.yale.its.tp.cas.client.filter.user']}"/>

另外,CAS 提供了一个 CASFilterRequestWrapper 类,该类继承自HttpServletRequestWrapper,主要是重写了 getRemoteUser() 方法,只要在前面配置 CASFilter 的时候为其设置“ edu.yale.its.tp.cas.client.filter.wrapRequest ”参数为 true,就能够经过 getRemoteUser() 方法来获取登陆用户名,具体方法如清单 13 所示:


清单 13. 经过 CASFilterRequestWrapper 获取登陆用户名
CASFilterRequestWrapper  reqWrapper=new CASFilterRequestWrapper(request);
out.println("The logon user:" + reqWrapper.getRemoteUser());

效果

在 casTest1 和 casTest2 中,都有一个简单 Servlet 做为欢迎页面 WelcomPage,且该页面必须登陆事后才能访问,页面代码如清单 14 所示:


清单 14. WelcomePage 页面代码
public class WelcomePage extends HttpServlet {
  public void doGet(HttpServletRequest request, HttpServletResponse response)
  throws IOException, ServletException
  {
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    out.println("<html>");
    out.println("<head>");
    out.println("<title>Welcome to casTest2 sample System!</title>");
    out.println("</head>");
    out.println("<body>");
    out.println("<h1>Welcome to casTest1 sample System!</h1>");
    CASFilterRequestWrapper  reqWrapper=new CASFilterRequestWrapper(request);
    out.println("<p>The logon user:" + reqWrapper.getRemoteUser() + "</p>");
    HttpSession session=request.getSession();
    out.println("<p>The logon user:" + 
                   session.getAttribute(CASFilter.CAS_FILTER_USER)  + "</p>");
    out.println("<p>The logon user:" + 
         session.getAttribute("edu.yale.its.tp.cas.client.filter.user") + "</p>");
    out.println("</body>");
    out.println("</html>");
    }
}

在上面全部配置结束事后,分别在 A, B, C上启动 cas, casTest1 和 casTest2,按照下面步骤来访问 casTest1 和 casTest2:

  1. 打开浏览器,访问 http://domainB:8080/casTest1/WelcomePage ,浏览器会弹出安全提示,接受后即转到 CAS 的登陆页面,如图 2 所示:

图 2. CAS 登陆页面
CAS 登陆页面 
  1. 登陆成功后,再重定向到 casTest1 的 WelcomePage 页面,如图 所示:

图 3. 登陆后访问 casTest1 的效果
登陆后访问 casTest1 的效果 

能够看到图 中地址栏里的地址多出了一个 ticket 参数,这就是 CAS 分配给当前应用的 ST(Service Ticket)。

  1. 再在同一个浏览器的地址栏中输入 http://domainC:8080/casTest2/WelcomePage ,系统再也不提示用户登陆,而直接出现如图 4 所示的页面,而且显示在 casTest1 中已经登陆过的用户。

图 4. 在 casTest1 中登陆事后访问 casTest2 的效果
在 casTest1 中登陆事后访问 casTest2 的效果 
  1. 从新打开一个浏览器窗口,先输入 http://domainC:8080/casTest2/WelcomePage ,系统要求登陆,在登陆成功事后,正确显示 casTest2 的页面。以后再在地址栏从新输入 http://domainB:8080/casTest1/WelcomePage ,会直接显示 casTest1 的页面而无需再次登陆。

结束语

本文介绍了 CAS 单点登陆解决方案的原理,并结合实例讲解了在 Tomcat 中使用 CAS 的配置、部署方法以及效果。CAS 是做为开源单点登陆解决方案的一个不错选择,更多的使用细节能够参考 CAS 官方网站。


参考资料

相关文章
相关标签/搜索