cas框架配置详解

 

一、概述

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

二、CAS介绍

CAS 是 Yale 大学发起的一个开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法,CAS 在 2004 年 12 月正式成为 JA-SIG 的一个项目(http://www.jasig.org)。CAS 具有以下特点:

1)开源的企业级单点登录解决方案

2)CAS Server 为需要独立部署的 Web 应用

3)CAS Client 支持非常多的客户端(指Web 应用),包括Java,.Net,PHP,Perl,Ruby 等

三、CAS原理及协议

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

 

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 官方网站上的相关文档。

 

四、CAS SERVER配置

1.准备工作

安装配置JDK、安装Tomcat7,此处不做详解。

到CAS官网下载CAS Server和Client,地址如下:

http://downloads.jasig.org/cas/cas-server-4.0.0-release.zip

http://downloads.jasig.org/cas-clients/cas-client-3.2.1-release.zip

 

2.部署

1.将下载的cas-server-4.0.0-release.zip解开,把cas-server-4.0.0/modules/cas-server-webapp-4.0.0.war拷贝到 tomcat的webapps目录,并更名为cas.war。

2.修改cas\WEB-INF\spring-configuration\ticketGrantingTicketCookieGenerator.xml文件,将属性p:cookieSecure="true" 变成 p:cookieSecure="false"(这个设置主要是让CAS不走SSL协议,详见:让CAS不用SSL也可实现跨域)

3.启动tomcat,然后访问:http://localhost:8888/cas,如果能出现正常的CAS登录页面,则说明CAS Server 已经部署成功。如下图:

 

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

 

五、CAS Client配置

将下载的cas-client-3.2.1-release.zip解开,将cas-client-3.2.1/modules/

cas-client-core-3.2.1.jar,放入你的web项目lib目录中,修改web.xml,添加如下web.xml中单点登录块配置信息:

 

Xml代码 复制代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3.     xmlns="http://java.sun.com/xml/ns/javaee"  
  4.     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  
  5.     id="WebApp_ID" version="2.5">  
  6.     <display-name>CasClientOne</display-name>  
  7.     <welcome-file-list>  
  8.         <welcome-file>index.html</welcome-file>  
  9.         <welcome-file>index.htm</welcome-file>  
  10.         <welcome-file>index.jsp</welcome-file>  
  11.         <welcome-file>default.html</welcome-file>  
  12.         <welcome-file>default.htm</welcome-file>  
  13.         <welcome-file>default.jsp</welcome-file>  
  14.     </welcome-file-list>  
  15.       
  16.     <!-- ======================== 单点登录结束 ======================== -->  
  17.     <!-- 用于单点退出,该过滤器用于实现单点登出功能,可选配置 -->  
  18.     <listener>  
  19.         <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>  
  20.     </listener>  
  21.       
  22.     <!-- 该过滤器用于实现单点登出功能,可选配置。 -->  
  23.     <filter>  
  24.         <filter-name>CAS Single Sign Out Filter</filter-name>  
  25.         <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>  
  26.     </filter>  
  27.   
  28.     <filter-mapping>  
  29.         <filter-name>CAS Single Sign Out Filter</filter-name>  
  30.         <url-pattern>/*</url-pattern>  
  31.     </filter-mapping>  
  32.   
  33.     <!-- 该过滤器负责用户的认证工作,必须启用它 -->  
  34.     <filter>  
  35.         <filter-name>CASFilter</filter-name>  
  36.         <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>  
  37.         <init-param>  
  38.             <param-name>casServerLoginUrl</param-name>  
  39.             <param-value>http://localhost:8888/cas/login</param-value><!-- cas 服务器登录 地址  http://IP:PORT/CasWebProName/login -->  
  40.         </init-param>  
  41.         <init-param>  
  42.             <!-- 这里的server是服务端的IP -->  
  43.             <param-name>serverName</param-name>  
  44.             <param-value>http://localhost:8080</param-value><!-- 客户端服务器地址   http://IP:PORT -->  
  45.         </init-param>  
  46.     </filter>  
  47.   
  48.     <filter-mapping>  
  49.         <filter-name>CASFilter</filter-name>  
  50.         <url-pattern>/*</url-pattern>  
  51.     </filter-mapping>  
  52.       
  53.     <!-- 该过滤器负责对Ticket的校验工作,必须启用它 -->  
  54.     <filter>  
  55.         <filter-name>CAS Validation Filter</filter-name>  
  56.         <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>  
  57.         <init-param>  
  58.             <param-name>casServerUrlPrefix</param-name>  
  59.             <param-value>http://localhost:8888/cas</param-value><!-- cas 服务器地址  http://IP:PORT/CasWebProName -->  
  60.         </init-param>  
  61.         <init-param>  
  62.             <param-name>serverName</param-name>  
  63.             <param-value>http://localhost:8080</param-value><!-- 客户端服务器地址   http://IP:PORT -->  
  64.         </init-param>  
  65.     </filter>  
  66.     <filter-mapping>  
  67.         <filter-name>CAS Validation Filter</filter-name>  
  68.         <url-pattern>/*</url-pattern>  
  69.     </filter-mapping>  
  70.       
  71.     <!-- 该过滤器负责实现HttpServletRequest请求的包裹, 比如允许开发者通过HttpServletRequest的getRemoteUser()方法获得SSO登录用户的登录名,可选配置。 -->  
  72.     <filter>  
  73.         <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>  
  74.         <filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>  
  75.     </filter>  
  76.     <filter-mapping>  
  77.         <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>  
  78.         <url-pattern>/*</url-pattern>  
  79.     </filter-mapping>  
  80.       
  81.     <!-- 该过滤器使得开发者可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名。 比如AssertionHolder.getAssertion().getPrincipal().getName()。 -->  
  82.     <filter>  
  83.         <filter-name>CAS Assertion Thread Local Filter</filter-name>  
  84.         <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class>  
  85.     </filter>  
  86.     <filter-mapping>  
  87.         <filter-name>CAS Assertion Thread Local Filter</filter-name>  
  88.         <url-pattern>/*</url-pattern>  
  89.     </filter-mapping>  
  90.     <!-- ======================== 单点登录结束 ======================== -->  
  91. </web-app>  
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="WebApp_ID" version="2.5">
	<display-name>CasClientOne</display-name>
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
		<welcome-file>index.htm</welcome-file>
		<welcome-file>index.jsp</welcome-file>
		<welcome-file>default.html</welcome-file>
		<welcome-file>default.htm</welcome-file>
		<welcome-file>default.jsp</welcome-file>
	</welcome-file-list>
	
	<!-- ======================== 单点登录结束 ======================== -->
	<!-- 用于单点退出,该过滤器用于实现单点登出功能,可选配置 -->
	<listener>
		<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
	</listener>
	
	<!-- 该过滤器用于实现单点登出功能,可选配置。 -->
	<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>

	<!-- 该过滤器负责用户的认证工作,必须启用它 -->
	<filter>
		<filter-name>CASFilter</filter-name>
		<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
		<init-param>
			<param-name>casServerLoginUrl</param-name>
			<param-value>http://localhost:8888/cas/login</param-value><!-- cas 服务器登录 地址  http://IP:PORT/CasWebProName/login -->
		</init-param>
		<init-param>
			<!-- 这里的server是服务端的IP -->
			<param-name>serverName</param-name>
			<param-value>http://localhost:8080</param-value><!-- 客户端服务器地址   http://IP:PORT -->
		</init-param>
	</filter>

	<filter-mapping>
		<filter-name>CASFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	
	<!-- 该过滤器负责对Ticket的校验工作,必须启用它 -->
	<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://localhost:8888/cas</param-value><!-- cas 服务器地址  http://IP:PORT/CasWebProName -->
		</init-param>
		<init-param>
			<param-name>serverName</param-name>
			<param-value>http://localhost:8080</param-value><!-- 客户端服务器地址   http://IP:PORT -->
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>CAS Validation Filter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	
	<!-- 该过滤器负责实现HttpServletRequest请求的包裹, 比如允许开发者通过HttpServletRequest的getRemoteUser()方法获得SSO登录用户的登录名,可选配置。 -->
	<filter>
		<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
		<filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	
	<!-- 该过滤器使得开发者可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名。 比如AssertionHolder.getAssertion().getPrincipal().getName()。 -->
	<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 Assertion Thread Local Filter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<!-- ======================== 单点登录结束 ======================== -->
</web-app>

配置完毕后,启动tomcat,然后访问:http://IP:PORT/WebProName/index.jsp,如果系统跳转到CAS登录页面,输入用户名/密码(正确的)后,会跳转到http://IP:PORT/WebProName/index.jsp,则说明CAS Client已经部署成功。

 

注:到此CAS框架已搭建完毕,此种方法未采用SSL协议(需要修改Cas),如果想采用SSL协议方式,则在上述配置中需要以下调整:

  • 修改Cas Server所处Web容器(本文为Tomcat),使其支持SSL协议
  • Cas Server部署时,不需要修改cas\WEB-INF\spring-configuration\ticketGrantingTicketCookieGenerator.xm
  • Cas Client  web.xml中配置的Cas Server相关地址需要将http变更成https

六、JDBC认证方式

1.相关JAR包准备 

cas-server-4.0.0-release.zip\cas-server-4.0.0\modules\cas-server-support-jdbc-4.0.0.jar及相应的数据库驱动包(这里是MySQL驱动mysql-connector-java-5.1.7-bin.jar)复制到cas/WEB-INF/lib目录下。

2.修改deployerConfigContext.xml1. 

       <!-- 注释掉原本固定登录用户 -->

<!--     <bean id="primaryAuthenticationHandler" -->

<!--           class="org.jasig.cas.authentication.AcceptUsersAuthenticationHandler"> -->

<!--         <property name="users"> -->

<!--             <map> -->

<!--                 <entry key="casuser" value="Mellon"/> -->

<!--             </map> -->

<!--         </property> -->

<!--     </bean> -->

 

<!-- 变更为JDBC验证方式 -->

    <bean id="primaryAuthenticationHandler" class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">

    <property name="dataSource" ref="dataSource"></property>

    <property name="sql" value="select password from user where user_name=?"></property>

    <property name="passwordEncoder" ref="MD5PasswordEncoder"></property>

    </bean>

 

<!-- 数据源配置 -->

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">

<property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>

<property name="url"><value>jdbc:mysql://localhost:3309/ossm?characterEncoding=utf8</value></property>

<property name="username"><value>root</value></property>

<property name="password"><value>root</value></property> 

</bean> 

 

<!-- 添加MD5密码加密功能 -->

<bean id="MD5PasswordEncoder" class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder">

<constructor-arg index="0">

<value>MD5</value>

</constructor-arg> 

</bean> 

七、客户端获取用户登录信息

CAS登录成功默认返回的只有用户名,

JAVA客户端获取:

AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal(); 

String username = principal.getName(); 

而在实际应用中,客户端需要知道更多的用户信息,比如用户的性别,年龄,爱好,地址,用户的分组,角色信息等等,下面介绍如何给客户端返回更多的用户资料和信息:

1.修改cas/WEB-INF/deployerConfigContext.xml,注释掉原有的attributeRepository及attributeRepository的引用信息,添加如下信息:

<bean id="attributeRepository" class="org.jasig.services.persondir.support.jdbc.SingleRowJdbcPersonAttributeDao">

        <constructor-arg index="0" ref="dataSource"/>

        <constructor-arg index="1" value="select * from user where {0}"/> 

<!--         <constructor-arg index="1" value="select * from user where {0} {1}"/> -->

        <!-- 组装sql用的查询条件属性 -->

        <property name="queryAttributeMapping">

            <map>

                <!-- 这里的key需写username,value对应数据库用户名字段 -->

                <entry key="username" value="user_name"/>

                <!-- <entry key="id" value="id"/> -->

            </map>

        </property>

        <!-- 如果要组装多个查询条件,需要加上下面这个,默认为AND -->  

        <property name="queryType">

         <value>OR</value>

        </property>  

        <!-- 要获取的属性在这里配置 -->

        <property name="resultAttributeMapping">

            <map>

             <!--key为对应的数据库字段名称,value为提供给客户端获取的属性名字,系统会自动填充值-->

                <entry key="id" value="id"/>

                <entry key="user_name" value="username"/>

                <entry key="phone" value="phone"/>

                <entry key="email" value="email"/>

            </map>

        </property>

    </bean>

2.修改cas/WEB-INF/view/jsp/protocol/2.0/casServiceValidationSuccess.jsp,添加下方标红部分:1. 

<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>

<cas:authenticationSuccess>

<cas:user>${fn:escapeXml(assertion.primaryAuthentication.principal.id)}</cas:user>

 

<%-- 返回更多用户信息配置  By Goma --%>

<c:if test="${fn:length(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes) > 0}">

            <cas:attributes>

                <c:forEach var="attr" items="${assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes}">

                    <cas:${fn:escapeXml(attr.key)}>${fn:escapeXml(attr.value)}</cas:${fn:escapeXml(attr.key)}>

                </c:forEach>

            </cas:attributes>

        </c:if>

 

        <c:if test="${not empty pgtIou}">

        <cas:proxyGrantingTicket>${pgtIou}</cas:proxyGrantingTicket>

        </c:if>

        <c:if test="${fn:length(assertion.chainedAuthentications) > 1}">

  <cas:proxies>

            <c:forEach var="proxy" items="${assertion.chainedAuthentications}" varStatus="loopStatus" begin="0" end="${fn:length(assertion.chainedAuthentications)-2}" step="1">

     <cas:proxy>${fn:escapeXml(proxy.principal.id)}</cas:proxy>

            </c:forEach>

  </cas:proxies>

        </c:if>

</cas:authenticationSuccess>

</cas:serviceResponse>

 

3.客户端取值3. 

AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal(); 

String username = principal.getName(); 

 

Map<String,Object> attributes = principal.getAttributes();

String email = attributes .get("email") + "";

八、界面定制

CAS 提供了一套默认的页面,在目录cas/WEB-INF/view/jsp/default下。在部署 CAS 之前,我们可能需要定制一套新的CAS Server页面,添加一些个性化的内容。最简单的方法就是拷贝一份 default文件到“ 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,重命名为as/WEB-INF/classes/ newUI_views.properties,并修改其中所有的值到相应新页面。最后是更新cas/WEB-INF/cas.properties文件中的 cas.viewResolver.basename,将default_views更改为newUI_views。

九、其他功能扩展

增加验证码、密码有效期、限制用户登录之类的功能这里不做描述,如果需要请自行查找。

猜你喜欢

转载自yansxjl.iteye.com/blog/2329672