465 lines
23 KiB
XML
465 lines
23 KiB
XML
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="cas"
|
|
xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
|
|
<title>CAS Authentication</title>
|
|
|
|
<section xml:id="cas-overview">
|
|
<title>Overview</title>
|
|
|
|
<para>JA-SIG produces an enterprise-wide single sign on system known
|
|
as CAS. Unlike other initiatives, JA-SIG's Central Authentication
|
|
Service is open source, widely used, simple to understand, platform
|
|
independent, and supports proxy capabilities. Spring Security fully
|
|
supports CAS, and provides an easy migration path from
|
|
single-application deployments of Spring Security through to
|
|
multiple-application deployments secured by an enterprise-wide CAS
|
|
server.</para>
|
|
|
|
<para>You can learn more about CAS at
|
|
<literal>http://www.ja-sig.org/products/cas/</literal>. You will also need
|
|
to visit this site to download the CAS Server files.</para>
|
|
</section>
|
|
|
|
<section xml:id="cas-how-it-works">
|
|
<info><title>How CAS Works</title></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 2.0 supports
|
|
CAS 3. At the time of writing, the CAS server was at version 3.2.</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 displayed
|
|
to users.</para>
|
|
|
|
<para>When deploying a CAS 3.2 server, you will also need to specify an
|
|
<literal>AuthenticationHandler</literal> in the
|
|
<filename>deployerConfigContext.xml</filename> included with CAS. The
|
|
<literal>AuthenticationHandler</literal> has a simple method that
|
|
returns a boolean as to whether a given set of Credentials is valid.
|
|
Your <literal>AuthenticationHandler</literal> implementation will need
|
|
to link into some type of backend authentication repository, such as
|
|
an LDAP server or database. CAS itself includes numerous
|
|
<literal>AuthenticationHandler</literal>s out of the box to assist
|
|
with this. When you download and deploy the server war file, it is set up
|
|
to successfully authenticate users who 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. This will be explained more fully later.</para>
|
|
|
|
<!--
|
|
<section xml:id="cas-sequence">
|
|
<title>Spring Security and CAS Interaction Sequence</title>
|
|
|
|
TODO: Needs reviewed
|
|
<para>The basic interaction between a web browser, CAS server and a
|
|
Spring Security-secured service is as follows:</para>
|
|
|
|
<orderedlist inheritnum="ignore" continuation="restarts">
|
|
<listitem>
|
|
<para>The web user is browsing the service's public pages. CAS or
|
|
Spring Security is not involved.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>The user eventually requests a page that is either secure or
|
|
one of the beans it uses is secure. Spring Security's
|
|
<classname>ExceptionTranslationFilter</classname> will detect the
|
|
<literal>AuthenticationException</literal>.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Because the user's <interfacename>Authentication</interfacename> object
|
|
(or lack thereof) caused an
|
|
<literal>AuthenticationException</literal>, the
|
|
<classname>ExceptionTranslationFilter</classname> will call the
|
|
configured <interfacename>AuthenticationEntryPoint</interfacename>. If using
|
|
CAS, this will be the
|
|
<literal>CasProcessingFilterEntryPoint</literal> class.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>The <literal>CasProcessingFilterEntry</literal> point will
|
|
redirect the user's browser to the CAS server. It will also
|
|
indicate a <literal>service</literal> parameter, which is the
|
|
callback URL for Spring Security service. For example, the URL to
|
|
which the browser is redirected might be
|
|
<literal>https://my.company.com/cas/login?service=https%3A%2F%2Fserver3.company.com%2Fwebapp%2Fj_spring_cas_security_check</literal>.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>After the user's browser redirects to CAS, they will be
|
|
prompted for their username and password. If the user presents a
|
|
session cookie which indicates they've previously logged on, they
|
|
will not be prompted to login again (there is an exception to this
|
|
procedure, which we'll cover later). CAS will use the
|
|
<literal>PasswordHandler</literal> (or
|
|
<literal>AuthenticationHandler</literal> if using CAS 3.0)
|
|
discussed above to decide whether the username and password is
|
|
valid.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Upon successful login, CAS will redirect the user's browser
|
|
back to the original service. It will also include a
|
|
<literal>ticket</literal> parameter, which is an opaque string
|
|
representing the "service ticket". Continuing our earlier example,
|
|
the URL the browser is redirected to might be
|
|
<literal>https://server3.company.com/webapp/j_spring_cas_security_check?ticket=ST-0-ER94xMJmn6pha35CQRoZ</literal>.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Back in the service web application, the
|
|
<literal>CasProcessingFilter</literal> is always listening for
|
|
requests to <literal>/j_spring_cas_security_check</literal> (this
|
|
is configurable, but we'll use the defaults in this introduction).
|
|
The processing filter will construct a
|
|
<literal>UsernamePasswordAuthenticationToken</literal>
|
|
representing the service ticket. The principal will be equal to
|
|
<literal>CasProcessingFilter.CAS_STATEFUL_IDENTIFIER</literal>,
|
|
whilst the credentials will be the service ticket opaque value.
|
|
This authentication request will then be handed to the configured
|
|
<interfacename>AuthenticationManager</interfacename>.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>The <interfacename>AuthenticationManager</interfacename> implementation
|
|
will be the <literal>ProviderManager</literal>, which is in turn
|
|
configured with the <literal>CasAuthenticationProvider</literal>.
|
|
The <literal>CasAuthenticationProvider</literal> only responds to
|
|
<literal>UsernamePasswordAuthenticationToken</literal>s containing
|
|
the CAS-specific principal (such as
|
|
<literal>CasProcessingFilter.CAS_STATEFUL_IDENTIFIER</literal>)
|
|
and <literal>CasAuthenticationToken</literal>s (discussed
|
|
later).</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>CasAuthenticationProvider</literal> will validate
|
|
the service ticket using a <literal>TicketValidator</literal>
|
|
implementation. Spring Security includes one implementation, the
|
|
<literal>CasProxyTicketValidator</literal>. This implementation a
|
|
ticket validation class included in the CAS client library. The
|
|
<literal>CasProxyTicketValidator</literal> makes an HTTPS request
|
|
to the CAS server in order to validate the service ticket. The
|
|
<literal>CasProxyTicketValidator</literal> 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>
|
|
<para>Back on the CAS server, the proxy validation request will be
|
|
received. If the presented service ticket matches the service URL
|
|
the ticket was issued to, CAS will provide an affirmative response
|
|
in XML indicating the username. If any proxy was involved in the
|
|
authentication (discussed below), the list of proxies is also
|
|
included in the XML response.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>[OPTIONAL] If the request to the CAS validation service
|
|
included the proxy callback URL (in the <literal>pgtUrl</literal>
|
|
parameter), CAS will include a <literal>pgtIou</literal> string in
|
|
the XML response. This <literal>pgtIou</literal> represents a
|
|
proxy-granting ticket IOU. The CAS server will then create its own
|
|
HTTPS connection back to the <literal>pgtUrl</literal>. This is to
|
|
mutually authenticate the CAS server and the claimed service URL.
|
|
The HTTPS connection will be used to send a proxy granting ticket
|
|
to the original web application. For example,
|
|
<literal>https://server3.company.com/webapp/casProxy/receptor?pgtIou=PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt&pgtId=PGT-1-si9YkkHLrtACBo64rmsi3v2nf7cpCResXg5MpESZFArbaZiOKH</literal>.
|
|
We suggest you use CAS' <literal>ProxyTicketReceptor</literal>
|
|
servlet to receive these proxy-granting tickets, if they are
|
|
required.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>The <literal>CasProxyTicketValidator</literal> will parse
|
|
the XML received from the CAS server. It will return to the
|
|
<literal>CasAuthenticationProvider</literal> a
|
|
<literal>TicketResponse</literal>, which includes the username
|
|
(mandatory), proxy list (if any were involved), and proxy-granting
|
|
ticket IOU (if the proxy callback was requested).</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Next <literal>CasAuthenticationProvider</literal> will call
|
|
a configured <literal>CasProxyDecider</literal>. The
|
|
<literal>CasProxyDecider</literal> indicates whether the proxy
|
|
list in the <literal>TicketResponse</literal> is acceptable to the
|
|
service. Several implementations are provided with Spring
|
|
Security: <literal>RejectProxyTickets</literal>,
|
|
<literal>AcceptAnyCasProxy</literal> and
|
|
<literal>NamedCasProxyDecider</literal>. These names are largely
|
|
self-explanatory, except <literal>NamedCasProxyDecider</literal>
|
|
which allows a <literal>List</literal> of trusted proxies to be
|
|
provided.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>CasAuthenticationProvider</literal> will next
|
|
request a <literal>CasAuthoritiesPopulator</literal> to advise the
|
|
<interfacename>GrantedAuthority</interfacename> objects that apply to the user
|
|
contained in the <literal>TicketResponse</literal>. Spring
|
|
Security includes a <literal>DaoCasAuthoritiesPopulator</literal>
|
|
which simply uses the <interfacename>UserDetailsService</interfacename>
|
|
infrastructure to find the <interfacename>UserDetails</interfacename> and
|
|
their associated <interfacename>GrantedAuthority</interfacename>s. Note that
|
|
the password and enabled/disabled status of
|
|
<interfacename>UserDetails</interfacename> returned by the
|
|
<interfacename>UserDetailsService</interfacename> are ignored, as the CAS
|
|
server is responsible for authentication decisions.
|
|
<literal>DaoCasAuthoritiesPopulator</literal> is only concerned
|
|
with retrieving the <interfacename>GrantedAuthority</interfacename>s.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>If there were no problems,
|
|
<literal>CasAuthenticationProvider</literal> constructs a
|
|
<literal>CasAuthenticationToken</literal> including the details
|
|
contained in the <literal>TicketResponse</literal> and the
|
|
<interfacename>GrantedAuthority</interfacename>s. The
|
|
<literal>CasAuthenticationToken</literal> contains the hash of a
|
|
key, so that the <literal>CasAuthenticationProvider</literal>
|
|
knows it created it.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Control then returns to
|
|
<literal>CasProcessingFilter</literal>, which places the created
|
|
<literal>CasAuthenticationToken</literal> into the
|
|
<literal>HttpSession</literal> attribute named
|
|
<literal>HttpSessionContextIntegrationFilter.SPRING_SECURITY_CONTEXT_KEY</literal>.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>The user's browser is redirected to the original page that
|
|
caused the <literal>AuthenticationException</literal>.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>As the <interfacename>Authentication</interfacename> object is now in
|
|
the well-known location, it is handled like any other
|
|
authentication approach. Usually the
|
|
<classname>HttpSessionContextIntegrationFilter</classname> will be
|
|
used to associate the <interfacename>Authentication</interfacename> object
|
|
with the <classname>SecurityContextHolder</classname> for the duration
|
|
of each request.</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
|
|
<para>It's good that you're still here! It might sound involved, but
|
|
you can relax as Spring Security classes hide much of the complexity.
|
|
Let's now look at how this is configured</para>
|
|
</section>
|
|
-->
|
|
</section>
|
|
|
|
<section xml:id="cas-client">
|
|
<info><title>Configuration of CAS Client</title></info>
|
|
|
|
<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 <literal>ServiceProperties</literal> bean
|
|
to your application context. This represents your service:</para>
|
|
|
|
<para><programlisting><![CDATA[
|
|
<bean id="serviceProperties"
|
|
class="org.springframework.security.cas.ServiceProperties">
|
|
<property name="service"
|
|
value="https://localhost:8443/cas-sample/j_spring_cas_security_check"/>
|
|
<property name="sendRenew" value="false"/>
|
|
</bean>]]>
|
|
</programlisting></para>
|
|
|
|
<para>The <literal>service</literal> must equal a URL that will be
|
|
monitored by the <literal>CasProcessingFilter</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:</para>
|
|
|
|
<para><programlisting><![CDATA[
|
|
<security:authentication-manager alias="authenticationManager"/>
|
|
|
|
<bean id="casProcessingFilter"
|
|
class="org.springframework.security.cas.web.CasProcessingFilter">
|
|
<security:custom-filter after="CAS_PROCESSING_FILTER"/>
|
|
<property name="authenticationManager" ref="authenticationManager"/>
|
|
<property name="authenticationFailureUrl" value="/casfailed.jsp"/>
|
|
<property name="defaultTargetUrl" value="/"/>
|
|
</bean>
|
|
|
|
<bean id="casProcessingFilterEntryPoint"
|
|
class="org.springframework.security.cas.web.CasProcessingFilterEntryPoint">
|
|
<property name="loginUrl" value="https://localhost:9443/cas/login"/>
|
|
<property name="serviceProperties" ref="serviceProperties"/>
|
|
</bean>
|
|
]]>
|
|
|
|
</programlisting></para>
|
|
|
|
<para>
|
|
The <classname>CasProcessingFilterEntryPoint</classname> should be selected to
|
|
drive authentication using <link xlink:href="ns-entry-point-ref"><literal>entry-point-ref</literal></link>.
|
|
</para>
|
|
|
|
<para>The <literal>CasProcessingFilter</literal> has very similar
|
|
properties to the <literal>UsernamePasswordAuthenticationProcessingFilter</literal>
|
|
(used for form-based logins). Each property is
|
|
self-explanatory. Note that we've also used the namespace syntax
|
|
for setting up an alias to the authentication mnager, since the
|
|
<literal>CasProcessingFilter</literal> needs a reference to it.</para>
|
|
|
|
<para>For CAS to operate, the
|
|
<classname>ExceptionTranslationFilter</classname> must have its
|
|
<literal>authenticationEntryPoint</literal> property set to the
|
|
<literal>CasProcessingFilterEntryPoint</literal> bean.</para>
|
|
|
|
<para>The <literal>CasProcessingFilterEntryPoint</literal> must refer
|
|
to the <literal>ServiceProperties</literal> 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
|
|
collaborators:
|
|
<programlisting><![CDATA[
|
|
<bean id="casAuthenticationProvider"
|
|
class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
|
|
<security:custom-authentication-provider />
|
|
<property name="userDetailsService" ref="userService"/>
|
|
<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">
|
|
<security:user name="joe" password="joe" authorities="ROLE_USER" />
|
|
...
|
|
</security:user-service>]]>
|
|
</programlisting>
|
|
The <classname>CasAuthenticationProvider</classname> uses a <interfacename>UserDetailsService</interfacename>
|
|
instance to load the authorities for a user, once they have been authentiated by CAS. We've shown a simple
|
|
in-memory setup here.
|
|
</para>
|
|
|
|
<para>The beans are all reasonable self-explanatory if you refer back
|
|
to the "How CAS Works" section.</para>
|
|
</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>
|
|
<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>
|
|
|
|
<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>
|
|
|
|
<para>There is also a <literal>contacts-cas.war</literal> file in the
|
|
sample applications directory. This sample application uses the above
|
|
settings and can be deployed to see CAS in operation</para>
|
|
</section>
|
|
|
|
<section xml:id="cas-advanced">
|
|
<info><title>Advanced Issues</title></info>
|
|
|
|
<para>The <literal>CasAuthenticationProvider</literal> distinguishes
|
|
between stateful and stateless clients. A stateful client is
|
|
considered any that originates via the
|
|
<literal>CasProcessingFilter</literal>. A stateless client is any that
|
|
presents an authentication request via the
|
|
<literal>UsernamePasswordAuthenticationToken</literal> with a
|
|
principal equal to
|
|
<literal>CasProcessingFilter.CAS_STATELESS_IDENTIFIER</literal>.</para>
|
|
|
|
<para>Stateless clients are likely to be via remoting protocols such
|
|
as Hessian and Burlap. The <literal>BasicProcessingFilter</literal> 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 <literal>HttpSession</literal>, it isn't
|
|
possible to rely on the <literal>HttpSession</literal>'s
|
|
<literal>HttpSessionContextIntegrationFilter.SPRING_SECURITY_CONTEXT_KEY</literal>
|
|
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>CasProcessingFilter.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> |