客户端应用创建
新建一动态web工程cas-client1,这里使用的是maven来创建,在pom文件中增加对cas-client的依赖。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.dylan</groupId> <artifactId>cas-client1</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>3.1.2.RELEASE</version> </dependency> <dependency> <groupId>org.jasig.cas</groupId> <artifactId>cas-client-core</artifactId> <version>3.1.10</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>tomcat-maven-plugin</artifactId> <configuration> <port>8081</port> </configuration> </plugin> </plugins> </build> </project>
Web.xml文件
<?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" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="cas-client1" version="2.5"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:applicationContext-cas.xml</param-value> </context-param> <!-- 用于单点退出,该过滤器用于实现单点登出功能,可选配置 --> <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>CAS Authentication Filter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetBeanName</param-name> <param-value>authenticationFilter</param-value> </init-param> </filter> <filter> <filter-name>CAS Validation Filter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetBeanName</param-name> <param-value>ticketValidationFilter</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Authentication Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <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> <!-- 自定义的filter,在用户登录成功之后进行处理 --> <filter> <filter-name>AutoSetUserAdapterFilter</filter-name> <filter-class>org.dylan.sso.filter.AutoSetUserAdapterFilter</filter-class> </filter> <filter-mapping> <filter-name>AutoSetUserAdapterFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- - Loads the root application context of this web app at startup. - The application context is then available via - WebApplicationContextUtils.getWebApplicationContext(servletContext). --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>
Spring配置文件
在web.xml中可以看出,这里使用了spring的DelegatingFilterProxy对cas的filter进行代理,这样做的好处是将cas与spring集成起来,方便对配置信息的管理。也更符合主流的编程风格。所以这里还用到了一个spring的配置文件和属性的配置文件。
applicationContext-cas.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd "> <context:property-placeholder location="classpath:cas-client.properties" /> <bean name="authenticationFilter" class="org.jasig.cas.client.authentication.AuthenticationFilter"> <property name="casServerLoginUrl" value="${cas.server.loginUrl}" /> <property name="renew" value="${cas.server.renew}" /> <property name="gateway" value="${cas.server.gateway}" /> <property name="service" value="${cas.client.serverName}" /> </bean> <!-- 对认证ticket进行校验 --> <bean name="ticketValidationFilter" class="org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter"> <property name="service" value="${cas.client.serverName}" /> <property name="ticketValidator"> <bean class="org.jasig.cas.client.validation.Cas10TicketValidator"> <constructor-arg index="0" value="${cas.server.url}" /> </bean> </property> </bean> </beans>
cas-client.properties
cas.server.url=http://localhost:8080/cas/ cas.server.loginUrl=http://localhost:8080/cas/login cas.server.renew=false cas.server.gateway=false cas.client.serverName=http://localhost:8081/cas-client1/
这里的配置信息根据实际的情况进行修改,我这里的cas服务器的地址是http://localhost:8080/cas,当前客户端的服务地址是http://localhost:8081/cas-client1(通过pom文件中可以看到使用的端口号)
AutoSetUserAdapterFilter
该类的doFilter方法如下(详细说明见后文中的参考资料):
/** * 过滤逻辑:首先判断单点登录的账户是否已经存在本系统中, 如果不存在使用用户查询接口查询出用户对象并设置在Session中 * * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain) */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; // _const_cas_assertion_是CAS中存放登录用户名的session标志 Object object = httpRequest.getSession().getAttribute( AbstractCasFilter.CONST_CAS_ASSERTION); if (object != null) { Assertion assertion = (Assertion) object; String loginName = assertion.getPrincipal().getName(); System.out.println("用户已经在SSO系统中登录,登录用户名为:" + loginName); String user = getCurrentUser(httpRequest); // 第一次登录系统 if (user == null) { System.out.println("用户第一次登录系统,保存session信息."); httpRequest.getSession().setAttribute("User_Info", loginName); } else { System.out.println("当前Session中的用户信息为:" + user); } } chain.doFilter(request, response); } private String getCurrentUser(HttpServletRequest request) { return (String) request.getSession().getAttribute("User_Info"); }
客户端页面index.jsp
%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> 首页,当前登录用户是:<%=session.getAttribute("User_Info") %> </body> </html>
由于在web.xml中对安全的拦截配置为/*,所以该页面是受保护的,需要用户输入用户名和密码才能使用。
为了测试单点登录在多个应用程序间是否生效,将上面创建的cas-client1复制一份,更名为cas-client2,并将pom文件中tomcat使用的端口配置为8082;并相应地修改cas-client.properties文件中的客户端配置。
现在,依次启动cas-server,cas-client1,cas-client2三个应用。
测试
在浏览器中输入地址:http://localhost:8081/cas-client1/index.jsp,可以看到,浏览器已经自动跳转到cas-server的登录页面,如下图:
输入用户名和密码,登录成功。浏览器又会返回到cas-client1的首页,如下图:
此时,在浏览器中打开一个选项卡(由于cas-server默认的cookie是当前窗口,所以不能是再打开一个新窗口测试),输入cas-client2的访问地址:http://localhost:8082/cas-client2/index.jsp。此时,看到用户已经登录,并获取到用户的登录名。如下图:
单点登录测试成功。