SEC-965: Updated CAS documentation to describe authenticating proxy tickets

This commit is contained in:
Rob Winch 2011-04-16 19:14:36 -05:00
parent 761d5af6ec
commit 04f1df2a1b
1 changed files with 162 additions and 145 deletions

View File

@ -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&amp;ticket=ST-0-ER94xMJmn6pha35CQRoZ&amp;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&amp;ticket=ST-0-ER94xMJmn6pha35CQRoZ&amp;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">
&lt;servlet&gt;
&lt;servlet-name&gt;casproxy&lt;/servlet-name&gt;
&lt;servlet-class&gt;edu.yale.its.tp.cas.proxy.ProxyTicketReceptor&lt;/servlet-class&gt;
&lt;/servlet&gt;
&lt;servlet-mapping&gt;
&lt;servlet-name&gt;casproxy&lt;/servlet-name&gt;
&lt;url-pattern&gt;/casProxy/*&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;
</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>