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页面
<beanid="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建立信任关系
假设 CASServer单独部署在一台机器 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>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://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>CASFilter</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:outvalue="${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=newCASFilterRequestWrapper(request);
out.println("The logon user:" + reqWrapper.getRemoteUser());
效果
在 casTest1和 casTest2中,都有一个简单Servlet作为欢迎页面 WelcomPage,且该页面必须登录过后才能访问,页面代码如清单 14所示:
清单 14. WelcomePage页面代码
public class WelcomePage extendsHttpServlet {
public void doGet(HttpServletRequestrequest, 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 sampleSystem!</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Welcometo casTest1 sample System!</h1>");
CASFilterRequestWrapper reqWrapper=new CASFilterRequestWrapper(request);
out.println("<p>The logonuser:" + reqWrapper.getRemoteUser() + "</p>");
HttpSession session=request.getSession();
out.println("<p>The logonuser:" +
session.getAttribute(CASFilter.CAS_FILTER_USER) + "</p>");
out.println("<p>The logonuser:" +
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:
- 打开浏览器,访问 http://domainB:8080/casTest1/WelcomePage ,浏览器会弹出安全提示,接受后即转到 CAS 的登录页面,如图 2所示:
图 2. CAS登录页面
- 登录成功后,再重定向到 casTest1的 WelcomePage页面,如图 所示:
图 3.登录后访问 casTest1的效果
可以看到图 中地址栏里的地址多出了一个 ticket 参数,这就是 CAS 分配给当前应用的ST(Service Ticket)。
- 再在同一个浏览器的地址栏中输入 http://domainC:8080/casTest2/WelcomePage ,系统不再提示用户登录,而直接出现如图 4 所示的页面,并且显示在 casTest1中已经登录过的用户。
图 4.在 casTest1中登录过后访问 casTest2的效果
- 重新打开一个浏览器窗口,先输入 http://domainC:8080/casTest2/WelcomePage ,系统要求登录,在登录成功过后,正确显示 casTest2 的页面。之后再在地址栏重新输入 http://domainB:8080/casTest1/WelcomePage ,会直接显示 casTest1 的页面而无需再次登录。