CAS實現SSO單點登錄案例(整合SpringSecurity)

CAS介紹

CAS是一個單點登錄框架,由耶魯大學的一個組織開發。CAS是一個開源項目,代碼目前是在github上管理。單點登錄:Single Sign On,簡稱SSO,SSO使得在多個應用系統中,用戶只需要登錄一次就可以訪問所有相互信任的應用系統。通俗理解爲一個應用登錄了,其他被授權的應用不用再登錄。之前也寫過一篇sso單點登錄,用redis實現的,有興趣可以翻翻
cas下載地址:https://github.com/apereo/cas/releases

cas服務搭建

我們案例下載使用的是CAS4.2,採用HTTPS協議處理用戶請求。搭建HTTPS需要咱們生成對應的證書信息。當然也可以通過配置使其支持http協議,但畢竟http協議不安全,所以我們還是學一下如何搭建本地HTTPS服務.

1.生成祕鑰庫
keytool -genkey -alias shemuel -keyalg RSA -keystore E:/javaee/cas/keystory/shemeul
其中-alias後面的是密鑰庫別名,最後的shemeul是要生成的密鑰庫文件.

在這裏插入圖片描述
命令運行後密鑰庫就生成了,接着就是導出這個密鑰的證書,運行如下命令:

keytool -export -trustcacerts -alias shemuel -file E:/javaee/cas/keystory/shemuel.cer -keystore E:/javaee/cas/keystory/shemuel
前面的路徑是要導出證書的位置,後面的路徑是之前密鑰庫的路徑

將證書導入到JDK證書庫
剛纔的操作會根據我們的祕鑰庫生成一個證書,緊接着我們需要將該證書導入到JDK的證書庫裏才能使用。

keytool -import -trustcacerts -alias shemuel -file E:/javaee/cas/keystory/shemuel.cer -keystore "F:/Java/jdk1.8.0_131/jre/lib/security/cacerts"

要求輸入密鑰庫口令:注意這個密鑰庫是jdk的密鑰庫,口令密碼爲:changeit

2.tomcat發佈cas服務端項目

在githib上下載cas服務端項目 https://github.com/apereo/cas/releases

  • 解壓一個tomcat,放到D:\workspace\cas\目錄下,把剛纔我們下載的cas解壓打開target目錄下有個cas.war 拷貝到tomcat的webapps目錄並解壓,刪除war包。

  • 下載的cas解壓目錄下還有一個文件target\war\work\org.jasig.cas\cas-server-webapp\WEB-INF\cas.properties,把他拷貝到webapps\cas\WEB-INF目錄下。

  • 修改spring-configuration\propertyFileConfigurer.xml,把location="file:/etc/cas/cas.properties"換成剛纔cas.properties的絕對路徑。

    <util:properties id="casProperties" location="file:D:/workspace/cas/apache-tomcat-8.5.16/webapps/cas/WEB-INF/cas.properties" />

修改tomcat的conf/server.xml,加入如下代碼

<Connector port="443" protocol="org.apache.coyote.http11.Http11NioProtocol"
    	   maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
    	   clientAuth="false" sslProtocol="TLS"
    	   keystoreFile="E:\javaee\cas\keystory\shemuel" 
    	   keystorePass="123456" />

啓動tomcat, bin目錄下的 startup.bat

訪問 https://shemuel:8443/cas/login
我這裏的本地hosts配置了 127.0.0.1 對應 shemuel了,有興趣也可以配置一下.
訪問後可能出現瀏覽器安全提示,因爲我們的證書是本地的,沒交錢所以會出現警告,添加個例外繼續訪問,看到如下界面就說明成功了
在這裏插入圖片描述
登錄名:casuser 密碼:Mellon
這是cas自帶的一個登錄用戶,我們可以配置數據庫信息,使用我們自己的數據庫表用戶登錄

3.配置cas數據庫連接池
  • 打開webapps\cas\WEB-INF\deployerConfigContext.xml
  • 把32行的<alias name="acceptUsersAuthenticationHandler" alias="primaryAuthenticationHandler" /> 註釋掉。
  • 將如下代碼拷貝到deployerConfigContext.xml中
<!--配置加密算法-->
		<bean id="MD5PasswordEncoder" class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder" autowire="byName">
			<constructor-arg  value="MD5"/>
		</bean>
		
		<!--查詢數據所採用什麼加密方式-->
		<bean id="queryDatabaseAuthenticationHandler" name="primaryAuthenticationHandler" class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
			<property name="passwordEncoder" ref="MD5PasswordEncoder"/>
		</bean>
		
		<!--名字叫dataSource的Bean取別名叫queryDatabaseDataSource-->
		<alias name="dataSource" alias="queryDatabaseDataSource"/>
		
		<!--配置數據源-->
		<bean id="dataSource"
				class="com.mchange.v2.c3p0.ComboPooledDataSource"
				p:driverClass="com.mysql.jdbc.Driver"
				p:jdbcUrl="jdbc:mysql://127.0.0.1:3306/springsecurity?useUnicode=true&amp;characterEncoding=UTF-8&amp;zeroDateTimeBehavior=convertToNull"
				p:user="root"
				p:password="123456"
				p:initialPoolSize="6"
				p:minPoolSize="6"
				p:maxPoolSize="18"
				p:maxIdleTimeExcessConnections="120"
				p:checkoutTimeout="10000"
				p:acquireIncrement="6"
				p:acquireRetryAttempts="5"
				p:acquireRetryDelay="2000"
				p:idleConnectionTestPeriod="30"
				p:preferredTestQuery="select 1"/>
		<!--end  從數據庫中的用戶表中讀取 -->

  • 把如下jar包拷貝到lib下
    在這裏插入圖片描述
  • 配置查詢語句
修改cas.properties 220行,去掉註釋,修改如下:

    cas.jdbc.authn.query.sql=select password from users where username=?

由於此時我們指定的加密算法爲md5了,所以我們需要重新增加一條md5加密的數據到數據庫去。

INSERT INTO users(username,password,enabled)VALUES('itheima',(SELECT MD5('123456')),'true');

重啓tomcat,用itheima登錄成功。

CSA實現單點登錄功能

新建一個maven工程,配置一下CAS相關過濾器,然後訪問首頁。
pom依賴

    <dependency>
      <groupId>org.jasig.cas.client</groupId>
      <artifactId>cas-client-core</artifactId>
      <version>3.3.3</version>
    </dependency>
    <dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
			<scope>provided</scope>
	</dependency>
	<build>
		<plugins>
			<!-- 配置Tomcat插件 -->
			<plugin>
				<groupId>org.apache.tomcat.maven</groupId>
				<artifactId>tomcat7-maven-plugin</artifactId>
				<configuration>
					<port>70</port>
					<!-- http://127.0.0.1:{port}/{path} -->
					<path>/</path>
				</configuration>
			</plugin>

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
				</configuration>
			</plugin>
		</plugins>
	</build>

web.xml

<!-- 用於單點退出,該過濾器用於實現單點登出功能,可選配置 -->
  <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>CASFilter</filter-name>
    <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
    <init-param>
      <param-name>casServerLoginUrl</param-name>
       <!--這是cas 服務端登陸認證地址 -->
      <param-value>https://shemuel:8443/cas/login</param-value>
     
    </init-param>
    <init-param>
      <param-name>serverName</param-name>
      <param-value>http://shemuel:70</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>CASFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  
  
  <!--登陸成功後cas server會重定向到之前要訪問的頁面並攜帶一個ticket,該ticket需要重新進行校驗認證。 以下過濾器負責對Ticket的校驗工作,必須啓用它 -->
  <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://shemuel:8443/cas</param-value>
    </init-param>
    <init-param>
      <param-name>serverName</param-name>
      <param-value>http://shemuel:70</param-value>
    </init-param>
  </filter>
  <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>

tomcat運行,端口70,出現如下報錯
在這裏插入圖片描述
上面錯誤的原因是因爲CAS默認值接受HTTPS有IMAPS協議請求的認證,我們需要把HTTP也給加上去。

打開下載的cas項目: cas\WEB-INF\classes\services\HTTPSandIMAPS-10000001.json,修改第3行
"serviceId" : "^(https|imaps)://.*",

改成

"serviceId" : "^(https|imaps|http)://.*",

保存,並重新啓動即可。

拷貝casclient-demo1 模擬兩個客戶端

拷貝casclien-demo1 改名casclient-demo2 部署,tomcat端口18083,進行單點測試。一個站點登錄後另外一個站點就不需要登錄了。
如果運營一旦出現如下錯誤,請調整tomcat的JDK,和生成祕鑰的JDK保持一致,不要用SDK即可。

java.lang.RuntimeException: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	org.jasig.cas.client.util.CommonUtils.getResponseFromServer(CommonUtils.java:407)
	org.jasig.cas.client.validation.AbstractCasProtocolUrlBasedTicketValidator.retrieveResponseFromServer(AbstractCasProtocolUrlBasedTicketValidator.java:45)
	org.jasig.cas.client.validation.AbstractUrlBasedTicketValidator.validate(AbstractUrlBasedTicketValidator.java:200)
	org.jasig.cas.client.validation.AbstractTicketValidationFilter.doFilter(AbstractTicketValidationFilter.java:206)
	org.jasig.cas.client.authentication.AuthenticationFilter.doFilter(AuthenticationFilter.java:161)
	org.jasig.cas.client.session.SingleSignOutFilter.doFilter(SingleSignOutFilter.java:100)

	Root Cause
	
	javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
		sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
		sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
		sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
		sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
		sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1509)
		sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
		sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
		sun.security.ssl.Handshaker.process_record(Handshaker.java:914)
		sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
		sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
		sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
		sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
		sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
		sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
		sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1513)
		sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441)
		sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
		org.jasig.cas.client.util.CommonUtils.getResponseFromServer(CommonUtils.java:393)
		org.jasig.cas.client.validation.AbstractCasProtocolUrlBasedTicketValidator.retrieveResponseFromServer(AbstractCasProtocolUrlBasedTicketValidator.java:45)
		org.jasig.cas.client.validation.AbstractUrlBasedTicketValidator.validate(AbstractUrlBasedTicketValidator.java:200)
		org.jasig.cas.client.validation.AbstractTicketValidationFilter.doFilter(AbstractTicketValidationFilter.java:206)
		org.jasig.cas.client.authentication.AuthenticationFilter.doFilter(AuthenticationFilter.java:161)
		org.jasig.cas.client.session.SingleSignOutFilter.doFilter(SingleSignOutFilter.java:100)
	
	Root Cause
	
	sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
		sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:387)
		sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
		sun.security.validator.Validator.validate(Validator.java:260)
		sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
		sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
		sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
		sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1491)
		sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
		sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
		sun.security.ssl.Handshaker.process_record(Handshaker.java:914)
		sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
		sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
		sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
		sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
		sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
		sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
		sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1513)
		sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441)
		sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
		org.jasig.cas.client.util.CommonUtils.getResponseFromServer(CommonUtils.java:393)
		org.jasig.cas.client.validation.AbstractCasProtocolUrlBasedTicketValidator.retrieveResponseFromServer(AbstractCasProtocolUrlBasedTicketValidator.java:45)
		org.jasig.cas.client.validation.AbstractUrlBasedTicketValidator.validate(AbstractUrlBasedTicketValidator.java:200)
		org.jasig.cas.client.validation.AbstractTicketValidationFilter.doFilter(AbstractTicketValidationFilter.java:206)
		org.jasig.cas.client.authentication.AuthenticationFilter.doFilter(AuthenticationFilter.java:161)
		org.jasig.cas.client.session.SingleSignOutFilter.doFilter(SingleSignOutFilter.java:100)

cas服務端和兩個客戶端都啓動後,我們訪問其中一個客戶端登錄後,另一個客戶端就不需要登錄了說明成功!
在這裏插入圖片描述
至此簡易的單點登錄系統就完成了,後面會介紹springsecurity 如何整合cas

參考:傳智播客教學視頻
傳智播客官網: http://itcast.cn

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章