首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

应用 CAS 在 Tomcat 中实现单点登录

2012-06-28 
使用 CAS 在 Tomcat 中实现单点登录CAS Client 与受保护的客户端应用部署在一起,以 Filter 方式保护受保护

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

CAS Client 与受保护的客户端应用部署在一起,以 Filter 方式保护受保护的资源。对于访问受保护资源的每个 Web 请求,CAS Client 会分析该请求的 Http 请求中是否包含 Service Ticket,如果没有,则说明当前用户尚未登录,于是将请求重定向到指定好的 CAS Server 登录地址,并传递 Service (也就是要访问的目的资源地址),以便登录成功过后转回该地址。用户在第 3 步中输入认证信息,如果登录成功,CAS Server 随机产生一个相当长度、唯一、不可伪造的 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 官方网站上的相关文档。

回页首

[java]

  • 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

    [html]
  • <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 />,我们可以将其注释掉,换成我们希望的一个 AuthenticationHandler,比如,使用QueryDatabaseAuthenticationHandler 或 SearchModeSearchDatabaseAuthenticationHandler 可以分别选取清单 5 或清单 6 的配置。


    清单 5. 使用 QueryDatabaseAuthenticationHandler

    [html]
  • ??????????????????
  • <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

    [html]
  • <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

    [html]
  • <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. 指定具体加密器类

    [html]
  • <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 目录。

    [html]

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


    回页首

    [html]

  • <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[java]

  • //?以下两者都可以??
  • session.getAttribute(CASFilter.CAS_FILTER_USER);??
  • session.getAttribute("edu.yale.its.tp.cas.client.filter.user");??


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


    清单 12. 通过 JSTL 获取登录用户名

    [java]
  • CASFilterRequestWrapper??reqWrapper=new?CASFilterRequestWrapper(request);??
  • out.println("The?logon?user:"?+?reqWrapper.getRemoteUser());??


    回页首

    [java]

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

      打开浏览器,访问

        登录成功后,再重定向到 casTest1 的 WelcomePage 页面,如图所示:


      图 3. 登录后访问 casTest1 的效果
      应用 CAS 在 Tomcat 中实现单点登录

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

        再在同一个浏览器的地址栏中输入

          重新打开一个浏览器窗口,先输入?http://domainC:8080/casTest2/WelcomePage?,系统要求登录,在登录成功过后,正确显示 casTest2 的页面。之后再在地址栏重新输入http://domainB:8080/casTest1/WelcomePage?,会直接显示 casTest1 的页面而无需再次登录

        http://blog.csdn.net/gs250220/article/details/7107503

  • 热点排行