Browse Source

SEC-1717: Document how to perform Single Logout with CAS and added integration test for sample application to test Single Logout

Rob Winch 14 years ago
parent
commit
11331d34d9

+ 77 - 0
docs/manual/src/docbook/cas-auth-provider.xml

@@ -307,6 +307,83 @@
             need to be concerned about the fact CAS handled authentication. In the following sections
             we will discuss some (optional) more advanced configurations.</para>
         </section>
+        <section xml:id="cas-singlelogout">
+            <info>
+                <title>Single Logout</title>
+            </info>
+            <para>The CAS protocol supports Single Logout and can be easily added to your Spring
+                Security configuration. Below are updates to the Spring Security configuration
+                that handle Single Logout <programlisting language="xml"><![CDATA[
+  <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="/j_spring_cas_security_logout"/>
+  </bean>
+            ]]></programlisting> The <literal>logout</literal> element logs the user out of the local application, but
+                does not terminate the session with the CAS server or any other applications that have been logged
+                into. The <literal>requestSingleLogoutFilter</literal> filter will allow the url of
+                <literal>/spring_security_cas_logout</literal> to be requested to redirect the application to the
+                configured CAS Server logout url. Then the CAS Server will send a Single Logout request to all the
+                services that were signed into. The <literal>singleLogoutFilter</literal> handles the Single Logout
+                request by looking up the <literal>HttpSession</literal> in a static <interfacename>Map</interfacename>
+                and then invalidating it.</para>
+            <para>It might be confusing why both the <literal>logout</literal> element and the
+                <literal>singleLogoutFilter</literal> are needed. It is considered best practice to logout locally
+                first since the <literal>SingleSignOutFilter</literal> just stores the
+                <interfacename>HttpSession</interfacename> in a static <interfacename>Map</interfacename> in order to
+                call invalidate on it. With the configuration above, the flow of logout would be:
+                <orderedlist inheritnum="ignore" continuation="restarts">
+                    <listitem>The user requests <literal>/j_spring_security_logout</literal> which would log the user
+                        out of the local application and send the user to the logout success page.</listitem>
+                    <listitem>The logout success page, <literal>/cas-logout.jsp</literal>, should instruct the user
+                        to click a link pointing to <literal>/j_spring_cas_security_logout</literal> in order to logout
+                        out of all applications.</listitem>
+                    <listitem>When the user clicks the link, the user is redirected to the CAS single logout URL
+                        (<literal>https://localhost:9443/cas/logout</literal>).</listitem>
+                    <listitem>On the CAS Server side, the CAS single logout URL then submits single logout requests to
+                        all the CAS Services. On the CAS Service side, JASIG's
+                        <classname>SingleSignOutFilter</classname> processes the logout request by invaliditing the
+                        original session.</listitem>
+                </orderedlist>
+            </para>
+            <para>The next step is to add the following to your web.xml
+                <programlisting language="xml"><![CDATA[
+  <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>]]></programlisting></para>
+            <para>When using the SingleSignOutFilter you might encounter some encoding issues. Therefore it is
+                recommended to add the <classname>CharacterEncodingFilter</classname> to ensure that the character
+                encoding is correct when using the <classname>SingleSignOutFilter</classname>. Again, refer to JASIG's
+                documentation for details. The <classname>SingleSignOutHttpSessionListener</classname> ensures that
+                when an <interfacename>HttpSession</interfacename> expires, the mapping used for single logout is
+                removed.</para>
+        </section>
         <section xml:id="cas-pt">
             <info>
                 <title>Proxy Ticket Authentication</title>

+ 15 - 0
samples/cas/src/integration-test/groovy/org/springframework/security/samples/cas/CasSampleSpec.groovy

@@ -21,6 +21,7 @@ import org.junit.runner.RunWith;
 import org.spockframework.runtime.Sputnik;
 import org.springframework.security.samples.cas.pages.*
 
+import spock.lang.Shared;
 import spock.lang.Stepwise;
 
 /**
@@ -30,6 +31,7 @@ import spock.lang.Stepwise;
  */
 @Stepwise
 class CasSampleSpec extends BaseSpec {
+    @Shared String casServerLogoutUrl = LoginPage.url.replaceFirst('/login','/logout')
 
     def 'access home page with unauthenticated user succeeds'() {
         when: 'Unauthenticated user accesses the Home Page'
@@ -108,4 +110,17 @@ class CasSampleSpec extends BaseSpec {
         then: 'login page is displayed'
         at LoginPage
     }
+
+    def 'loging out of the cas server successfully logs out of the cas servers'() {
+        setup: 'login with ROLE_USER'
+        to SecurePage
+        at LoginPage
+        login 'rod'
+        at SecurePage
+        when: 'logout of the CAS Server'
+        go casServerLogoutUrl
+        to SecurePage
+        then: 'user is logged out of the CAS Service'
+        at LoginPage
+    }
 }