`
toplchx
  • 浏览: 339171 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

CAS3单点登录与SpringSecurity3整合全纪录

阅读更多

最近在做一个单点登录的项目,在网上找了一些资料大都是比较旧的文章。不过还是给了我很多的帮助。在这记录一下我的整合过程。废话少说,直奔主题。

 

单点登录服务端采用的是CAS 3.4.7  下载地址:http://www.jasig.org/cas/download

            客户端采用的是CAS client java 3.1.12  下载地址:https://wiki.jasig.org/display/CASC/CAS+Client+for+Java+3.1

权限控制用Spring Security3.0.5

 

网络环境:

192.168.10.167 : xx网站

192.168.10.173 : 单点登录服务器

 

1、安装JDK,tomcat

这个不说了。

 

2、让tomcat支持SSL

 

     1)用keytool制作密钥

        命令行模式(winows)

        确认系统环境变量里有JAVA_HOME

        >keytool -delete -alias tomcatsso167 -keystore "%JAVA_HOME%/jre/lib/security/cacerts" -storepass changeit

>keytool -delete -alias tomcatsso167 -storepass changeit
>keytool -list -keystore "%JAVA_HOME%/jre/lib/security/cacerts" -storepass changeit
>keytool -genkey -keyalg RSA -alias tomcatsso167 -dname "CN=www-167" -storepass changeit -validity 365
        输入<tomcatsso>的主密码
            (如果和 keystore 密码相同,按回车):
        再次输入新密码:
>keytool -export -alias tomcatsso167 -file "%JAVA_HOME%/jre/lib/security/tomcatsso167.crt" -storepass changeit
>keytool -import -alias tomcatsso167 -file "%JAVA_HOME%/jre/lib/security/tomcatsso167.crt" -keystore "%JAVA_HOME%/jre/lib/security/cacerts" -storepass changeit
 

        说明:“CN=www-167”是计算机名,如果是多台服务器一定要用计算机名。上面这段是在一台windows服务器上生成SSL需要的证书。        

 

        命令行模式(linux)

        export  (查看JAVA_HOME环境变量是否存在)

        让SSH终端显示中文,修改/etc/sysconfig/i18n文件如下

        LANG="zh_CN.gb2312"//表明你当前系统的语言环境变量设置 

        SUPPORTED="zh_CN.gb2312:zh_CN:zh:en_US.UTF-8:en_US:en"//表明系统预置了那些语言支持 ,不在项目中的语言不能正常显示 

        SYSFONT="latarcyrheb-sun16"//定义控制台终端字体,你文本登录的时候显示的字体就是这个 latarcyrheb-sun16

 

        将window命令中的%JAVA_HOME% 换成$JAVA_HOME

        >keytool -delete -alias tomcatsso173 -keystore "$JAVA_HOME/jre/lib/security/cacerts" -storepass changeit

        >keytool -delete -alias tomcatsso173 -storepass changeit
        >keytool -list -keystore "$JAVA_HOME/jre/lib/security/cacerts" -storepass changeit
        >keytool -genkey -keyalg RSA -alias tomcatsso173 -dname "CN=www-173" -storepass changeit -validity 365
        >keytool -export -alias tomcatsso173 -file "$JAVA_HOME/jre/lib/security/tomcatsso173.crt" -storepass changeit
        >keytool -import -alias tomcatsso173 -file "$JAVA_HOME/jre/lib/security/tomcatsso173.crt" -keystore "$JAVA_HOME/jre/lib/security/cacerts" -storepass changeit
 

 

        如果client和server不是同一台机器,需要将server的证书(tomcatsso173.crt)导入到client机器中,这样server在登录后才能正确返回。

        导入命令:keytool -import -alias tomcatsso173 -file "%JAVA_HOME%/jre/lib/security/tomcatsso173.crt" -keystore "%JAVA_HOME%/jre/lib/security/cacerts" -storepass changeit

         说明:"CN=www-173"是这台机器的计算机名(hostname)。

 

     2)设置tomcat配置文件 server.xml

     windows:

 

 

<Connector protocol="org.apache.coyote.http11.Http11Protocol" 
               port="8443" minSpareThreads="5" maxSpareThreads="75" 
               enableLookups="true" disableUploadTimeout="true" 
               acceptCount="100" maxThreads="200" 
               scheme="https" secure="true" SSLEnabled="true" 
               keystoreFile="C:/Documents and Settings/Administrator/.keystore" keystorePass="changeit" 
               truststoreFile="D:/Program Files/Java/jdk1.6.0_20/jre/lib/security/cacerts" 
               clientAuth="false" sslProtocol="TLS"/>
 

     linux:带路径的两行修改为以下内容    

               keystoreFile="/root/.keystore" keystorePass="changeit"

               truststoreFile="/usr/java/jdk1.6.0_20/jre/lib/security/cacerts"

 

     3)启动tomcat,在浏览器输入https://localhost:8443 显示tomcat页面就对了。有的浏览器会出一个安全的提示,继续就可以了。

 

 

3、部署CAS Server

   到CAS网站上下载server程序,将cas-server-webapp-<版本号>.war 拷贝到 tomcat的 webapps 目录,并更名为 cas.war。由于前面已配置好 tomcat 的 https 协议,可以重新启动 tomcat,然后访问:https://localhost:8443/cas ,如果能出现正常的 CAS 登录页面,则说明 CAS Server 已经部署成功。

 

 

4、修改CAS Server为jdbc连接

    1)修改cas/WEB-INF/deployerConfigContext.xml

    添加DataSource

 

 

    <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://ip:3306/database?characterEncoding=UTF-8</value>
        </property>
        <property name="username">
            <value>root</value>
        </property>
        <property name="password">
            <value></value>
        </property>
    </bean>
 

    2)替换AuthenticationHandler

    注释掉原来的

    <!-- <bean class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" /> -->

    增加

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

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

        <property name="sql" value="select passwd from v_user where account=?" />

    </bean>

    <!-- sql是查询密码的sql语句,修改成符合你的数据结构的语句 -->

 

    3)添加依赖jar包

     cas-server-support-jdbc-<版本号>.jar、mysql-connector-java-<版本号>-bin.jar、commons-collections-3.2.jar、commons-dbcp-1.2.1.jar、commons-pool-1.3.jar

 

至此单点登录的Server端已经安装配置完成了。在浏览器输入 https://localhost:8443/cas 就可以测试一下了。

 

 

5、纯洁的配置CAS-client。纯洁?就是只用CAS client配置一个标准的web工程,没有其他框架和权限工具,这样有利于理解CAS client的工作原理。如果没兴趣,也可以直接看第7步和Spring Security结合的部分。

    1)新建一个web工程,添加一个jsp页面。

    2)添加依赖jar包

    cas-client-core-3.1.10.jar、commons-logging.jar

    3)web.xml的配置,添加如下配置:

    <!-- 单点登出过滤器 -->

 

 

<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>
    <listener>
        <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
    </listener>
    <!-- 认证过滤器,当本地不含登录信息时,跳转到casServerLoginUrl进行身份认证 -->
    <filter>
        <filter-name>CAS Authentication Filter</filter-name>
        <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
        <init-param>
            <param-name>casServerLoginUrl</param-name>
            <param-value>https://www-173:8443/cas/login</param-value> <!-- 单点登录服务器的登录URL -->
        </init-param>
        <init-param>
            <param-name>serverName</param-name>
            <param-value>http://www-167:8080</param-value>  <!-- 登陆后返回的服务器地址 -->
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CAS Authentication Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 解析登录服务器返回的认证信息,可选多种protocol -->
    <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>https://www-173:8443/cas</param-value>
        </init-param>
        <init-param>
            <param-name>serverName</param-name>
            <param-value>http://www-167:8080</param-value>
        </init-param>
        <init-param>
            <param-name>redirectAfterValidation</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CAS Validation Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 将上面解析的结果装入request -->
    <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>

    <!-- 在本地线程保存解析结果 -->
    <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>
 

 

     启动client端web服务器,浏览jsp页面,浏览器会跳到单点登录服务器进行登录,登陆后返回client服务。

 

 

 

6、CAS返回信息

     上面配置的CAS Validation Filter如果是CAS1.0的协议,即Cas10TicketValidationFilter类,就只能返回登录账号。取得方法是:

     request.getRemoteUser();

     或者

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

     principal.getName();

 

     如果用CAS2.0的协议,即Cas20ProxyReceivingTicketValidationFilter,理论上是可以增加返回属性的。但是如果用cas server3.4.7这个版本,还需要自己添加一些类帮助完成这项工作。SAML1.1协议没有测试。

 

     实际上单点登录服务器也就只应该返回登录账号,不应该将其他工作交给登录服务器,应该有client服务器去处理。不过实际情况总是出人意料,如果就要返回其他属性,那就改吧。

 

7、Spring Security3.0.5的配置。

     我的web服务是在spring security的cas例子上做了一些修改。建议到http://s3browse.springsource.com/browse/maven.springframework.org/snapshot/org/springframework/security/ 下载spring的例子。

     1)web.xml

 

 

<context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/applicationContext-security.xml
        </param-value>
    </context-param>

    <context-param>
        <param-name>log4jConfigLocation</param-name>
        <param-value>/WEB-INF/classes/log4j.properties</param-value>
    </context-param>

    <context-param>
        <param-name>webAppRootKey</param-name>
        <param-value>cas.root</param-value>
    </context-param>

    <filter>
       <filter-name>CAS Single Sign Out Filter</filter-name>
       <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
    </filter>

    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter-mapping>
       <filter-name>CAS Single Sign Out Filter</filter-name>
       <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter-mapping>
      <filter-name>springSecurityFilterChain</filter-name>
      <url-pattern>/*</url-pattern>
    </filter-mapping>

    <listener>
        <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
    </listener>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <listener>
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    </listener>
 

 

2)applicationContext-security.xml

 

 

<!-- 浏览权限设定,根据自己的情况修改 -->

    <sec:http entry-point-ref="casProcessingFilterEntryPoint">
        <sec:intercept-url pattern="/secure/extreme/**" access="ROLE_PROJECTLEADER" />
        <sec:intercept-url pattern="/secure/**" access="ROLE_USER" />
        <sec:logout logout-success-url="/cas-logout.jsp"/>
        <sec:custom-filter ref="casAuthenticationFilter" after="CAS_FILTER"/>
    </sec:http>

    <sec:authentication-manager alias="authenticationManager">
        <sec:authentication-provider ref="casAuthenticationProvider"/>
    </sec:authentication-manager>

    <bean id="casAuthenticationFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
        <property name="authenticationManager" ref="authenticationManager"/>
        <!-- 认证失败返回的页面(非403错误) -->
        <property name="authenticationFailureHandler">
            <bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
                <property name="defaultFailureUrl" value="/casfailed.jsp"/>
            </bean>
        </property>
        <!-- 认证成功返回的页面,此处做了修改,这个类是继续之前的操作。默认的类是设置一个固定的页面 -->
        <property name="authenticationSuccessHandler">
        	<bean class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler" />
        </property>
    </bean>

    <bean id="casProcessingFilterEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
        <property name="loginUrl" value="https://www-167:8443/cas/login"/>  <!-- 单点登录服务器登录URL -->
        <property name="serviceProperties" ref="serviceProperties"/>
    </bean>

    <bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
        <property name="userDetailsService" ref="userService"/>
        <property name="serviceProperties" ref="serviceProperties" />
        <property name="ticketValidator">
            <bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
                <constructor-arg index="0" value="https://www-167:8443/cas" />
            </bean>
        </property>
        <property name="key" value="an_id_for_this_auth_provider_only"/>
    </bean>

    <bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
        <property name="service" value="http://www-167:8080/castest2/j_spring_cas_security_check"/>  <!-- client服务的URL, j_spring_cas_security_check是security自己定义的一个filter-->
        <property name="sendRenew" value="false"/>
    </bean>
    <!-- 权限用jdbc从数据库中读取,数据库结构请看http://static.springsource.org/spring-security/site/docs/3.0.x/reference/appendix-schema.html -->
    <!-- 这里因为用了Group的权限,需要设置enableGroups,所以这样配。如果不需要Group的权限,也可以简单的如下配置 -->
    <!-- <sec:jdbc-user-service id="userService" data-source-ref="dataSource" /> -->
    <!-- 如果有自己的数据结构,可以在下面增加2个属性配置:users-by-username-query,authorities-by-username-query用这两个配置你的sql -->
    <bean id="userService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
    	<property name="dataSource" ref="dataSource" />
    	<property name="enableGroups" value="true" />
    </bean>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName">
            <value>com.mysql.jdbc.Driver</value>
        </property>
        <property name="url">
            <value>jdbc:mysql://ip:3306/database?characterEncoding=UTF-8</value>
        </property>
        <property name="username">
            <value>root</value>
        </property>
        <property name="password">
            <value></value>
        </property>
    </bean>
 

 

OK,配置完成,开始漫长的开发吧。

 

有帮助的文章和链接:

CAS Server wiki:https://wiki.jasig.org/display/CASUM/Home

CAS Client wiki:https://wiki.jasig.org/display/CASC/Home

Spring Security reference:http://static.springsource.org/spring-security/site/docs/3.0.x/reference/springsecurity.html

Srping Security程序及源码下载:http://static.springsource.org/spring-security/site/downloads.html

其他前辈的文章:http://yangguowen.iteye.com/blog/678498

http://www.iteye.com/topic/40129

http://liuqq.iteye.com/blog/732866

3
2
分享到:
评论
5 楼 fengjidly 2016-05-26  
应用配置文件applicationContext-spring-security-cas.xml代码是这样:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd
               http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

	<bean id="authenticationSuccessHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
	    <!-- After login, return to the last visited page -->
	    <property name="useReferer" value="true" />
	</bean>
	
  <bean id="serviceProperties"
        class="org.springframework.security.cas.ServiceProperties">
    <property name="service"
        value="http://HOSTNAME:PORT/saiku/j_spring_cas_security_check"/>
    <property name="sendRenew" value="false"/>
  </bean>
  
<bean id="casEntryPoint"
    class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
  <property name="loginUrl" value="http://HOSTNAME:PORT/cas/login"/>
  <property name="serviceProperties" ref="serviceProperties"/>
</bean> 


	<bean id="casAuthenticationFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
    <property name="authenticationManager" ref="authenticationManager" />
    <property name="authenticationFailureHandler">
      <bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
        <property name="defaultFailureUrl" value="/casFailed.jsp" />
      </bean>
    </property>
  </bean>
  
	<bean id="exceptionTranslationFilter"
		class="org.springframework.security.web.access.ExceptionTranslationFilter">
		<property name="authenticationEntryPoint">
			<ref bean="casProcessingFilterEntryPoint" />
		</property>
		<property name="accessDeniedHandler">
			<bean class="org.springframework.security.web.access.AccessDeniedHandlerImpl" />
		</property>
	</bean>
	
	<bean id="casProcessingFilterEntryPoint"
		class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
		<property name="loginUrl"
			value="http://HOSTNAME:PORT/cas/login" />
		<property name="serviceProperties" ref="serviceProperties" />
	</bean>
	
<bean id="ticketValidator"
		class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
    <constructor-arg index="0" value="http://localhost:81/cas" />
	</bean>
<bean id="casAuthenticationProvider"
		class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
		<property name="userDetailsService" ref="userDetailsService" />
    <property name="serviceProperties" ref="serviceProperties" />
		<property name="ticketValidator" ref="ticketValidator" />
		<property name="key"
			value="my_password_for_this_auth_provider_only" />
	</bean>

    <bean id="userDetailsService"
          class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
        <property name="dataSource">
            <ref bean="dataSource" />
        </property>
        <property name="authoritiesByUsernameQuery">
            <value>
            </value>
        </property>
        <property name="usersByUsernameQuery">
            <value>
                             

            </value>
        </property>
    </bean>
	<bean id="dataSource"
          class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="net.sourceforge.jtds.jdbc.Driver" />
        <property name="url"
                  value=""/>
        <property name="username" value="" />
        <property name="password" value="" />
    </bean>
	<!-- Automatically receives AuthenticationEvent messages -->
	 <bean id="loggerListener"
			class="org.springframework.security.access.event.LoggerListener" /> 
	
		<bean id="authenticationEntryPoint"
    class="org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint">
    <property name="realmName" value="Pentaho Realm" />
</bean>
	
		<!--<bean id="anonymousProcessingFilter"
						class="org.springframework.security.web.authentication.AnonymousAuthenticationFilter">
						<constructor-arg index="0" value="foobar" />
						
		</bean>-->
	 <security:authentication-manager alias="authenticationManager">
        <security:authentication-provider ref="casAuthenticationProvider" />
    </security:authentication-manager>
</beans>

4 楼 fengjidly 2016-05-26  
PS:系统用的是http
3 楼 fengjidly 2016-05-26  
你好,如果我的系统已经用CAS实现了单点登录,想要添加一个新的应用进去,这个应用(saiku一个国外的开源项目)是用的Spring Security ,我该怎么配置呢?找的资料没怎么明白。谢谢
2 楼 ziang5173 2015-10-07  
haijun5211314 写道
你好,询问下按照你帖子里的操作把法来操作,结果出现:java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty这个错误。根据查询说是证书的问题。我的server是放在linux中的生成证书的方法是按照你linux的方法来的,win上的是从linux拷贝下来以后导入进去的。下面是我win上tomcat的配置:Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"
               maxThreads="150" SSLEnabled="true" scheme="https" secure="true"  
               truststoreFile="D:/Java/jdk1.7.0_45/jre/lib/security/cacerts"
               clientAuth="false" sslProtocol="TLS" />麻烦能帮我看下吗?谢谢

你这个证书密码没有输入吧
1 楼 haijun5211314 2015-08-05  
你好,询问下按照你帖子里的操作把法来操作,结果出现:java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty这个错误。根据查询说是证书的问题。我的server是放在linux中的生成证书的方法是按照你linux的方法来的,win上的是从linux拷贝下来以后导入进去的。下面是我win上tomcat的配置:Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"
               maxThreads="150" SSLEnabled="true" scheme="https" secure="true"  
               truststoreFile="D:/Java/jdk1.7.0_45/jre/lib/security/cacerts"
               clientAuth="false" sslProtocol="TLS" />麻烦能帮我看下吗?谢谢

相关推荐

Global site tag (gtag.js) - Google Analytics