1.1.1配置AuthenticationEntryPoint
首先需要做的是将應用的登入認證入口改為使用CasAuthenticationEntryPoint。是以首先我們需要配置一個CasAuthenticationEntryPoint對應的bean,然後指定需要進行登入認證時使用該AuthenticationEntryPoint。配置CasAuthenticationEntryPoint時需要指定一個ServiceProperties,該對象主要用來描述service(Cas概念)相關的屬性,主要是指定在Cas Server認證成功後将要跳轉的位址。
<!-- 指定登入入口為casEntryPoint -->
<security:http entry-point-ref="casEntryPoint">
...
</security:http>
<!-- 認證的入口 -->
<bean id="casEntryPoint"
class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<!-- Cas Server的登入位址,elim是我的計算機名 -->
<property name="loginUrl" value="https://elim:8443/cas/login" />
<!-- service相關的屬性 -->
<property name="serviceProperties" ref="serviceProperties" />
</bean>
<!-- 指定service相關資訊 -->
<bean id="serviceProperties"class="org.springframework.security.cas.ServiceProperties">
<!-- Cas Server認證成功後的跳轉位址,這裡要跳轉到我們的Spring Security應用,之後會由CasAuthenticationFilter處理,預設處理位址為/j_spring_cas_security_check -->
<property name="service"
value="http://elim:8080/app/j_spring_cas_security_check" />
</bean>
1.1.2配置CasAuthenticationFilter
之後我們需要配置一個CasAuthenticationFilter,并将其放置在Filter連結清單中CAS_FILTER的位置,以處理Cas Server認證成功後的頁面跳轉,用以在Spring Security中進行認證。該Filter會将Cas Server傳遞過來的ticket(Cas概念)封裝成一個Authentication(對應UsernamePasswordAuthenticationToken,其中ticket作為該Authentication的password),然後傳遞給AuthenticationManager進行認證。
<security:http entry-point-ref="casEntryPoint">
...
<security:custom-filter ref="casFilter" position="CAS_FILTER"/>
...
</security:http>
<bean id="casFilter"
class="org.springframework.security.cas.web.CasAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager" />
<!-- 指定處理位址,不指定時預設将會是“/j_spring_cas_security_check” -->
<property name="filterProcessesUrl" value="/j_spring_cas_security_check"/>
</bean>
1.1.3配置AuthenticationManager
CasAuthenticationFilter會将封裝好的包含Cas Server傳遞過來的ticket的Authentication對象傳遞給AuthenticationManager進行認證。我們知道預設的AuthenticationManager實作類為ProviderManager,而ProviderManager中真正進行認證的是AuthenticationProvider。是以接下來我們要在AuthenticationManager中配置一個能夠處理CasAuthenticationFilter傳遞過來的Authentication對象的AuthenticationProvider實作,CasAuthenticationProvider。CasAuthenticationProvider首先會利用TicketValidator(Cas概念)對Authentication中包含的ticket資訊進行認證。認證通過後将利用持有的AuthenticationUserDetailsService根據認證通過後回傳的Assertion對象中擁有的username加載使用者對應的UserDetails,即主要是加載使用者的相關權限資訊GrantedAuthority。然後構造一個CasAuthenticationToken進行傳回。之後的邏輯就是正常的Spring Security的邏輯了。
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="casAuthenticationProvider"/>
</security:authentication-manager>
<bean id="casAuthenticationProvider"
class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<!-- 通過username來加載UserDetails -->
<property name="authenticationUserDetailsService">
<beanclass="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<!-- 真正加載UserDetails的UserDetailsService實作 -->
<constructor-arg ref="userDetailsService" />
</bean>
</property>
<property name="serviceProperties" ref="serviceProperties" />
<!-- 配置TicketValidator在登入認證成功後驗證ticket -->
<property name="ticketValidator">
<bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
<!-- Cas Server通路位址的字首,即根路徑-->
<constructor-arg index="0" value="https:// elim:8443/cas" />
</bean>
</property>
<property name="key" value="key4CasAuthenticationProvider" />
</bean>
<bean id="userDetailsService"
class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource" />
</bean>
經過以上三步配置以後,我們的Spring Security應用就已經跟Cas整合好了,可以在需要登入的時候通過Cas Server進行單點登入了。
1.2 單點登出
Spring Security應用整合Cas Client配置單點登出功能實際和單獨使用Cas Client配置單點登出功能一樣,其根本都是通過配置一個SingleSignOutFilter響應Cas Server單點登出時的回調,配置一個SingleSignOutHttpSessionListener用于在Session過期時删除SingleSignOutFilter存放的對應資訊。SingleSignOutFilter需要配置在Cas 的AuthenticationFilter之前,對于Spring Security應用而言,該Filter通常是配置在Spring Security的配置檔案中,而且是配置在CAS_FILTER之前。是以我們可以在Spring Security的配置檔案中進行如下配置。
<security:http entry-point-ref="casEntryPoint">
<!-- SingleSignOutFilter放在CAS_FILTER之前 -->
<security:custom-filter ref="casLogoutFilter" before="CAS_FILTER"/>
<security:custom-filter ref="casFilter" position="CAS_FILTER"/>
...
</security:http>
<bean id="casLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter"/>
然後跟單獨使用Cas Client一樣,在web.xml檔案中配置一個SingleSignOutHttpSessionListener。
<listener>
<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
</listener>
經過以上配置在通路Cas Server的logout位址(如:https:elim:8443/cas/logout)進行登出時,Cas Server登出後将回調其中注冊的每一個Service(Cas概念,即client應用),此時在client應用中配置好的SingleSignOutFilter将處理對應Client應用的登出操作。
雖然以上配置可以滿足我們在Spring Security應用中的單點登出要求,但Cas官方文檔和Spring Security官方文檔都推薦我們在Cas Client應用進行登出操作時,不是直接通路Cas Server的logout,而是先登出本應用,然後告訴使用者其目前登出的隻是本應用,再提供一個對應Cas Server的連結,使其可以進行真正的單點登出。對此,Spring Security官方文檔中給我們提供例子是提供兩個LogoutFilter,一個是登出目前Spring Security應用,一個是登出Cas Server的。
<security:http entry-point-ref="casEntryPoint">
<!-- 請求登出Cas Server的過濾器,放在Spring Security的登出過濾器之前 -->
<security:custom-filter ref="requestCasLogoutFilter" before="LOGOUT_FILTER"/>
<!-- SingleSignOutFilter放在CAS_FILTER之前 -->
<security:custom-filter ref="casLogoutFilter" before="CAS_FILTER"/>
<security:custom-filter ref="casFilter" position="CAS_FILTER"/>
...
</security:http>
<bean id="requestCasLogoutFilter"class="org.springframework.security.web.authentication.logout.LogoutFilter">
<!-- 指定登出成功後需要跳轉的位址,這裡指向Cas Server的登出URL,以實作單點登出 -->
<constructor-arg value="https://elim:8443/cas/logout"/>
<constructor-arg>
<beanclass="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
</constructor-arg>
<!-- 該Filter需要處理的位址,預設是Spring Security的預設登出位址“/j_spring_security_logout”-->
<property name="filterProcessesUrl" value="/j_spring_cas_security_logout"/>
</bean>
此外,Spring Security推薦我們在使用Cas Server的單點登出時一起使用CharacterEncodingFilter,以避免SingleSignOutFilter在擷取參數時出現編碼問題。