单点登录CAS+Acegi配置指南

13年前

我的单点登录CAS+Acegi配置指南介绍 :

      CASCentral Authentication Service)是耶鲁大学开发的一个开源的SSOsingle sign on,单点登录)系统。它提供了丰富的客户端库,如Java, .NET, PHP, Perl等版本,使用这些库用户可以方便地给自己的应用程序加上CAS支持。Acegi security system for SpringSpring的一个子项目,它为Java EE开发者提供了一个易于使用的提供认证和授权服务的安全框架。Acegi支持CAS,也可看作是CAS的一个Java版的Client

    以下详细介绍如何配置CAS以及应用程序,使其利用AcegiCAS进行用户的登录和认证。我将以acegi-security-1.0.1发布包中附带的acegi-security-sample-tutorial应用为例,它使用DaoAuthenticationProvider对用户进行认证,用户帐号和权限信息保存在一个properties文件中,我将对其进行改造,改造之后,acegi-security-sample-tutorial使用CAS进行用户认证,授权信息仍从该properties文件读取,因为CAS只负责认证,不负责授权,所以授权工作交由客户端Acegi来完成,CAS的用户源配置为数据库,利用JDBC进行读取。本文需要读者对SSOAcegi有一定的了解。

   配置Tomcat支持SSL

      由于CAS要求使用https和客户端进行通信,所以需要配置Tomcat支持SSL,首先介绍如何制作自签名证书以及将其导入到证书库。
1. keytool -keystore keystore -alias acegisecurity -genkey -keyalg RSA -validity 9999 -storepass password -keypass password
What is your first and last name?
  [Unknown]:  localhost
其他随便填写即可。
2. keytool -export -v -rfc -alias acegisecurity -file acegisecurity.txt -keystore keystore -storepass password
3. copy acegisecurity.txt %JAVA_HOME%\jre\lib\security  
4. copy keystore %CATALINA_HOME %
5. cd %JAVA_HOME%\jre\lib\security
6. keytool -import -v -file acegisecurity.txt -keypass password -keystore cacerts -storepass changeit -alias acegisecurity
       接下来,用编辑器打开%CATALINA_HOME%/conf/server.xml,找到
    <Connector port="8443" maxHttpHeaderSize="8192"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" disableUploadTimeout="true"
               acceptCount="100" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS" />
       这一行默认是被注释掉的,取消注释,并加入keystoreFile="keystore" keystorePass="password"这两个属性,注意keystoreFile属性可以使用keystore文件的绝对路径,也可使用基于%CATALINA_HOME%环境变量的相对路径,keystorePass是访问keystore的密码,应和上面制作证书时设定的密码保持一致。
       访问https://localhost:8443,应弹出一个对话框,告知用户正要访问的站点的证书不安全,是否接受,确认接受,应可看到那只熟悉可爱的小猫。

改造acegi

    用编辑器打开WEB-INF/applicationContext-acegi-security.xml
       <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
                  <property name="authenticationManager" ref="authenticationManager"/>
                  <property name="authenticationFailureUrl" value="/acegilogin.jsp?login_error=1"/>
              <property name="defaultTargetUrl" value="/"/>
              <property name="filterProcessesUrl" value="/j_acegi_security_check"/>
              <property name="rememberMeServices" ref="rememberMeServices"/>
       </bean>
将其替换为:
       <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.cas.CasProcessingFilter">
              <property name="authenticationManager" ref="authenticationManager"/>
              <property name="authenticationFailureUrl" value="/acegilogin.jsp?login_error=1"/>
              <property name="defaultTargetUrl" value="/"/>
              <property name="filterProcessesUrl" value="/j_acegi_cas_security_check"/>
              <property name="rememberMeServices" ref="rememberMeServices"/>
       </bean>
其中,authenticationFailureUrl是认证失败时显示的页面,acegi-security-sample-tutorial登录失败时会在登录页(acegilogin.jsp)显示失败原因,现改为使用CAS之后,acegi-security-sample-tutorial使用CAS的登录页面,故acegilogin.jsp可去掉。接下来,找到
              <bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
                     <property name="loginFormUrl" value="/acegilogin.jsp"/>
                     <property name="forceHttps" value="false"/>
              </bean>
替换为:
              <bean class="org.acegisecurity.ui.cas.CasProcessingFilterEntryPoint">
                     <property name="loginUrl">
                            <value>https://localhost:8443/cas/login</value>
                     </property>
                     <property name="serviceProperties">
                            <ref bean="serviceProperties"/>
                     </property>
              </bean>
再接下来,找到
       <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
              <property name="providers">
                     <list>
                            <ref local="daoAuthenticationProvider"/>
                            <bean class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider">
                                   <property name="key" value="changeThis"/>
                            </bean>
                            <bean class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider">
                                   <property name="key" value="changeThis"/>
                            </bean>
                     </list>
              </property>
       </bean>
<ref local="daoAuthenticationProvider"/>修改为<ref local="casAuthenticationProvider"/>,并添加以下bean
       <bean id="casAuthenticationProvider" class="org.acegisecurity.providers.cas.CasAuthenticationProvider">
              <property name="ticketValidator">
                     <ref bean="ticketValidator"/>
              </property>
              <property name="casProxyDecider">
                     <ref bean="casProxyDecider"/>
              </property>
              <property name="statelessTicketCache">
                     <ref bean="statelessTicketCache"/>
              </property>
              <property name="casAuthoritiesPopulator">
                     <ref bean="casAuthritiesPopulator"/>
              </property>
              <property name="key">
                     <value>some_unique_key</value>
              </property>
       </bean>
    
       <bean id="ticketValidator" class="org.acegisecurity.providers.cas.ticketvalidator.CasProxyTicketValidator">
              <property name="casValidate">
                     <value>https://localhost:8443/cas/proxyValidate</value>
              </property>
              <property name="serviceProperties">
                     <ref bean="serviceProperties"/>
              </property>
       </bean>
    
       <bean id="serviceProperties" class="org.acegisecurity.ui.cas.ServiceProperties">
              <property name="service">
                     <value>https://localhost:8443/acegi-security-sample-tutorial/j_acegi_cas_security_check</value>
              </property>  
       </bean>
    
       <bean id="casProxyDecider" class="org.acegisecurity.providers.cas.proxy.RejectProxyTickets"/>
    
       <bean id="statelessTicketCache" class="org.acegisecurity.providers.cas.cache.EhCacheBasedTicketCache">
              <property name="cache">
                     <bean class="org.springframework.cache.ehcache.EhCacheFactoryBean">
                            <property name="cacheManager">
                                   <bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
                            </property>
                            <property name="cacheName" value="userCache"/>
                     </bean>
              </property>
       </bean>
    
       <bean id="casAuthritiesPopulator" class="org.acegisecurity.providers.cas.populator.DaoCasAuthoritiesPopulator">
              <property name="userDetailsService">
                     <ref bean="userDetailsService"/>
              </property>
       </bean>
改造完毕!

配置CAS使用JDBC数据源进行用户认证

       CAS默认设置为只要用户名和密码相同,即可进行登录,这在现实使用中是不允许的。我们修改为使用MySQLtest数据库中的app_user表作为用户数据源。首先,我们在test库中创建一个表:
CREATE TABLE `app_user` (
  `username` varchar(30) NOT NULL default '',
  `password` varchar(45) NOT NULL default '',
  PRIMARY KEY  (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
并添加如下用户:
INSERT INTO `app_user` (`username`,`password`) VALUES
 ('dianne','emu'),
 ('marissa','koala'),
 ('peter','opal'),
 ('scott','wombat');
用编辑器打开%CATALINA_HOME%/webapps/cas/WEB-INF/deployerConfigContext.xml,找到
    <bean class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" />
注释掉该行,在其下加入:
<bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
                            <property name="sql" value="select password from app_user where username=?" />
                            <property name="dataSource" ref="dataSource" />
                     </bean>
并添加一个bean
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" destroy-method="close">
       <property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>
       <property name="url"><value>jdbc:mysql://localhost:3306/test</value></property>
       <property name="username"><value>test</value></property>
       <property name="password"><value>test</value></property>
    </bean>
拷贝cas-server-jdbc-3.0.5-rc2.jarmysql-connector-java-3.1.12-bin.jar%CATALINA_HOME%/webapps/cas/WEB-INF/lib下。
 
    重新启动tomcat你会发现,一旦你访问了受保护的页面,请求就会被重定向到CAS的登录页面,登录成功之后请求会被再被定向到最初访问的页面,如果有多个系统,在这些系统之间进行切换将不会要求用户重新登录,这就达到了单点登录的目的。
 

参考文献: