mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-10-30 22:28:46 +00:00 
			
		
		
		
	SEC-965: Updated CAS documentation to describe authenticating proxy tickets
This commit is contained in:
		
							parent
							
								
									761d5af6ec
								
							
						
					
					
						commit
						04f1df2a1b
					
				| @ -18,7 +18,7 @@ | ||||
|         </info> | ||||
|         <para>Whilst the CAS web site contains documents that detail the architecture of CAS, we | ||||
|       present the general overview again here within the context of Spring Security. Spring Security | ||||
|       3.0 supports CAS 3. At the time of writing, the CAS server was at version 3.4.</para> | ||||
|       3.x supports CAS 3. At the time of writing, the CAS server was at version 3.4.</para> | ||||
|         <para>Somewhere in your enterprise you will need to setup a CAS server. The CAS server is | ||||
|             simply a standard WAR file, so there isn't anything difficult about setting up your | ||||
|             server. Inside the WAR file you will customise the login and other single sign on pages | ||||
| @ -35,8 +35,9 @@ | ||||
|       enter a password matching their username, which is useful for testing.</para> | ||||
|         <para>Apart from the CAS server itself, the other key players are of course the secure web | ||||
|       applications deployed throughout your enterprise. These web applications are known as | ||||
|       "services". There are two types of services: standard services and proxy services. A proxy | ||||
|       service is able to request resources from other services on behalf of the user.</para> | ||||
|       "services". There are three types of services. Those that authenticate service tickets, those that | ||||
|       can obtain proxy tickets, and those that authenticate proxy tickets. Authenticating a proxy ticket | ||||
|       differs because the list of proxies must be validated and often times a proxy ticket can be reused.</para> | ||||
| 
 | ||||
|   <section xml:id="cas-sequence"> | ||||
|     <title>Spring Security and CAS Interaction Sequence</title> | ||||
| @ -119,14 +120,14 @@ | ||||
| 
 | ||||
|       <listitem> | ||||
|         <para><classname>CasAuthenticationProvider</classname> will validate the service ticket using a | ||||
|               <interfacename>TicketValidator</interfacename> implementation. This will typically be a | ||||
|               <classname>Cas20ServiceTicketValidator</classname> ticket which is one of the classes | ||||
|             included in the CAS client library. The <classname>Cas20ServiceTicketValidator</classname> | ||||
|             makes an HTTPS request to the CAS server in order to validate the service ticket. <!-- It | ||||
|             may also include a proxy callback URL, which is included in this example: | ||||
|               <literal>https://my.company.com/cas/proxyValidate?service=https%3A%2F%2Fserver3.company.com%2Fwebapp%2Fj_spring_cas_security_check&ticket=ST-0-ER94xMJmn6pha35CQRoZ&pgtUrl=https://server3.company.com/webapp/casProxy/receptor</literal>. | ||||
|             In this case a <classname>Cas20ProxyTicketValidator</classname> should be used as the | ||||
|             validator.--></para> | ||||
|             <interfacename>TicketValidator</interfacename> implementation. This will typically be a | ||||
|             <classname>Cas20ServiceTicketValidator</classname> which is one of the classes | ||||
|             included in the CAS client library. In the event the application needs to validate proxy tickets, the | ||||
|             <classname>Cas20ProxyTicketValidator</classname> is used. The | ||||
|             <interfacename>TicketValidator</interfacename> makes an HTTPS request to the CAS server in order to | ||||
|             validate the service ticket. <!--  It may also include a proxy callback URL, which is included in this example: | ||||
|             <literal>https://my.company.com/cas/proxyValidate?service=https%3A%2F%2Fserver3.company.com%2Fwebapp%2Fj_spring_cas_security_check&ticket=ST-0-ER94xMJmn6pha35CQRoZ&pgtUrl=https://server3.company.com/webapp/casProxy/receptor</literal>.--> | ||||
|             </para> | ||||
|       </listitem> | ||||
| 
 | ||||
|       <listitem> | ||||
| @ -211,10 +212,17 @@ | ||||
|         <para>The web application side of CAS is made easy due to Spring Security. It is assumed you | ||||
|             already know the basics of using Spring Security, so these are not covered again below. | ||||
|             We'll assume a namespace based configuration is being used and add in the CAS beans as | ||||
|             required. </para> | ||||
|         <para>You will need to add a <classname>ServiceProperties</classname> bean to your | ||||
|             application context. This represents your CAS service:</para> | ||||
|         <para> <programlisting language="xml"><![CDATA[ | ||||
|             required. Each section builds upon the previous section. A full | ||||
|             <link xlink:href="#cas-sample">CAS sample application</link> can be found in the Spring | ||||
|             Security Samples.</para> | ||||
|         <section xml:id="cas-st"> | ||||
|             <info> | ||||
|                 <title>Service Ticket Authentication</title> | ||||
|             </info> | ||||
|             <para>This section describes how to setup Spring Security to authenticate Service Tickets. You will need | ||||
|                 to add a <classname>ServiceProperties</classname> bean to your application context. This represents | ||||
|                 your CAS service:</para> | ||||
|             <para> <programlisting language="xml"><![CDATA[ | ||||
|   <bean id="serviceProperties" | ||||
|         class="org.springframework.security.cas.ServiceProperties"> | ||||
|     <property name="service" | ||||
| @ -222,46 +230,45 @@ | ||||
|     <property name="sendRenew" value="false"/> | ||||
|   </bean>]]> | ||||
|     </programlisting> </para> | ||||
|         <para>The <literal>service</literal> must equal a URL that will be monitored by the | ||||
|             <literal>CasAuthenticationFilter</literal>. The <literal>sendRenew</literal> defaults to | ||||
|             false, but should be set to true if your application is particularly sensitive. What | ||||
|             this parameter does is tell the CAS login service that a single sign on login is | ||||
|             unacceptable. Instead, the user will need to re-enter their username and password in | ||||
|             order to gain access to the service.</para> | ||||
|         <para>The following beans should be configured to commence the CAS authentication process | ||||
|             (assuming you're using a namespace configuration):</para> | ||||
|         <para> <programlisting language="xml"><![CDATA[ | ||||
| <security:http entry-point-ref="casEntryPoint"> | ||||
|             <para>The <literal>service</literal> must equal a URL that will be monitored by the | ||||
|                 <literal>CasAuthenticationFilter</literal>. The <literal>sendRenew</literal> defaults to | ||||
|                 false, but should be set to true if your application is particularly sensitive. What | ||||
|                 this parameter does is tell the CAS login service that a single sign on login is | ||||
|                 unacceptable. Instead, the user will need to re-enter their username and password in | ||||
|                 order to gain access to the service.</para> | ||||
|             <para>The following beans should be configured to commence the CAS authentication process | ||||
|                 (assuming you're using a namespace configuration):</para> | ||||
|             <para> <programlisting language="xml"><![CDATA[ | ||||
|   <security:http entry-point-ref="casEntryPoint"> | ||||
|    ... | ||||
|    <security:custom-filter position="CAS_FILTER" ref="casFilter" /> | ||||
| </security:http> | ||||
|      <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="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> | ||||
|   <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> | ||||
| ]]> | ||||
|     </programlisting> </para> | ||||
|         <para> The <classname>CasAuthenticationEntryPoint</classname> should be selected to drive | ||||
|             authentication using <link xlink:href="#ns-entry-point-ref" | ||||
|             ><literal>entry-point-ref</literal></link>. </para> | ||||
|         <para>The <classname>CasAuthenticationFilter</classname> has very similar properties to the | ||||
|             <classname>UsernamePasswordAuthenticationFilter</classname> (used for form-based | ||||
|             logins). </para> | ||||
|         <para>For CAS to operate, the <classname>ExceptionTranslationFilter</classname> must have | ||||
|             its <literal>authenticationEntryPoint</literal> property set to the | ||||
|             <classname>CasAuthenticationEntryPoint</classname> bean.</para> | ||||
|         <para>The <classname>CasAuthenticationEntryPoint</classname> must refer to the | ||||
|             <classname>CasAuthenticationEntryPoint</classname> bean. This can easily be done using | ||||
|             <link xlink:href="#ns-entry-point-ref"><literal>entry-point-ref</literal></link> as is | ||||
|             done in the example above. The <classname>CasAuthenticationEntryPoint</classname> must refer to the | ||||
|             <classname>ServiceProperties</classname> bean (discussed above), which provides the URL | ||||
|             to the enterprise's CAS login server. This is where the user's browser will be | ||||
|             redirected.</para> | ||||
|         <para>Next you need to add a <literal>CasAuthenticationProvider</literal> and its | ||||
|         <para>The <classname>CasAuthenticationFilter</classname> has very similar properties to the | ||||
|             <classname>UsernamePasswordAuthenticationFilter</classname> (used for form-based | ||||
|             logins). You can use these properties to customize things like behavior for authentication | ||||
|             success and failure.</para> | ||||
|         <para>Next you need to add a <classname>CasAuthenticationProvider</classname> and its | ||||
|             collaborators: <programlisting language="xml"><![CDATA[ | ||||
|   <security:authentication-manager alias="authenticationManager"> | ||||
|     <security:authentication-provider ref="casAuthenticationProvider" /> | ||||
| @ -269,7 +276,11 @@ | ||||
| 
 | ||||
|   <bean id="casAuthenticationProvider" | ||||
|       class="org.springframework.security.cas.authentication.CasAuthenticationProvider"> | ||||
|     <property name="userDetailsService" ref="userService"/> | ||||
|     <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"> | ||||
| @ -286,104 +297,110 @@ | ||||
|       </programlisting> The <classname>CasAuthenticationProvider</classname> uses a | ||||
|             <interfacename>UserDetailsService</interfacename> instance to load the authorities for a | ||||
|             user, once they have been authenticated by CAS. We've shown a simple in-memory setup | ||||
|             here. </para> | ||||
|         <para>The beans are all reasonably self-explanatory if you refer back to the | ||||
|           <quote>How CAS Works</quote> section.</para> | ||||
|         <para>This completes the configuration of CAS. If you haven't made any | ||||
|         mistakes, your web application should happily work within the | ||||
|         framework of CAS single sign on. No other parts of Spring Security | ||||
|         need to be concerned about the fact CAS handled authentication.</para> | ||||
|             here. Note that the <classname>CasAuthenticationProvider</classname> does not actually use | ||||
|             the password for authentication.</para> | ||||
|          <para>The beans are all reasonably self-explanatory if you refer back to the | ||||
|               <link xlink:href="#cas-how-it-works">How CAS Works</link> section.</para> | ||||
|          <para>This completes the most basic configuration for CAS. If you haven't made any | ||||
|             mistakes, your web application should happily work within the | ||||
|             framework of CAS single sign on. No other parts of Spring Security | ||||
|             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-pt"> | ||||
|             <info> | ||||
|                 <title>Proxy Ticket Authentication</title> | ||||
|             </info> | ||||
|                 <para>The <classname>CasAuthenticationProvider</classname> distinguishes | ||||
|                     between stateful and stateless clients. A stateful client is | ||||
|                     considered any that submits to the <literal>filterProcessUrl</literal> of the | ||||
|                     <classname>CasAuthenticationFilter</classname>. A stateless client is any that | ||||
|                     presents an authentication request to <classname>CasAuthenticationFilter</classname> | ||||
|                     on a URL other than the <literal>filterProcessUrl</literal>.</para> | ||||
|                 <para>Because remoting protocols have no way of presenting themselves | ||||
|                     within the context of an <classname>HttpSession</classname>, it isn't | ||||
|                     possible to rely on the default practice of storing the security context in the | ||||
|                     session between requests. Furthermore, because the CAS server invalidates a | ||||
|                     ticket after it has been validated by the <literal>TicketValidator</literal>, | ||||
|                     presenting the same proxy ticket on subsequent requests will not | ||||
|                     work.</para> | ||||
|                 <para>One obvious option is to not use CAS at all for remoting | ||||
|                     protocol clients. However, this would eliminate many of the desirable | ||||
|                     features of CAS. As a middle-ground, the | ||||
|                     <literal>CasAuthenticationProvider</literal> uses a | ||||
|                     <literal>StatelessTicketCache</literal>. This is used solely for stateless clients | ||||
|                     which use a principal equal to | ||||
|                     <literal>CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER</literal>. What | ||||
|                     happens is the <literal>CasAuthenticationProvider</literal> will store | ||||
|                     the resulting <literal>CasAuthenticationToken</literal> in the | ||||
|                     <literal>StatelessTicketCache</literal>, keyed on the proxy ticket. | ||||
|                     Accordingly, remoting protocol clients can present the same proxy | ||||
|                     ticket and the <literal>CasAuthenticationProvider</literal> will not | ||||
|                     need to contact the CAS server for validation (aside from the first | ||||
|                     request). Once authenticated, the proxy ticket could be used for URLs other than the | ||||
|                     original target service.</para> | ||||
|                 <para>This section builds upon the previous sections to accomodate proxy ticket authentication. | ||||
|                     The first step is to specify to authenticate all artifacts as shown below. | ||||
| <programlisting language="xml"><![CDATA[ | ||||
|   <bean id="serviceProperties" | ||||
|         class="org.springframework.security.cas.ServiceProperties"> | ||||
|     ... | ||||
|     <property name="authenticateAllArtifacts" value="true"/> | ||||
|   </bean> | ||||
| ]]></programlisting></para> | ||||
|                 <para>The next step is to specify <literal>serviceProperties</literal> and the | ||||
|                     <literal>authenticationDetailsSource</literal> for the <classname>CasAuthenticationFilter</classname>. | ||||
|                     The <literal>serviceProperties</literal> property instructs the | ||||
|                     <classname>CasAuthenticationFilter</classname> to attempt to authenticate all artifacts instead of only | ||||
|                     ones present on the <literal>filterProcessUrl</literal>. The | ||||
|                     <classname>ServiceAuthenticationDetailsSource</classname> creates a | ||||
|                     <interfacename>ServiceAuthenticationDetails</interfacename> that ensures the current URL, based | ||||
|                     upon the <literal>HttpServletRequest</literal>, is used as the service URL when validating the ticket. | ||||
|                     The method for generating the service URL can be customized by injecting a custom | ||||
|                     <literal>AuthenticationDetailsSource</literal> that returns a custom | ||||
|                     <interfacename>ServiceAuthenticationDetails</interfacename>.<programlisting language="xml"><![CDATA[ | ||||
|   <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"/> | ||||
|     </property> | ||||
|   </bean> | ||||
| ]]></programlisting></para> | ||||
|                 <para>You will also need to update the <classname>CasAuthenticationProvider</classname> to handle proxy tickets. | ||||
|                     To do this replace the <classname>Cas20ServiceTicketValidator</classname> with a | ||||
|                     <classname>Cas20ProxyTicketValidator</classname>. You will need to configure the | ||||
|                     <literal>statelessTicketCache</literal> and which proxies you want to accept. You can find an example of the updates | ||||
|                     required to accept all proxies below. | ||||
| <programlisting language="xml"><![CDATA[ | ||||
|   <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> | ||||
| ]]></programlisting></para> | ||||
|         </section> | ||||
|     </section> | ||||
|     <!-- | ||||
|     <para>Note the <literal>CasProxyTicketValidator</literal> has a | ||||
|     remarked out <literal>trustStore</literal> property. This property | ||||
|     might be helpful if you experience HTTPS certificate issues. Also note | ||||
|     the <literal>proxyCallbackUrl</literal> is set so the service can | ||||
|     receive a proxy-granting ticket. As mentioned above, this is optional | ||||
|     and unnecessary if you do not require proxy-granting tickets. If you | ||||
|     do use this feature, you will need to configure a suitable servlet to | ||||
|     receive the proxy-granting tickets. We suggest you use CAS' | ||||
|     <literal>ProxyTicketReceptor</literal> by adding the following to your | ||||
|     web application's <literal>web.xml</literal>:</para> | ||||
| 
 | ||||
|     <para><programlisting language="xml"> | ||||
| <servlet> | ||||
| <servlet-name>casproxy</servlet-name> | ||||
| <servlet-class>edu.yale.its.tp.cas.proxy.ProxyTicketReceptor</servlet-class> | ||||
| </servlet> | ||||
| 
 | ||||
| <servlet-mapping> | ||||
| <servlet-name>casproxy</servlet-name> | ||||
| <url-pattern>/casProxy/*</url-pattern> | ||||
| </servlet-mapping> | ||||
| 
 | ||||
|     </programlisting></para> | ||||
| 
 | ||||
|   </section> | ||||
|     --> | ||||
| 
 | ||||
|  <!-- | ||||
|   <section xml:id="cas-advanced"> | ||||
|     <info><title>Advanced Issues</title></info> | ||||
| 
 | ||||
|     <para>The <classname>CasAuthenticationProvider</classname> distinguishes | ||||
|     between stateful and stateless clients. A stateful client is | ||||
|     considered any that originates via the | ||||
|     <classname>CasAuthenticationFilter</classname>. A stateless client is any that | ||||
|     presents an authentication request via the | ||||
|     <literal>UsernamePasswordAuthenticationToken</literal> with a | ||||
|     principal equal to | ||||
|     <literal>CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER</literal>.</para> | ||||
| 
 | ||||
|     <para>Stateless clients are likely to be via remoting protocols such | ||||
|     as Hessian and Burlap. The <classname>BasicAuthenticationFilter</classname> is | ||||
|     still used in this case, but the remoting protocol client is expected | ||||
|     to present a username equal to the static string above, and a password | ||||
|     equal to a CAS service ticket. Clients should acquire a CAS service | ||||
|     ticket directly from the CAS server.</para> | ||||
| 
 | ||||
|     <para>Because remoting protocols have no way of presenting themselves | ||||
|     within the context of an <classname>HttpSession</classname>, it isn't | ||||
|     possible to rely on the default practice of storing the security context in the | ||||
|     session between requests. | ||||
|     attribute to locate the <literal>CasAuthenticationToken</literal>. | ||||
|     Furthermore, because the CAS server invalidates a service ticket after | ||||
|     it has been validated by the <literal>TicketValidator</literal>, | ||||
|     presenting the same service ticket on subsequent requests will not | ||||
|     work. It is similarly very difficult to obtain a proxy-granting ticket | ||||
|     for a remoting protocol client, as they are often deployed on client | ||||
|     machines which rarely have HTTPS URLs that would be accessible to the | ||||
|     CAS server.</para> | ||||
| 
 | ||||
|     <para>One obvious option is to not use CAS at all for remoting | ||||
|     protocol clients. However, this would eliminate many of the desirable | ||||
|     features of CAS.</para> | ||||
| 
 | ||||
|     <para>As a middle-ground, the | ||||
|     <literal>CasAuthenticationProvider</literal> uses a | ||||
|     <literal>StatelessTicketCache</literal>. This is used solely for | ||||
|     requests with a principal equal to | ||||
|     <literal>CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER</literal>. What | ||||
|     happens is the <literal>CasAuthenticationProvider</literal> will store | ||||
|     the resulting <literal>CasAuthenticationToken</literal> in the | ||||
|     <literal>StatelessTicketCache</literal>, keyed on the service ticket. | ||||
|     Accordingly, remoting protocol clients can present the same service | ||||
|     ticket and the <literal>CasAuthenticationProvider</literal> will not | ||||
|     need to contact the CAS server for validation (aside from the first | ||||
|     request).</para> | ||||
| 
 | ||||
|     <para>The other aspect of advanced CAS usage involves creating proxy | ||||
|     tickets from the proxy-granting ticket. As indicated above, we | ||||
|     recommend you use CAS' <literal>ProxyTicketReceptor</literal> to | ||||
|     receive these tickets. The <literal>ProxyTicketReceptor</literal> | ||||
|     provides a static method that enables you to obtain a proxy ticket by | ||||
|     presenting the proxy-granting IOU ticket. You can obtain the | ||||
|     proxy-granting IOU ticket by calling | ||||
|     <literal>CasAuthenticationToken.getProxyGrantingTicketIou()</literal>.</para> | ||||
| 
 | ||||
|     <para>It is hoped you find CAS integration easy and useful with Spring | ||||
|     Security classes. Welcome to enterprise-wide single sign on!</para> | ||||
| 
 | ||||
|   </section> | ||||
| --> | ||||
| </chapter> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user