13.7 CAS認證
JA-SIG生產一種稱為CAS的企業級單點登錄系統。與其他計劃不同,JA-SIG的中央身份驗證服務是開源的,廣泛使用的,易于理解,獨立于平臺,并支持代理功能。Spring Security完全支持CAS,并提供從Spring Security的單應用程序部署到企業級CAS服務器保護的多應用程序部署的輕松遷移路徑。
您可以在https://www.apereo.org上了解有關CAS的更多信息。您還需要訪問此站點以下載CAS Server文件。
雖然CAS網站包含詳細說明CAS體系結構的文檔,但我們在Spring Security的上下文中再次提供了一般概述。Spring Security 3.x支持CAS 3.在編寫本文時,CAS服務器的版本為3.4。
在企業的某個地方,您需要設置CAS服務器。CAS服務器只是一個標準的WAR文件,因此設置服務器沒有任何困難。在WAR文件中,您將自定義向用戶顯示的登錄頁面和其他單點登錄頁面。
部署CAS 3.4服務器時,還需要AuthenticationHandler在deployerConfigContext.xmlCAS附帶中指定一個。該AuthenticationHandler有一個返回一個布爾值作為一組給定的證書是否有效的簡單方法。您的AuthenticationHandler實現需要鏈接到某種類型的后端身份驗證存儲庫,例如LDAP服務器或數據庫。CAS本身包括許多AuthenticationHandler開箱即用的協助。下載并部署服務器war文件時,它設置為成功驗證輸入與其用戶名匹配的密碼的用戶,這對測試很有用。
除CAS服務器本身外,其他主要參與者當然是整個企業中部署的安全Web應用程序。這些Web應用程序稱為“服務”。有三種類型的服務。那些驗證服務票證的,可以獲得代理票證的那些,以及驗證代理票證的那些。驗證代理票證的方式不同,因為必須驗證代理列表,并且通常可以重復使用代理票證。
Spring Security和CAS交互序列
Web瀏覽器,CAS服務器和Spring Security安全服務之間的基本交互如下:
Web用戶正在瀏覽服務的公共頁面。不涉及CAS或Spring Security。
用戶最終會請求一個安全的頁面,或者它使用的一個bean是安全的。Spring SecurityExceptionTranslationFilter將檢測AccessDeniedException或AuthenticationException。
因為用戶的Authentication對象(或缺少它)引起了AuthenticationException,所以ExceptionTranslationFilter會調用配置的AuthenticationEntryPoint。如果使用CAS,這將是CasAuthenticationEntryPoint該類。
在CasAuthenticationEntryPoint將用戶的瀏覽器重定向到CAS服務器。它還將指示一個service參數,它是Spring Security服務(您的應用程序)的回調URL。例如,瀏覽器重定向到的URL可能是https://my.company.com/cas/login?service=https%3A%2F%2Fserver3.company.com%2Fwebapp%2Flogin/cas。
用戶的瀏覽器重定向到CAS后,系統會提示他們輸入用戶名和密碼。如果用戶提供了一個表明他們之前已登錄的會話cookie,則不會再提示他們再次登錄(此過程有一個例外,我們將在后面介紹)。CAS將使用上面討論的PasswordHandler(或AuthenticationHandler使用CAS 3.0)來確定用戶名和密碼是否有效。
成功登錄后,CAS會將用戶的瀏覽器重定向回原始服務。它還將包含一個ticket參數,該參數是表示“服務票證”的不透明字符串。繼續我們之前的示例,瀏覽器重定向到的URL可能是https://server3.company.com/webapp/login/cas?ticket=ST-0-ER94xMJmn6pha35CQRoZ。
回到服務Web應用程序,CasAuthenticationFilter總是在監聽請求/login/cas(這是可配置的,但我們將使用本簡介中的默認值)。處理過濾器將構造UsernamePasswordAuthenticationToken表示服務票證的過程。委托人將等于CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER,而憑證將是服務票不透明值。然后,此身份驗證請求將傳遞給已配置的AuthenticationManager。
該AuthenticationManager實施將是ProviderManager,其又配置了CasAuthenticationProvider。將CasAuthenticationProvider只響應UsernamePasswordAuthenticationToken包含CAS特異性主要(如SCasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER)和CasAuthenticationTokenS(稍后討論)。
CasAuthenticationProvider將使用TicketValidator實現驗證服務票證。這通常是Cas20ServiceTicketValidatorCAS客戶端庫中包含的類之一。如果應用程序需要驗證代理票證,Cas20ProxyTicketValidator則使用該代理票證。在TicketValidator以驗證服務票證發出HTTPS請求到CAS服務器。它還可能包含代理回調URL,該URL包含在此示例中:https://my.company.com/cas/proxyValidate?service = https%3A%2F%2Fserver3.company.com%2Fwebapp%2Flogin/cas&ticket= ST-0-ER94xMJmn6pha35CQRoZ&pgtUrl = https://server3.company.com/webapp/login/cas/proxyreceptor。
返回CAS服務器,將收到驗證請求。如果提供的服務票證與發出票證的服務URL相匹配,CAS將以XML格式提供肯定響應,指示用戶名。如果身份驗證中涉及任何代理(如下所述),則代理列表也包含在XML響應中。
[可選]如果對CAS驗證服務的請求包含代理回調URL(在pgtUrl參數中),CAS將pgtIou在XML響應中包含一個字符串。這pgtIou表示代理授予票證IOU。然后CAS服務器將創建自己的HTTPS連接回pgtUrl。這是為了相互驗證CAS服務器和聲明的服務URL。HTTPS連接將用于將代理授予票證發送到原始Web應用程序。例如,https://server3.company.com/webapp/login/cas/proxyreceptor?pgtIou=PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt&pgtId=PGT-1-si9YkkHLrtACBo64rmsi3v2nf7cpCResXg5MpESZFArbaZiOKH。
該Cas20TicketValidator會解析從CAS服務器接收到的XML。它將返回到CasAuthenticationProvideraTicketResponse,其中包括用戶名(必填),代理列表(如果涉及)和代理授予票證IOU(如果請求代理回調)。
接下來CasAuthenticationProvider將調用已配置的CasProxyDecider。該CasProxyDecider指示在代理列表是否TicketResponse是可以接受的服務。多次實現Spring Security提供:RejectProxyTickets,AcceptAnyCasProxy和NamedCasProxyDecider。這些名稱在很大程度上是不言自明的,除了NamedCasProxyDecider允許提供List可信代理。
CasAuthenticationProvider接下來將請求AuthenticationUserDetailsService加載GrantedAuthority適用于包含在中的用戶的對象Assertion。
如果沒有問題,CasAuthenticationProvider構建一個CasAuthenticationToken包含TicketResponse和GrantedAuthoritys中包含的細節。
然后控制返回到CasAuthenticationFilter,將創建的內容CasAuthenticationToken放在安全上下文中。
用戶的瀏覽器將重定向到導致AuthenticationException(或自定義目標取決于配置)的原始頁面。
你還在這里真好!我們現在來看看它是如何配置的
由于Spring Security,CAS的Web應用程序端變得簡單。假設您已經了解使用Spring Security的基礎知識,因此下面不再介紹這些內容。我們假設正在使用基于命名空間的配置,并根據需要添加CAS bean。每個部分都基于上一部分。可以在Spring Security Samples中找到完整的CAS示例應用程序。
服務票證認證
本節介紹如何設置Spring Security以驗證服務票證。通常,這是Web應用程序所需的全部內容。您需要將ServicePropertiesbean?添加到應用程序上下文中。這代表您的CAS服務:
<bean id="serviceProperties"
? ? class="org.springframework.security.cas.ServiceProperties">
<property name="service"
? ? value="https://localhost:8443/cas-sample/login/cas"/>
<property name="sendRenew" value="false"/>
</bean>
本service必須是一個被監控的URLCasAuthenticationFilter。該sendRenew應用是否特別敏感默認為false,但應設置為true。此參數的作用是告訴CAS登錄服務單點登錄是不可接受的。相反,用戶需要重新輸入用戶名和密碼才能訪問該服務。
應配置以下bean以啟動CAS身份驗證過程(假設您正在使用命名空間配置):
<security:http entry-point-ref="casEntryPoint">
...
<security:custom-filter position="CAS_FILTER" ref="casFilter" />
</security:http>
<bean id="casFilter"
? ? class="org.springframework.security.cas.web.CasAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<bean id="casEntryPoint"
? ? class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<property name="loginUrl" value="https://localhost:9443/cas/login"/>
<property name="serviceProperties" ref="serviceProperties"/>
</bean>
要使CAS運行,ExceptionTranslationFilter必須將其authenticationEntryPoint屬性設置為CasAuthenticationEntryPointbean。這可以使用入口點ref輕松完成,如上例所示。在CasAuthenticationEntryPoint必須引用ServiceProperties豆(如上所述),它提供的URL企業CAS登錄服務器。這是用戶瀏覽器重定向的位置。
它CasAuthenticationFilter具有非常相似的屬性UsernamePasswordAuthenticationFilter(用于基于表單的登錄)。您可以使用這些屬性來自定義身份驗證成功和失敗等行為。
接下來,您需要添加一個CasAuthenticationProvider及其協作者:
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="casAuthenticationProvider" />
</security:authentication-manager>
<bean id="casAuthenticationProvider"
? ? class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<property name="authenticationUserDetailsService">
? ? <bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
? ? <constructor-arg ref="userService" />
? ? </bean>
</property>
<property name="serviceProperties" ref="serviceProperties" />
<property name="ticketValidator">
? ? <bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
? ? <constructor-arg index="0" value="https://localhost:9443/cas" />
? ? </bean>
</property>
<property name="key" value="an_id_for_this_auth_provider_only"/>
</bean>
<security:user-service id="userService">
<!-- Password is prefixed with {noop} to indicate to DelegatingPasswordEncoder that
NoOpPasswordEncoder should be used.
This is not safe for production, but makes reading
in samples easier.
Normally passwords should be hashed using BCrypt -->
<security:user name="joe" password="{noop}joe" authorities="ROLE_USER" />
...
</security:user-service>
該CasAuthenticationProvider使用UserDetailsService實例來加載當局用戶,一旦他們被CAS認證。我們在這里展示了一個簡單的內存設置。請注意,CasAuthenticationProvider實際上并不使用密碼進行身份驗證,但它確實使用了權限。
如果您再參考CAS工作原理部分,那么這些bean都是相當不言自明的。
這樣就完成了CAS的最基本配置。如果您沒有犯任何錯誤,您的Web應用程序應該在CAS單點登錄框架內愉快地工作。Spring Security的其他任何部分都不需要擔心CAS處理身份驗證的事實。在以下部分中,我們將討論一些(可選)更高級的配置。
單點注銷
CAS協議支持單點注銷,可以輕松添加到Spring Security配置中。以下是處理Single Logout的Spring Security配置的更新
<security:http entry-point-ref="casEntryPoint">
...
<security:logout logout-success-url="/cas-logout.jsp"/>
<security:custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER"/>
<security:custom-filter ref="singleLogoutFilter" before="CAS_FILTER"/>
</security:http>
<!-- This filter handles a Single Logout Request from the CAS Server -->
<bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter"/>
<!-- This filter redirects to the CAS Server to signal Single Logout should be performed -->
<bean id="requestSingleLogoutFilter"
? ? class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg value="https://localhost:9443/cas/logout"/>
<constructor-arg>
? ? <bean class=
? ? ? ? "org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
</constructor-arg>
<property name="filterProcessesUrl" value="/logout/cas"/>
</bean>
該logout元素將用戶從本地應用程序中注銷,但不會終止與CAS服務器或已登錄的任何其他應用程序的會話。該requestSingleLogoutFilter過濾器將允許的URL/spring_security_cas_logout被請求重定向到應用程序的配置CAS服務器注銷URL。然后CAS服務器將向登錄的所有服務發送Single Logout請求。該singleLogoutFilter處理通過查找單注銷請求HttpSession靜態Map,然后無效的。
可能會混淆為什么需要logout元素和元素singleLogoutFilter。首先在本地注銷是最佳實踐,因為SingleSignOutFilter只是將其存儲HttpSession在靜態Map中以便在其上調用invalidate。通過上面的配置,注銷流程將是:
用戶請求/logout將用戶從本地應用程序中注銷并將用戶發送到注銷成功頁面。
注銷成功頁面/cas-logout.jsp應指示用戶單擊指向的鏈接/logout/cas以退出所有應用程序。
當用戶單擊該鏈接時,該用戶將被重定向到CAS單一注銷URL(https:// localhost:9443 / cas / logout)。
在CAS服務器端,CAS單一注銷URL然后向所有CAS服務提交單個注銷請求。在CAS服務方面,JASIGSingleSignOutFilter通過使原始會話無效來處理注銷請求。
下一步是將以下內容添加到web.xml中
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>
? ? org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
? ? <param-name>encoding</param-name>
? ? <param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>
? ? org.jasig.cas.client.session.SingleSignOutHttpSessionListener
</listener-class>
</listener>
使用SingleSignOutFilter時,您可能會遇到一些編碼問題。因此,建議添加CharacterEncodingFilter以確保使用時字符編碼正確SingleSignOutFilter。再次,請參閱JASIG的文檔以獲取詳細信息。的SingleSignOutHttpSessionListener確保,當HttpSession期滿時,用于單注銷的映射被去除。
使用CAS對無狀態服務進行身份驗證
本節介紹如何使用CAS對服務進行身份驗證。換句話說,本節討論如何設置使用通過CAS進行身份驗證的服務的客戶端。下一節將介紹如何使用CAS設置無狀態服務以進行身份??驗證。
配置CAS以獲取代理授予票證
為了對無狀態服務進行身份驗證,應用程序需要獲取代理授予票證(PGT)。本節介紹如何配置Spring Security以獲取基于thencas-st [Service Ticket Authentication]配置的PGT構建。
第一步是ProxyGrantingTicketStorage在Spring Security配置中包含一個。這用于存儲通過CasAuthenticationFilter它們獲得的PGT,以便它們可用于獲取代理票據。示例配置如下所示
<!--
NOTE: In a real application you should not use an in memory implementation.
You will also want to ensure to clean up expired tickets by calling
ProxyGrantingTicketStorage.cleanup()
-->
<bean id="pgtStorage" class="org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl"/>
下一步是更新CasAuthenticationProvider以獲得代理票證。要做到這一點,請Cas20ServiceTicketValidator用a?替換Cas20ProxyTicketValidator。該proxyCallbackUrl應設置為應用程序將獲得PGT的一個URL。最后,配置也應該引用ProxyGrantingTicketStorage它,以便它可以使用PGT來獲取代理票據。您可以在下面找到應該進行的配置更改的示例。
<bean id="casAuthenticationProvider"
? ? class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
...
<property name="ticketValidator">
? ? <bean class="org.jasig.cas.client.validation.Cas20ProxyTicketValidator">
? ? <constructor-arg value="https://localhost:9443/cas"/>
? ? ? ? <property name="proxyCallbackUrl"
? ? ? ? value="https://localhost:8443/cas-sample/login/cas/proxyreceptor"/>
? ? <property name="proxyGrantingTicketStorage" ref="pgtStorage"/>
? ? </bean>
</property>
</bean>
最后一步是更新CasAuthenticationFilter接受PGT并將它們存儲在ProxyGrantingTicketStorage。這一點很重要的proxyReceptorUrl比賽proxyCallbackUrl的Cas20ProxyTicketValidator。示例配置如下所示。
<bean id="casFilter"
? ? ? ? class="org.springframework.security.cas.web.CasAuthenticationFilter">
? ? ...
? ? <property name="proxyGrantingTicketStorage" ref="pgtStorage"/>
? ? <property name="proxyReceptorUrl" value="/login/cas/proxyreceptor"/>
</bean>
使用代理票證調用無狀態服務
現在Spring Security獲得了PGT,您可以使用它們來創建可用于對無狀態服務進行身份驗證的代理票據。該CAS示例應用程序包含了一個工作示例ProxyTicketSampleServlet。示例代碼可以在下面找到:
protected void doGet(HttpServletRequest request, HttpServletResponse response)
? ? throws ServletException, IOException {
// NOTE: The CasAuthenticationToken can also be obtained using
// SecurityContextHolder.getContext().getAuthentication()
final CasAuthenticationToken token = (CasAuthenticationToken) request.getUserPrincipal();
// proxyTicket could be reused to make calls to the CAS service even if the
// target url differs
final String proxyTicket = token.getAssertion().getPrincipal().getProxyTicketFor(targetUrl);
// Make a remote call using the proxy ticket
final String serviceUrl = targetUrl+"?ticket="+URLEncoder.encode(proxyTicket, "UTF-8");
String proxyResponse = CommonUtils.getResponseFromServer(serviceUrl, "UTF-8");
...
}
代理票證認證
的CasAuthenticationProvider狀態和無狀態的客戶進行了區分。一個有狀態的客戶機被認為是任何提交給filterProcessUrl的CasAuthenticationFilter。無狀態客戶端是指向CasAuthenticationFilter除了以外的URL?的身份驗證請求的任何客戶端filterProcessUrl。
由于遠程處理協議無法在a的上下文中呈現自身HttpSession,因此不可能依賴于在請求之間的會話中存儲安全性上下文的默認實踐。此外,由于CAS服務器在經過驗證后使其無效,因此TicketValidator在后續請求中顯示相同的代理票證將不起作用。
一個顯而易見的選擇是根本不使用CAS來遠程協議客戶端。但是,這將消除CAS的許多理想特征。作為中間地帶,CasAuthenticationProvider使用aStatelessTicketCache。這僅用于使用等于的主體的無狀態客戶端CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER。會發生什么事是CasAuthenticationProvider將存儲所產生CasAuthenticationToken的StatelessTicketCache,鍵上的代理票。因此,遠程協議客戶端可以呈現相同的代理票證,并且CasAuthenticationProvider不需要聯系CAS服務器進行驗證(除了第一個請求之外)。經過身份驗證后,代理票證可用于除原始目標服務之外的URL。
本節以前面的部分為基礎,以適應代理票證身份驗證。第一步是指定驗證所有工件,如下所示。
<bean id="serviceProperties"
? ? class="org.springframework.security.cas.ServiceProperties">
...
<property name="authenticateAllArtifacts" value="true"/>
</bean>
下一步是指定serviceProperties和authenticationDetailsSource為CasAuthenticationFilter。該serviceProperties屬性指示CasAuthenticationFilter嘗試對所有工件進行身份驗證,而不是僅對存在的工件進行身份驗證filterProcessUrl。在ServiceAuthenticationDetailsSource一個創建ServiceAuthenticationDetails,以確保當前的URL,基于HttpServletRequest被驗證檢票時使用的服務URL。可以通過注入AuthenticationDetailsSource返回自定義的自定義來自定義用于生成服務URL的方法ServiceAuthenticationDetails。
<bean id="casFilter"
? ? class="org.springframework.security.cas.web.CasAuthenticationFilter">
...
<property name="serviceProperties" ref="serviceProperties"/>
<property name="authenticationDetailsSource">
? ? <bean class=
? ? "org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource">
? ? <constructor-arg ref="serviceProperties"/>
? ? </bean>
</property>
</bean>
您還需要更新CasAuthenticationProvider處理代理票證。要做到這一點,請Cas20ServiceTicketValidator用a?替換Cas20ProxyTicketValidator。您需要配置statelessTicketCache要接受的代理和代理。您可以找到接受以下所有代理所需的更新示例。
<bean id="casAuthenticationProvider"
? ? class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
...
<property name="ticketValidator">
? ? <bean class="org.jasig.cas.client.validation.Cas20ProxyTicketValidator">
? ? <constructor-arg value="https://localhost:9443/cas"/>
? ? <property name="acceptAnyProxy" value="true"/>
? ? </bean>
</property>
<property name="statelessTicketCache">
? ? <bean class="org.springframework.security.cas.authentication.EhCacheBasedTicketCache">
? ? <property name="cache">
? ? ? ? <bean class="net.sf.ehcache.Cache"
? ? ? ? ? ? init-method="initialise" destroy-method="dispose">
? ? ? ? <constructor-arg value="casTickets"/>
? ? ? ? <constructor-arg value="50"/>
? ? ? ? <constructor-arg value="true"/>
? ? ? ? <constructor-arg value="false"/>
? ? ? ? <constructor-arg value="3600"/>
? ? ? ? <constructor-arg value="900"/>
? ? ? ? </bean>
? ? </property>
? ? </bean>
</property>
</bean>
原文:https://docs.spring.io/spring-security/site/docs/5.1.5.RELEASE/reference/htmlsingle/#cas