spring-security/src/docbkx/technical-overview.xml

580 lines
30 KiB
XML

<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="technical-overview">
<info>
<title>Technical Overview</title>
</info>
<section xml:id="runtime-environment">
<info>
<title>Runtime Environment</title>
</info>
<para>Spring Security is written to execute within a standard Java 1.4
Runtime Environment. It also supports Java 5.0, although the Java
types which are specific to this release are packaged in a separate
package with the suffix "tiger" in their JAR filename. As Spring
Security aims to operate in a self-contained manner, there is no need
to place any special configuration files into your Java Runtime
Environment. In particular, there is no need to configure a special
Java Authentication and Authorization Service (JAAS) policy file or
place Spring Security into common classpath locations.</para>
<para>Similarly, if you are using an EJB Container or Servlet
Container there is no need to put any special configuration files
anywhere, nor include Spring Security in a server classloader.</para>
<para>This design offers maximum deployment time flexibility, as
you can simply copy your target artifact (be it a JAR, WAR or EAR)
from one system to another and it will immediately work.</para>
</section>
<section xml:id="shared-components">
<info>
<title>Shared Components</title>
</info>
<para>Let's explore some of the most important shared components in
Spring Security. Components are considered "shared" if they are
central to the framework and the framework cannot operate without
them. These Java types represent the building blocks of the remaining
system, so it's important to understand that they're there, even if
you don't need to directly interact with them.</para>
<section>
<title>
SecurityContextHolder, SecurityContext and Authentication Objects
</title>
<para>The most fundamental object is
<classname>SecurityContextHolder</classname>. This is where we store
details of the present security context of the application, which
includes details of the principal currently using the application. By
default the <classname>SecurityContextHolder</classname> uses a
<literal>ThreadLocal</literal> to store these details, which means
that the security context is always available to methods in the same
thread of execution, even if the security context is not explicitly
passed around as an argument to those methods. Using a
<literal>ThreadLocal</literal> in this way is quite safe if care is
taken to clear the thread after the present principal's request is
processed. Of course, Spring Security takes care of this for you
automatically so there is no need to worry about it.</para>
<para>Some applications aren't entirely suitable for using a
<literal>ThreadLocal</literal>, because of the specific way they work
with threads. For example, a Swing client might want all threads in a
Java Virtual Machine to use the same security context. For this
situation you would use the
<literal>SecurityContextHolder.MODE_GLOBAL</literal>. Other
applications might want to have threads spawned by the secure thread
also assume the same security identity. This is achieved by using
<literal>SecurityContextHolder.MODE_INHERITABLETHREADLOCAL</literal>.
You can change the mode from the default
<literal>SecurityContextHolder.MODE_THREADLOCAL</literal> in two ways.
The first is to set a system property. Alternatively, call a static
method on <classname>SecurityContextHolder</classname>. Most applications
won't need to change from the default, but if you do, take a look at
the JavaDocs for <classname>SecurityContextHolder</classname> to learn
more.</para>
<para>Inside the <classname>SecurityContextHolder</classname> we store
details of the principal currently interacting with the application.
Spring Security uses an <interfacename>Authentication</interfacename> object to
represent this information. Whilst you won't normally need to create
an <interfacename>Authentication</interfacename> object yourself, it is fairly
common for users to query the <interfacename>Authentication</interfacename>
object. You can use the following code block - from anywhere in your
application - to obtain the name of the authenticated user, for example:</para>
<programlisting>
Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (obj instanceof UserDetails) {
String username = ((UserDetails)obj).getUsername();
} else {
String username = obj.toString();
}</programlisting>
<para>The above code introduces a number of interesting relationships
and key objects. First, you will notice that there is an intermediate
object between <classname>SecurityContextHolder</classname> and
<interfacename>Authentication</interfacename>. The
<literal>SecurityContextHolder.getContext()</literal> method is
actually returning a <interfacename>SecurityContext</interfacename>.
<!-- Commented out as Captcha sandboxed
Spring
Security uses a few different <interfacename>SecurityContext</interfacename>
implementations, such as if we need to store special information
related to a request that is not principal-specific.
A good example of
this is our JCaptcha integration, which needs to know whether the
current request came from a human user or not. Because such a decision
has nothing at all to do with the principal the request may or may not
be authenticated as, we store it in the
<interfacename>SecurityContext</interfacename>. -->
</para>
</section>
<section>
<title>The UserDetailsService</title>
<para>Another item to note from the above code fragment is that you
can obtain a principal from the <interfacename>Authentication</interfacename>
object. The principal is just an <literal>Object</literal>. Most of
the time this can be cast into a <interfacename>UserDetails</interfacename>
object. <interfacename>UserDetails</interfacename> is a central interface in
Spring Security. It represents a principal, but in an extensible and
application-specific way. Think of <interfacename>UserDetails</interfacename> as
the adapter between your own user database and what Spring Security
needs inside the <classname>SecurityContextHolder</classname>. Being a
representation of something from your own user database, quite often
you will cast the <interfacename>UserDetails</interfacename> to the original
object that your application provided, so you can call
business-specific methods (like <literal>getEmail()</literal>,
<literal>getEmployeeNumber()</literal> and so on).</para>
<para>By now you're probably wondering, so when do I provide a
<interfacename>UserDetails</interfacename> object? How do I do that? I thought you
said this thing was declarative and I didn't need to write any Java
code - what gives? The short answer is that there is a special
interface called <interfacename>UserDetailsService</interfacename>. The only
method on this interface accepts a <literal>String</literal>-based
username argument and returns a <interfacename>UserDetails</interfacename>. Most
authentication providers that ship with Spring Security delegate to a
<interfacename>UserDetailsService</interfacename> as part of the authentication
process. The <interfacename>UserDetailsService</interfacename> is used to build
the <interfacename>Authentication</interfacename> object that is stored in the
<classname>SecurityContextHolder</classname>. The good news is that we
provide a number of <interfacename>UserDetailsService</interfacename>
implementations, including one that uses an in-memory map and another
that uses JDBC. Most users tend to write their own, though, with such
implementations often simply sitting on top of an existing Data Access
Object (DAO) that represents their employees, customers, or other
users of the enterprise application. Remember the advantage that
whatever your UserDetailsService returns can always be obtained from
the <classname>SecurityContextHolder</classname>, as per the above code
fragment.</para>
</section>
<section xml:id="tech-granted-authority">
<title>GrantedAuthority</title>
<para>Besides the principal, another important method provided by
<interfacename>Authentication</interfacename> is
<literal>getAuthorities(</literal>). This method provides an array of
<interfacename>GrantedAuthority</interfacename> objects. A
<interfacename>GrantedAuthority</interfacename> is, not surprisingly, an authority
that is granted to the principal. Such authorities are usually
"roles", such as <literal>ROLE_ADMINISTRATOR</literal> or
<literal>ROLE_HR_SUPERVISOR</literal>. These roles are later on
configured for web authorization, method authorization and domain
object authorization. Other parts of Spring Security are capable of
interpreting these authorities, and expect them to be present.
<interfacename>GrantedAuthority</interfacename> objects are usually loaded by the
<interfacename>UserDetailsService</interfacename>.</para>
<para>Usually the <interfacename>GrantedAuthority</interfacename> objects are
application-wide permissions. They are not specific to a given domain
object. Thus, you wouldn't likely have a
<interfacename>GrantedAuthority</interfacename> to represent a permission to
<literal>Employee</literal> object number 54, because if there are
thousands of such authorities you would quickly run out of memory (or,
at the very least, cause the application to take a long time to
authenticate a user). Of course, Spring Security is expressly designed
to handle this common requirement, but you'd instead use the project's
domain object security capabilities for this purpose.</para>
<para>Last but not least, sometimes you will need to store the
<interfacename>SecurityContext</interfacename> between HTTP requests. Other times
the principal will re-authenticate on every request, although most of
the time it will be stored. The
<classname>HttpSessionContextIntegrationFilter</classname> is responsible
for storing a <interfacename>SecurityContext</interfacename> between HTTP
requests. As suggested by the name of the class, the
<literal>HttpSession</literal> is used to store this information. You
should never interact directly with the <literal>HttpSession</literal>
for security purposes. There is simply no justification for doing so -
always use the <classname>SecurityContextHolder</classname>
instead.</para>
</section>
<section>
<title>Summary</title>
<para>Just to recap, the major building blocks of Spring Security
are:</para>
<itemizedlist spacing="compact">
<listitem>
<para><classname>SecurityContextHolder</classname>, to provide any
type access to the <interfacename>SecurityContext</interfacename>.</para>
</listitem>
<listitem>
<para><interfacename>SecurityContext</interfacename>, to hold the
<interfacename>Authentication</interfacename> and possibly request-specific
security information.</para>
</listitem>
<listitem>
<para><classname>HttpSessionContextIntegrationFilter</classname>, to
store the <interfacename>SecurityContext</interfacename> in the
<literal>HttpSession</literal> between web requests.</para>
</listitem>
<listitem>
<para><interfacename>Authentication</interfacename>, to represent the
principal in a Spring Security-specific manner.</para>
</listitem>
<listitem>
<para><interfacename>GrantedAuthority</interfacename>, to reflect the
application-wide permissions granted to a principal.</para>
</listitem>
<listitem>
<para><interfacename>UserDetails</interfacename>, to provide the necessary
information to build an Authentication object from your
application's DAOs.</para>
</listitem>
<listitem>
<para><interfacename>UserDetailsService</interfacename>, to create a
<interfacename>UserDetails</interfacename> when passed in a
<literal>String</literal>-based username (or certificate ID or
alike).</para>
</listitem>
</itemizedlist>
<para>Now that you've gained an understanding of these repeatedly-used
components, let's take a closer look at the process of
authentication.</para>
</section>
</section>
<section xml:id="common-authentication">
<info><title>Authentication</title></info>
<para>As mentioned in the beginning of this reference guide, Spring
Security can participate in many different authentication
environments. Whilst we recommend people use Spring Security for
authentication and not integrate with existing Container Managed
Authentication, it is nevertheless supported - as is integrating with
your own proprietary authentication system. Let's first explore
authentication from the perspective of Spring Security managing web
security entirely on its own, which is illustrative of the most
complex and most common situation.</para>
<para>Consider a typical web application's authentication
process:</para>
<orderedlist inheritnum="ignore" continuation="restarts">
<listitem>
<para>You visit the home page, and click on a link.</para>
</listitem>
<listitem>
<para>A request goes to the server, and the server decides that
you've asked for a protected resource.</para>
</listitem>
<listitem>
<para>As you're not presently authenticated, the server sends back
a response indicating that you must authenticate. The response
will either be an HTTP response code, or a redirect to a
particular web page.</para>
</listitem>
<listitem>
<para>Depending on the authentication mechanism, your browser will
either redirect to the specific web page so that you can fill out
the form, or the browser will somehow retrieve your identity (eg a
BASIC authentication dialogue box, a cookie, a X509 certificate
etc).</para>
</listitem>
<listitem>
<para>The browser will send back a response to the server. This
will either be an HTTP POST containing the contents of the form
that you filled out, or an HTTP header containing your
authentication details.</para>
</listitem>
<listitem>
<para>Next the server will decide whether or not the presented
credentials are valid. If they're valid, the next step will
happen. If they're invalid, usually your browser will be asked to
try again (so you return to step two above).</para>
</listitem>
<listitem>
<para>The original request that you made to cause the
authentication process will be retried. Hopefully you've
authenticated with sufficient granted authorities to access the
protected resource. If you have sufficient access, the request
will be successful. Otherwise, you'll receive back an HTTP error
code 403, which means "forbidden".</para>
</listitem>
</orderedlist>
<para>Spring Security has distinct classes responsible for most of the
steps described above. The main participants (in the order that they
are used) are the <classname>ExceptionTranslationFilter</classname>, an
<interfacename>AuthenticationEntryPoint</interfacename>, an authentication
mechanism, and an <classname>AuthenticationProvider</classname>.</para>
<section>
<title>ExceptionTranslationFilter</title>
<para><classname>ExceptionTranslationFilter</classname> is a Spring
Security filter that has responsibility for detecting any Spring
Security exceptions that are thrown. Such exceptions will generally be
thrown by an <classname>AbstractSecurityInterceptor</classname>, which is
the main provider of authorization services. We will discuss
<classname>AbstractSecurityInterceptor</classname> in the next section,
but for now we just need to know that it produces Java exceptions and
knows nothing about HTTP or how to go about authenticating a
principal. Instead the <classname>ExceptionTranslationFilter</classname>
offers this service, with specific responsibility for either returning
error code 403 (if the principal has been authenticated and therefore
simply lacks sufficient access - as per step seven above), or
launching an <interfacename>AuthenticationEntryPoint</interfacename> (if the
principal has not been authenticated and therefore we need to go
commence step three).</para>
</section>
<section xml:id="tech-auth-entry-point">
<title>AuthenticationEntryPoint</title>
<para>The <interfacename>AuthenticationEntryPoint</interfacename> is responsible
for step three in the above list. As you can imagine, each web
application will have a default authentication strategy (well, this
can be configured like nearly everything else in Spring Security, but
let's keep it simple for now). Each major authentication system will
have its own <interfacename>AuthenticationEntryPoint</interfacename>
implementation, which takes actions such as described in step
three.</para>
<para>After your browser decides to submit your authentication
credentials (either as an HTTP form post or HTTP header) there needs
to be something on the server that "collects" these authentication
details. By now we're at step six in the above list. In Spring
Security we have a special name for the function of collecting
authentication details from a user agent (usually a web browser), and
that name is "authentication mechanism". After the authentication
details are collected from the user agent, an
"<interfacename>Authentication</interfacename> request" object is built and then
presented to an
<interfacename>AuthenticationProvider</interfacename>.</para>
</section>
<section>
<title>AuthenticationProvider</title>
<para>The last player in the Spring Security authentication process is
an <classname>AuthenticationProvider</classname>. Quite simply, it is
responsible for taking an <interfacename>Authentication</interfacename> request
object and deciding whether or not it is valid. The provider will
either throw an exception or return a fully populated
<interfacename>Authentication</interfacename> object. Remember our good friends,
<interfacename>UserDetails</interfacename> and
<interfacename>UserDetailsService</interfacename>? If not, head back to the
previous section and refresh your memory. Most
<classname>AuthenticationProvider</classname>s will ask a
<interfacename>UserDetailsService</interfacename> to provide a
<interfacename>UserDetails</interfacename> object. As mentioned earlier, most
application will provide their own
<interfacename>UserDetailsService</interfacename>, although some will be able to
use the JDBC or in-memory implementation that ships with Spring
Security. The resultant <interfacename>UserDetails</interfacename> object - and
particularly the <literal>GrantedAuthority[]</literal>s contained
within the <interfacename>UserDetails</interfacename> object - will be used when
building the fully populated <interfacename>Authentication</interfacename>
object.</para>
<para>After the authentication mechanism receives back the
fully-populated <interfacename>Authentication</interfacename> object, it will deem
the request valid, put the <interfacename>Authentication</interfacename> into the
<classname>SecurityContextHolder</classname>, and cause the original
request to be retried (step seven above). If, on the other hand, the
<classname>AuthenticationProvider</classname> rejected the request, the
authentication mechanism will ask the user agent to retry (step two
above).</para>
</section>
<section>
<title>Setting the SecurityContextHolder Contents Directly</title>
<para>Whilst this describes the typical authentication workflow, the
good news is that Spring Security doesn't mind how you put an
<interfacename>Authentication</interfacename> inside the
<classname>SecurityContextHolder</classname>. The only critical
requirement is that the <classname>SecurityContextHolder</classname>
contains an <interfacename>Authentication</interfacename> that represents a
principal before the <classname>AbstractSecurityInterceptor</classname>
needs to authorize a request.</para>
<para>You can (and many users do) write their own filters or MVC
controllers to provide interoperability with authentication systems
that are not based on Spring Security. For example, you might be using
Container-Managed Authentication which makes the current user
available from a ThreadLocal or JNDI location. Or you might work for a
company that has a legacy proprietary authentication system, which is
a corporate "standard" over which you have little control. In such
situations it's quite easy to get Spring Security to work, and still
provide authorization capabilities. All you need to do is write a
filter (or equivalent) that reads the third-party user information
from a location, build a Spring Security-specific Authentication
object, and put it onto the SecurityContextHolder. It's quite easy to
do this, and it is a fully-supported integration approach.</para>
</section>
</section>
<section xml:id="secure-objects">
<info><title>Secure Objects</title></info>
<para>Spring Security uses the term "secure object" to refer to any
object that can have security (such as an authorization decision) applied to it.
The most common examples are method invocations and web requests.
</para>
<section>
<title>Security and AOP Advice</title>
<para>If you're familiar with AOP, you'd be aware there are different
types of advice available: before, after, throws and around. An around
advice is very useful, because an advisor can elect whether or not to
proceed with a method invocation, whether or not to modify the
response, and whether or not to throw an exception. Spring Security
provides an around advice for method invocations as well as web
requests. We achieve an around advice for method invocations using Spring's
standard AOP support and we achieve an around advice for web requests using a
standard Filter.</para>
<para>For those not familiar with AOP, the key point to understand is
that Spring Security can help you protect method invocations as well
as web requests. Most people are interested in securing method
invocations on their services layer. This is because the services
layer is where most business logic resides in current-generation J2EE
applications (for clarification, the author disapproves of this design
and instead advocates properly encapsulated domain objects together
with the DTO, assembly, facade and transparent persistence patterns,
but as use of anemic domain objects is the present mainstream approach, we'll
talk about it here). If you just need to secure method invocations to
the services layer, Spring's standard AOP (otherwise known as AOP Alliance)
will be adequate. If you need to secure domain objects directly, you
will likely find that AspectJ is worth considering.</para>
<para>You can elect to perform method authorization using AspectJ or
Spring AOP, or you can elect to perform web request authorization
using filters. You can use zero, one, two or three of these approaches
together. The mainstream usage is to perform some web request
authorization, coupled with some Spring AOP method invocation
authorization on the services layer.</para>
</section>
<section>
<title>AbstractSecurityInterceptor</title>
<para>Each secure object type supported by Spring Security has its own class,
which is a subclass of <classname>AbstractSecurityInterceptor</classname>.
Importantly, by the time the <classname>AbstractSecurityInterceptor</classname> is called, the
<classname>SecurityContextHolder</classname> will contain a valid
<interfacename>Authentication</interfacename> if the principal has been
authenticated.</para>
<para><classname>AbstractSecurityInterceptor</classname> provides a
consistent workflow for handling secure object requests, typically:
<orderedlist>
<listitem><para>Look up the "configuration attributes" associated with the
present request</para></listitem>
<listitem><para>Submitting the secure object, current <interfacename>Authentication</interfacename>
and configuration attributes to the <interfacename>AccessDecisionManager</interfacename> for
an authorization decision</para></listitem>
<listitem><para>Optionally change the <interfacename>Authentication</interfacename> under which the invocation
takes place</para></listitem>
<listitem><para>Allow the secure object to proceed (assuming access was granted)</para></listitem>
<listitem><para>Call the <literal>AfterInvocationManager</literal> if configured, once the invocation
has returned.</para></listitem>
</orderedlist>
</para>
<section>
<title>What are Configuration Attributes?</title>
<para>
A "configuration attribute" can be thought of as a String that has special meaning to the classes used by
<classname>AbstractSecurityInterceptor</classname>. They may be simple role names or have more complex meaning, depending on the
how sophisticated the <interfacename>AccessDecisionManager</interfacename> implementation is.
The <classname>AbstractSecurityInterceptor</classname> is configured with a <interfacename>SecurityMetadataSource</interfacename> which
it uses to look up the attributes for a secure object. Usually this configuration will be hidden from the user. Configuration
attributes will be entered as annotations on secured methods, or as access attributes on secured URLs (using the
namespace <literal>&lt;intercept-url&gt;</literal> syntax).
</para>
</section>
<section>
<title>RunAsManager</title>
<para>Assuming <interfacename>AccessDecisionManager</interfacename> decides to
allow the request, the <classname>AbstractSecurityInterceptor</classname>
will normally just proceed with the request. Having said that, on rare
occasions users may want to replace the
<interfacename>Authentication</interfacename> inside the
<interfacename>SecurityContext</interfacename> with a different
<interfacename>Authentication</interfacename>, which is handled by the
<interfacename>AccessDecisionManager</interfacename> calling a
<literal>RunAsManager</literal>. This might be useful in reasonably
unusual situations, such as if a services layer method needs to call a
remote system and present a different identity. Because Spring
Security automatically propagates security identity from one server to
another (assuming you're using a properly-configured RMI or
HttpInvoker remoting protocol client), this may be useful.</para>
</section>
<section>
<title>AfterInvocationManager</title>
<para>Following the secure object proceeding and then returning -
which may mean a method invocation completing or a filter chain
proceeding - the <classname>AbstractSecurityInterceptor</classname> gets
one final chance to handle the invocation. At this stage the
<classname>AbstractSecurityInterceptor</classname> is interested in
possibly modifying the return object. We might want this to happen
because an authorization decision couldn't be made "on the way in" to
a secure object invocation. Being highly pluggable,
<classname>AbstractSecurityInterceptor</classname> will pass control to an
<literal>AfterInvocationManager</literal> to actually modify the
object if needed. This class can even entirely replace the object, or
throw an exception, or not change it in any way.</para>
<para><classname>AbstractSecurityInterceptor</classname> and its related objects
are shown in <xref linkend="abstract-security-interceptor"/>.
<figure xml:id="abstract-security-interceptor">
<title>The key "secure object" model</title>
<mediaobject>
<imageobject role="html">
<imagedata align="center" fileref="images/SecurityInterception.gif" format="GIF"/>
</imageobject>
<imageobject role="fo">
<imagedata align="center" fileref="resources/images/SecurityInterception.gif" format="GIF"/>
</imageobject>
</mediaobject>
</figure>
</para>
</section>
<section>
<title>Extending the Secure Object Model</title>
<para>Only developers contemplating an entirely new way of
intercepting and authorizing requests would need to use secure objects
directly. For example, it would be possible to build a new secure
object to secure calls to a messaging system. Anything that requires
security and also provides a way of intercepting a call (like the AOP
around advice semantics) is capable of being made into a secure
object. Having said that, most Spring applications will simply use the
three currently supported secure object types (AOP Alliance
<classname>MethodInvocation</classname>, AspectJ
<literal>JoinPoint</literal> and web request
<classname>FilterInvocation</classname>) with complete
transparency.</para>
</section>
</section>
</section>
</chapter>