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.
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.
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.
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.
The most fundamental object is
SecurityContextHolder
. 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 SecurityContextHolder
uses a
ThreadLocal
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
ThreadLocal
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.
Some applications aren't entirely suitable for using a
ThreadLocal
, 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
SecurityContextHolder.MODE_GLOBAL
. Other
applications might want to have threads spawned by the secure thread
also assume the same security identity. This is achieved by using
SecurityContextHolder.MODE_INHERITABLETHREADLOCAL
.
You can change the mode from the default
SecurityContextHolder.MODE_THREADLOCAL
in two ways.
The first is to set a system property. Alternatively, call a static
method on SecurityContextHolder
. Most applications
won't need to change from the default, but if you do, take a look at
the JavaDocs for SecurityContextHolder
to learn
more.
Inside the SecurityContextHolder
we store
details of the principal currently interacting with the application.
Spring Security uses an Authentication
object to
represent this information. Whilst you won't normally need to create
an Authentication
object yourself, it is fairly
common for users to query the Authentication
object. You can use the following code block - from anywhere in your
application - to obtain the name of the authenticated user, for example:
Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); if (obj instanceof UserDetails) { String username = ((UserDetails)obj).getUsername(); } else { String username = obj.toString(); }
The above code introduces a number of interesting relationships
and key objects. First, you will notice that there is an intermediate
object between SecurityContextHolder
and
Authentication
. The
SecurityContextHolder.getContext()
method is
actually returning a SecurityContext
.
Another item to note from the above code fragment is that you
can obtain a principal from the Authentication
object. The principal is just an Object
. Most of
the time this can be cast into a UserDetails
object. UserDetails
is a central interface in
Spring Security. It represents a principal, but in an extensible and
application-specific way. Think of UserDetails
as
the adapter between your own user database and what Spring Security
needs inside the SecurityContextHolder
. Being a
representation of something from your own user database, quite often
you will cast the UserDetails
to the original
object that your application provided, so you can call
business-specific methods (like getEmail()
,
getEmployeeNumber()
and so on).
By now you're probably wondering, so when do I provide a
UserDetails
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 UserDetailsService
. The only
method on this interface accepts a String
-based
username argument and returns a UserDetails
. Most
authentication providers that ship with Spring Security delegate to a
UserDetailsService
as part of the authentication
process. The UserDetailsService
is used to build
the Authentication
object that is stored in the
SecurityContextHolder
. The good news is that we
provide a number of UserDetailsService
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 SecurityContextHolder
, as per the above code
fragment.
Besides the principal, another important method provided by
Authentication
is
getAuthorities(
). This method provides an array of
GrantedAuthority
objects. A
GrantedAuthority
is, not surprisingly, an authority
that is granted to the principal. Such authorities are usually
"roles", such as ROLE_ADMINISTRATOR
or
ROLE_HR_SUPERVISOR
. 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.
GrantedAuthority
objects are usually loaded by the
UserDetailsService
.
Usually the GrantedAuthority
objects are
application-wide permissions. They are not specific to a given domain
object. Thus, you wouldn't likely have a
GrantedAuthority
to represent a permission to
Employee
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.
Last but not least, sometimes you will need to store the
SecurityContext
between HTTP requests. Other times
the principal will re-authenticate on every request, although most of
the time it will be stored. The
HttpSessionContextIntegrationFilter
is responsible
for storing a SecurityContext
between HTTP
requests. As suggested by the name of the class, the
HttpSession
is used to store this information. You
should never interact directly with the HttpSession
for security purposes. There is simply no justification for doing so -
always use the SecurityContextHolder
instead.
Just to recap, the major building blocks of Spring Security are:
SecurityContextHolder
, to provide any
type access to the SecurityContext
.
SecurityContext
, to hold the
Authentication
and possibly request-specific
security information.
HttpSessionContextIntegrationFilter
, to
store the SecurityContext
in the
HttpSession
between web requests.
Authentication
, to represent the
principal in a Spring Security-specific manner.
GrantedAuthority
, to reflect the
application-wide permissions granted to a principal.
UserDetails
, to provide the necessary
information to build an Authentication object from your
application's DAOs.
UserDetailsService
, to create a
UserDetails
when passed in a
String
-based username (or certificate ID or
alike).
Now that you've gained an understanding of these repeatedly-used components, let's take a closer look at the process of authentication.
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.
Consider a typical web application's authentication process:
You visit the home page, and click on a link.
A request goes to the server, and the server decides that you've asked for a protected resource.
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.
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).
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.
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).
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".
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 ExceptionTranslationFilter
, an
AuthenticationEntryPoint
, an authentication
mechanism, and an AuthenticationProvider
.
ExceptionTranslationFilter
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 AbstractSecurityInterceptor
, which is
the main provider of authorization services. We will discuss
AbstractSecurityInterceptor
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 ExceptionTranslationFilter
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 AuthenticationEntryPoint
(if the
principal has not been authenticated and therefore we need to go
commence step three).
The AuthenticationEntryPoint
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 AuthenticationEntryPoint
implementation, which takes actions such as described in step
three.
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
"Authentication
request" object is built and then
presented to an
AuthenticationProvider
.
The last player in the Spring Security authentication process is
an AuthenticationProvider
. Quite simply, it is
responsible for taking an Authentication
request
object and deciding whether or not it is valid. The provider will
either throw an exception or return a fully populated
Authentication
object. Remember our good friends,
UserDetails
and
UserDetailsService
? If not, head back to the
previous section and refresh your memory. Most
AuthenticationProvider
s will ask a
UserDetailsService
to provide a
UserDetails
object. As mentioned earlier, most
application will provide their own
UserDetailsService
, although some will be able to
use the JDBC or in-memory implementation that ships with Spring
Security. The resultant UserDetails
object - and
particularly the GrantedAuthority[]
s contained
within the UserDetails
object - will be used when
building the fully populated Authentication
object.
After the authentication mechanism receives back the
fully-populated Authentication
object, it will deem
the request valid, put the Authentication
into the
SecurityContextHolder
, and cause the original
request to be retried (step seven above). If, on the other hand, the
AuthenticationProvider
rejected the request, the
authentication mechanism will ask the user agent to retry (step two
above).
Whilst this describes the typical authentication workflow, the
good news is that Spring Security doesn't mind how you put an
Authentication
inside the
SecurityContextHolder
. The only critical
requirement is that the SecurityContextHolder
contains an Authentication
that represents a
principal before the AbstractSecurityInterceptor
needs to authorize a request.
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.
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.
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.
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.
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.
Each secure object type supported by Spring Security has its own class,
which is a subclass of AbstractSecurityInterceptor
.
Importantly, by the time the AbstractSecurityInterceptor
is called, the
SecurityContextHolder
will contain a valid
Authentication
if the principal has been
authenticated.
AbstractSecurityInterceptor
provides a
consistent workflow for handling secure object requests, typically:
Look up the "configuration attributes" associated with the present request
Submitting the secure object, current Authentication
and configuration attributes to the AccessDecisionManager
for
an authorization decision
Optionally change the Authentication
under which the invocation
takes place
Allow the secure object to proceed (assuming access was granted)
Call the AfterInvocationManager
if configured, once the invocation
has returned.
A "configuration attribute" can be thought of as a String that has special meaning to the classes used by
AbstractSecurityInterceptor
. They may be simple role names or have more complex meaning, depending on the
how sophisticated the AccessDecisionManager
implementation is.
The AbstractSecurityInterceptor
is configured with an ObjectDefinitionSource
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 <intercept-url>
syntax).
Assuming AccessDecisionManager
decides to
allow the request, the AbstractSecurityInterceptor
will normally just proceed with the request. Having said that, on rare
occasions users may want to replace the
Authentication
inside the
SecurityContext
with a different
Authentication
, which is handled by the
AccessDecisionManager
calling a
RunAsManager
. 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.
Following the secure object proceeding and then returning -
which may mean a method invocation completing or a filter chain
proceeding - the AbstractSecurityInterceptor
gets
one final chance to handle the invocation. At this stage the
AbstractSecurityInterceptor
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,
AbstractSecurityInterceptor
will pass control to an
AfterInvocationManager
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.
AbstractSecurityInterceptor
and its related objects
are shown in Figure 5.1, “The key "secure object" model”.
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
MethodInvocation
, AspectJ
JoinPoint
and web request
FilterInvocation
) with complete
transparency.