使用CAS实现SSO(单点登录)
名词解释:
- 单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
- CAS (Central Authentication Service),是Yale大学的ITS开发的一套JAVA实现的开源的SSO(single sign-on)的服务。
需求:
0、准备工作
1、Hello World配置;
2、http通道,客户端/服务端配置;
3、嵌入到已有的Struts2框架中;
4、已有的多个独立的web应用,使用CAS实现单点登录;
5、自定义的用户密码认证方式,基于数据库;
6、自定义页面;
7、单点注销;
8、ticket生成与验证算法;
9、使用https通道。
环境:
Windows XP, jdk1.6.0_16, eclipse-jee-galileo-SR2, Tomcat 6.0.20, maven2.2.1
0、准备工作:
a) 下载软件包:CAS服务端,cas-server-3.4.2-release。CAS客户端,cas-client-3.1.10-release;
b) 解压后,发现cas-server和cas-client都是基于Maven构建的;
c) 分别进入cas-server和cas-client主目录,mvn eclipse:eclipse,下载依赖包,导入工程,为修改源码做准备;
d) Tomcat修改server.xml,配置新的service,名为cas,端口8000,内容指向:…/ cas-server-3.4.2/cas-server-webapp/target/cas-server-webapp-3.4.2
1、Hello World配置:
a) war包发布后,启动tomcat,访问http://localhost:8000/cas/,转入登录页面,此时输入用户名与密码相同的情况则登录成功,否则登录失败。
b) 至此,cas服务器Hello World配置完成。
2、http通道,客户端/服务端配置:
a) 客户端源码修改
1、 cas-client-support-distributed-memcached包缺少依赖包memcached-2.4.2,手工去code.google.com网上下载。
2、 mvn install失败,test有错,pom.xml文件,在<properties></properties>之间增加属性<maven.test.skip>true</maven.test.skip>,以跳过test步骤,mvn install成功了。
3、 修改:HttpBasedServiceCredentialsAuthenticationHandler.java,注释掉关于https校验的几行代码,重新mvn install。
4、 相关jar包已成功编译打包,乖乖地躺在MAVEN_REPO目录里了。
b) 服务端修改
1、 修改cas-server-webapp包WEB-INF目录下的deployerConfigContext.xml文件,p:httpClient-ref="httpClient"属性后增加p:requireSecure="false"以关闭https;
2、 修改WEB-INF/spring-configuration目录下的
ticketGrantingTicketCookieGenerator.xml文件,p:cookieSecure="true"属性改为p:cookieSecure="false"
3、 修改WEB-INF/spring-configuration目录下的warnCookieGenerator.xml文件,p:cookieSecure="true"属性改为p:cookieSecure="false"
4、 跳过test阶段重新mvn install
c) Web应用端配置,这里先拿tweb2这个网站做小白鼠
1、 pom文件增加对cas-client的依赖,<dependency><groupId>org.jasig.cas</groupId><artifactId>cas-client-core</artifactId><version>3.1.10</version></dependency>
2、 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="WebApp_ID" version="2.5"> <display-name>tweb1</display-name> <!-- listener --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> </listener> <!-- context --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml,/WEB-INF/casContext.xml</param-value> </context-param> <!-- filters --> <filter> <filter-name>CAS Single Sign Out Filter</filter-name> <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class> </filter> <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>casAuthenticationFilter</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>casValidationFilter</param-value> </init-param> </filter> <filter> <filter-name>CAS HttpServletRequestWrapperFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetBeanName</param-name> <param-value>casHttpServletRequestWrapperFilter</param-value> </init-param> </filter> <filter> <filter-name>Struts2 Dispatcher</filter-name> <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class> <init-param> <param-name>actionPackages</param-name> <param-value>net.welken.t.tweb1.action</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Single Sign Out Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>CAS Authentication Filter</filter-name> <url-pattern>/userLogin.action</url-pattern> </filter-mapping> <filter-mapping> <filter-name>CAS Validation Filter</filter-name> <url-pattern>/userLogin.action</url-pattern> </filter-mapping> <filter-mapping> <filter-name>CAS HttpServletRequestWrapperFilter</filter-name> <url-pattern>/userLogin.action</url-pattern> </filter-mapping> <filter-mapping> <filter-name>Struts2 Dispatcher</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- common set --> <session-config> <session-timeout>1</session-timeout> </session-config> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
3、 casContext.xml内容
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <bean id="casAuthenticationFilter" class="org.jasig.cas.client.authentication.AuthenticationFilter"> <property name="casServerLoginUrl" value="http://localhost:8000/cas/login" /> <property name="serverName" value="http://localhost:8081" /> </bean> <bean id="casValidationFilter" class="org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter"> <property name="ticketValidator"> <ref bean="Cas20ServiceTicketValidator" /> </property> <property name="useSession" value="true" /> <property name="serverName" value="http://localhost:8081" /> <property name="redirectAfterValidation" value="true" /> </bean> <bean id="Cas20ServiceTicketValidator" class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator"> <constructor-arg index="0" value="http://localhost:8000/cas" /> </bean> <bean id="casHttpServletRequestWrapperFilter" class="org.jasig.cas.client.util.HttpServletRequestWrapperFilter" /> </beans>
3、嵌入到已有的Struts2框架中:
a) 思路:
1、 struts2设置登录拦截器,判断session中的用户登录状态,未登录时转向userLogin.action
2、 配置CAS过滤器,仅匹配userLogin.action这一个URL
3、 这样,所有需要登录的资源,都会通过userLogin.action统一转向CAS server
4、 CAS登录成功后,跳回userLogin.action,将登录用户的信息记录到session中,action从session中获取拦截器之前的URL,并跳转
b) 遇到的问题:
问题:单点登录仅对*.jsp有效,对*.action无效。解决:调换Filters的位置,将CASFilter配置在Struts2 FilterDispatcher之上。感谢:DavyLee@javaeye。
4、已有的多个独立的web应用,使用CAS实现单点登录:
a) tweb2应用在之前的改造中,已纳入CAS;
b) tweb1应用中仍使用独自的登录验证;
c) 页面互访中,如果首先登录tweb1,访问tweb2内容,被跳转到CAS登录页面再次登录;如果首先登录tweb2,访问tweb1内容,被跳转到tweb1自有的登录页面。
d) tweb1应用纳入CAS后,同一浏览器,tweb1与tweb2两个应用间跳转,只需一次登录。
5、自定义的用户密码认证方式,基于数据库:
a) 建表脚本:
create table TB_USER_PASSWORD ( ID int not null auto_increment, USER varchar(32), PASSWORD char(32), primary key (ID) );
其中password字段存放的是密码明文MD5转化大写32位字符串;
b) 修改配置文件
<!--<bean class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" /> --> <bean class="org.jasig.cas.adaptors.jdbc.SearchModeSearchDatabaseAuthenticationHandler"> <property name="tableUsers"><value>tb_user_password</value></property> <property name="fieldUser"><value>user</value></property> <property name="fieldPassword"><value>password</value></property> <property name="dataSource" ref="casDataSource" /> <property name="passwordEncoder" ref="md5Encoder" /> </bean> <bean id="md5Encoder" class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder"> <constructor-arg value="MD5"/> </bean> <bean id="casDataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property> <property name="url"> <value>jdbc:mysql://localhost:3306/dbcomm</value> </property> <property name="username"><value>commuser</value></property> <property name="password"><value>test123</value></property> </bean>
c) Jar包拷贝到lib目录,因为运行时需要:
cas-server-support-jdbc-3.4.2.jar, commons-dbcp-1.4.jar, ommons-pool-1.5.4.jar, mysql-connector-java-5.1.12.jar
6、自定义页面:
a) …/WEB-INF/ cas.properties文件,其中cas.viewResolver.basename表示当前使用的样式,改为xxx_views;
b) …/WEB-INF/view/jsp目录下default目录存放了具体的jsp文件,复制目录改名为xxx,修改其中的ui/casLoginView.jsp文件;
c) …/WEB-INF/classes/default_views.properties文件,模仿之,新建一个xxx_views.properties文件,里面定义了登录用哪个页面,注销用哪个页面等等,注意页面所在的目录;
发现登录页面改动已经生效了,浅尝则止,毕竟美工的活极不熟练。