SSO英文全稱Single Sign On,單點登錄。SSO是在多個應用系統中,用戶只需要登錄一次就可以訪問所有相互信任的應用系統。它包括可以將這次主要的登錄映射到其他應用中用於同一個用戶的登錄的機制。它是目前比較流行的企業業務整合的解決方案之一。
SSO的實現機制是:
當用戶第一次訪問應用系統1的時候,因爲還沒有登錄,會被引導到認證系統中進行登錄;根據用戶提供的登錄信息,認證系統進行身份校驗,如果通過校驗,應該返回給用戶一個認證的憑據--ticket;用戶再訪問別的應用的時候就會將這個ticket帶上,作爲自己認證的憑據,應用系統接受到請求之後會把ticket送到認證系統進行校驗,檢查ticket的合法性。如果通過校驗,用戶就可以在不用再次登錄的情況下訪問應用系統2和應用系統3了。
要實現SSO,需要以下主要的功能:
(1)系統共享
統一的認證系統是SSO的前提之一。認證系統的主要功能是將用戶的登錄信息和用戶信息庫相比較,對用戶進行登錄認證;認證成功後,認證系統應該生成統一的認證標誌(ticket),返還給用戶。另外,認證系統還應該對ticket進行校驗,判斷其有效性。
(2)信息識別
要實現SSO的功能,讓用戶只登錄一次,就必須讓應用系統能夠識別已經登錄過的用戶。應用系統應該能對ticket進行識別和提取,通過與認證系統的通訊,能自動判斷當前用戶是否登錄過,從而完成單點登錄的功能。
單點登錄在web項目中有着廣泛運用。比如,用戶在訪問頁面1的時候進行了登錄,但是客戶端的每個請求都是單獨的連接,當客戶再次訪問頁面2的時候,如何才能告訴Web服務器,客戶剛纔已經登錄過了呢?瀏覽器和服務器之間有約定:通過使用cookie技術來維護應用的狀態。Cookie是可以被Web服務器設置的字符串,並且可以保存在瀏覽器中。當瀏覽器訪問了頁面1時,web服務器設置了一個cookie,並將這個cookie和頁面1一起返回給瀏覽器,瀏覽器接到cookie之後,就會保存起來,在它訪問頁面2的時候會把這個cookie也帶上,Web服務器接到請求時也能讀出cookie的值,根據cookie值的內容就可以判斷和恢復一些用戶的信息狀態。Web-SSO完全可以利用Cookie技術來完成用戶登錄信息的保存,將瀏覽器中的Cookie和上文中的Ticket結合起來,完成SSO的功能。
爲了完成一個簡單的SSO的功能,需要兩個部分的合作:
(1)統一的身份認證服務。
(2)修改Web應用,使得每個應用都通過這個統一的認證服務來進行身份校驗。
目前,實現SSO的技術主要有:
(1)基於cookies實現。
(2) Broker-based(基於經紀人)。這種技術的特點是:有一個集中的認證和用戶帳號管理的服務器,爲認證提供一個公共和獨立的"第三方"。Kerberos是由麻省理工大學發明的安全認證服務,當前版本V5,已經被UNIX和Windows作爲默認的安全認證服務集成進操作系統。
(3) Agent-based(基於代理人)。在這種解決方案中,有一個自動地爲不同的應用程序認證用戶身份的代理程序。這個代理程序需要設計有不同的功能。比如,它可以使用口令表或加密密鑰來自動地將認證的負擔從用戶移開。代理人被放在服務器上面,在服務器的認證系統和客戶端認證方法之間充當一個"翻譯"。例如SSH等。
SSH,全稱Secure Shell,安全外殼協議,是建立在應用層基礎上的安全協議。SSH 是目前較可靠,專爲遠程登錄會話和其他網絡服務提供安全性的協議。利用 SSH 協議可以有效防止遠程管理過程中的信息泄露問題。
(4) Token-based。例如SecurID,WebID,現在被廣泛使用的口令認證,比如FTP,郵件服務器的登錄認證,這是一種簡單易用的方式,實現一個口令在多種應用當中使用。
(5) 基於網關Agent and Broker-based。
(6) 基於安全斷言標記語言(SAML)實現。SAML(Security Assertion Markup Language,安全斷言標記語言)的出現大大簡化了SSO,並被OASIS批准爲SSO的執行標準。開源組織OpenSAML 實現了 SAML 規範。
(7)CAS由耶魯大學開發的單點登錄系統(SSO,single sign-on),應用廣泛,具有獨立於平臺,易於理解,支持代理的功能。
接下來,我們先實現CAS的初步搭建,然後實現多個應用系統基於CAS進行單點登錄。
一.初步搭建CAS
1.把CAS的server發佈到一臺獨立的tomcat上。
從網上下載cas-server-4.0.0-release.zip,解壓後,拷貝出cas-server-4.0.0\modules\cas-server-webapp-4.0.0.war,爲了簡化起見,將cas-server-webapp-4.0.0.war更名爲cas-server.war,然後將這個war文件拷貝到到一個tomcat的webapps文件夾下。tomcat啓動時,會自動解壓war文件。
2.配置https。
由於CAS單點登錄爲了保障安全,因此必須基於https來搭建。
(1)鑑於https的要求,我們以win7系統爲例,需要在C:\Windows\System32\drivers\etc\hosts中配置幾個域名,如下:
127.0.0.1 dddr.money.com
127.0.0.1 dddr.store.com
127.0.0.1 dddr.cas.com
(2)生成ssl需要的證書。
①生成keystore:
keytool -genkey -alias dddritszt -keyalg RSA -keysize 1024 -keypass 123456 -validity 365 -keystore C:\Users\Administrator\Desktop\cas\ssl\dddritszt.keystore -storepass 123456
②轉換keystore:
keytool -export -alias dddritszt -keystore C:\Users\Administrator\Desktop\cas\ssl\dddritszt.keystore -file C:\Users\Administrator\Desktop\cas\ssl\dddritszt.crt -storepass 123456
③信任證書【注意:JDK的java_home的路徑中有中文或者空格都不行,比如我設置的java_home值爲:E:\Java\jdk1.8.0_131】:
keytool -import -keystore %JAVA_HOME%\jre\lib\security\cacerts -file C:\Users\Administrator\Desktop\cas\ssl\dddritszt.crt -alias dddritszt
這一步按回車鍵後,會提示輸入密碼,請輸入:changeit
由於要將證書導入jdk,因此輸入的是jdk默認的祕鑰。
(3)給tomcat配置https。在tomcat的conf/server.xml文件中添加下述配置:
<Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
keystoreFile="C:\Users\Administrator\Desktop\cas\ssl\dddritszt.keystore" keystorePass="123456"
clientAuth="false" sslProtocol="TLS" URIEncoding="UTF-8"/>
到此就搭建好了CAS,接着開啓tomcat服務器,在瀏覽器的地址欄中輸入 https://dddr.cas.com:8443/cas-server/login,顯示頁面如下:
我在網上看到,有人說爲了找出用戶名和密碼應該填什麼,折騰了4個小時,在這裏提醒大家,CAS默認,用戶名是:casuser,密碼是:Mellon。登錄成功後,頁面顯示如下:
我們還可以通過CAS登出,在瀏覽器地址欄中修改url路徑爲 https://dddr.cas.com:8443/cas-server/logout,顯示頁面如下:
在演示完畢搭建CAS服務後,我們接下來進行多個應用系統在CAS上實現單點登錄。
二.多個應用系統在CAS上實現單點登錄。
1.再部署兩個tomcat,http訪問端口分別爲17070和18080,這兩個tomcat上將發佈我們的測試案例。
(1)在idea中創建一個webApp項目,名稱爲“DDDR_CAS_Money”,在該項目的web/WEB-INF下新建一個lib文件夾,拷貝CAS客戶端的一個jar包(此處爲cas-client-core-3.3.3.jar)到該lib文件夾下,並將這個jar包配置爲項目依賴。
注意,該項目的SDK(Software Development Kit,軟件開發工具包),此處主要是指Java,要和添加證書信賴的Java路徑一致。
另外,本項目所在的tomcat的conf/server.xml文件中,端口號不要和CAS服務器上的端口號衝突。我是這樣配置的,親測後,可以正常訪問:
<Server port="8085" shutdown="SHUTDOWN">
...
<Connector port="18080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="18443" />
...
<Connector port="8009" protocol="AJP/1.3" redirectPort="18443" />
</Server>
該項目下的web.xml文件配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- ======================== 單點登錄開始 ======================== -->
<!-- 用於單點退出,該過濾器用於實現單點登出功能,可選配置-->
<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 Filter</filter-name>
<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
<init-param>
<param-name>casServerLoginUrl</param-name>
<param-value>https://dddr.cas.com:8443/cas-server/login</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://dddr.money.com:18080</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CAS Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 該過濾器負責對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://dddr.cas.com:8443/cas-server</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://dddr.money.com:18080</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>
<!-- ======================== 單點登錄結束 ======================== -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
該項目的index.jsp文件:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>cas-money</title>
</head>
<body>
我是cas-money...
</body>
</html>
接着,在確保CAS服務器開啓的情況下,在瀏覽器地址欄裏輸入 http://dddr.money.com:18080/index.jsp,會跳轉到 https://dddr.cas.com:8443/cas-server/login登錄頁面,用戶名和密碼分別輸入(用戶名:casuser,密碼:Mellon),登錄成功後會跳轉到http://dddr.money.com:18080/index.jsp的頁面。
(2)仿照上面步驟,我們再建一個web案例,名稱爲DDDR_CAS_store。該項目下的web.xml也仿照上面的案例,注意ur要改爲自己的。index.jsp爲:
<%@ page import="org.jasig.cas.client.validation.Assertion" %>
<%@ page import="org.jasig.cas.client.util.AssertionHolder" %>
<%@ page import="java.security.Principal" %>
<%@ page import="org.jasig.cas.client.util.AbstractCasFilter" %>
<%@ page import="java.util.Map" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
我是DDDR_CAS_store....
<hr>
<dl>
<dt>Your user name:</dt>
<dd>1、通過getRemoteUser來獲取信息:
<%= request.getRemoteUser() == null ? "null" : request.getRemoteUser() %>
</dd>
<br>
<dd>2、通過getUserPrincipal來獲取信息:
<%= request.getRemoteUser() == null ? "null" : request.getUserPrincipal() %>
</dd>
<br>
<dd>3、通過Assertion來獲取信息:<%
Assertion ass = AssertionHolder.getAssertion();
Principal p = ass.getPrincipal();
String name = p.getName();
out.print(name);
%></dd>
<br>
<dd>4、從session中獲取Assertion對象,建議使用AssertionHolder:
<%
ass = (Assertion) session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
String nm = ass.getPrincipal().getName();
out.print(nm);
%></dd>
<br>
<dd>
<%
Map m = ass.getPrincipal().getAttributes();
out.print(m.size());
%>
</dd>
</dl>
</body>
</html>
由於前面的應用DDDR_CAS_Money已經在CAS上登錄,因此,在瀏覽器地址欄裏輸入 http://dddr.store.com:17070/index.jsp,會直接呈現下述頁面:
現在,我們已經實現了多個應用通過CAS實現單點登錄。接下來,我們定製化CAS頁面,進行登錄頁與登錄方式的自定義,使得頁面呈現的效果更符合我們想要的效果。
三.定製化CAS頁面風格
1.新建一個maven的web項目,名稱爲MyCAS_Server,將原先cas-server下的文件都拷貝到該項目的webapp文件夾下。
2.修改登錄和登出頁面的風格,然後在maven中執行package打包命令,將打出的war包替換CAS所在tomcat服務器下的cas-server即可。注意:要確保CAS的war包名稱一致。
四.CAS數據庫驗證
我們上面進行的CAS驗證,都是基於CAS自帶的用戶名和密碼進行驗證的,如果要對接我們的數據庫信息,首先將cas-server-4.0.0-release\cas-server-4.0.0\modules\cas-server-support-jdbc-4.0.0.jar以及對於數據庫的jar包加入到cas_server的lib目錄下。接着,修改WebRoot\WEB-INF\deployerConfigContext.xm文件,將下面代碼註釋掉:
<bean id="primaryAuthenticationHandler"
class="org.jasig.cas.authentication.AcceptUsersAuthenticationHandler">
<property name="users">
<map>
<entry key="casuser" value="Mellon"/>
</map>
</property>
</bean>
根據自己的數據庫配置信息,添加如下代碼,更新爲JDBC驗證方式:
<!-- 更新爲JDBC驗證方式 開始 -->
<!-- 配置數據源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>
<property name="url"><value>jdbc:mysql://localhost:3306/itszt4?characterEncoding=utf8</value></property>
<property name="username"><value>root</value></property>
<property name="password"><value>2018</value></property>
</bean>
<bean id="primaryAuthenticationHandler" class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
<property name="dataSource" ref="dataSource"></property>
<property name="sql" value="select userpwd from user where username=?"></property>
<!-- 如果需要加密,則配置加密器 ,我這裏不需要-->
<!-- <property name="passwordEncoder" ref="MD5PasswordEncoder"></property> -->
</bean>
<!-- 添加MD5密碼加密功能 -->
<bean id="MD5PasswordEncoder" class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder">
<constructor-arg index="0">
<value>MD5</value>
</constructor-arg>
</bean>
<!-- 更新爲JDBC驗證方式 結束 -->
接下來再進行CAS驗證,就改爲匹配數據庫表中的數據了。