624 lines
40 KiB
XML
624 lines
40 KiB
XML
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="technical-overview"
|
|
xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
<info>
|
|
<title>Technical Overview</title>
|
|
</info>
|
|
<section xml:id="runtime-environment">
|
|
<info>
|
|
<title>Runtime Environment</title>
|
|
</info>
|
|
<para>Spring Security 3.0 requires a Java 5.0 Runtime Environment or higher. 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.
|
|
All the required files will be contained within your application.</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="core-components">
|
|
<info>
|
|
<title>Core Components</title>
|
|
</info>
|
|
<para>In Spring Security 3.0, the contents of the <filename>spring-security-core</filename> jar
|
|
were stripped down to the bare minimum. It no longer contains any code related to
|
|
web-application security, LDAP or namespace configuration. We'll take a look here at some of
|
|
the Java types that you'll find in the core module. They represent the building blocks of the
|
|
the framework, so if you ever need to go beyond a simple namespace configuration then it's
|
|
important that you understand what they are, even if you don't actually need to interact with
|
|
them directly.</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.
|
|
<classname>SecurityContextHolder</classname> can be configured with a strategy on startup
|
|
to specify how you would like the contex to be stored. For a standalone application you
|
|
would use the <literal>SecurityContextHolder.MODE_GLOBAL</literal> strategy. 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, the second is to 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>
|
|
<section>
|
|
<title>Obtaining information about the current user</title>
|
|
<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. You
|
|
won't normally need to create an <interfacename>Authentication</interfacename> object
|
|
yourself, but 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 currently
|
|
authenticated user, for example:</para>
|
|
<programlisting language="java">
|
|
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
|
|
|
if (principal instanceof UserDetails) {
|
|
String username = ((UserDetails)principal).getUsername();
|
|
} else {
|
|
String username = principal.toString();
|
|
}</programlisting>
|
|
<para>The object returned by the call to <methodname>getContext()</methodname> is an
|
|
instance of the <interfacename>SecurityContext</interfacename> interface. This is the
|
|
object that is kept in thread-local storage. As we'll see below, Most authentication
|
|
mechanisms withing Spring Security return an instance of
|
|
<interfacename>UserDetails</interfacename> as the principal. </para>
|
|
</section>
|
|
</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>:
|
|
<programlisting language="java">
|
|
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
|
|
</programlisting>
|
|
This is the most common approach to loading information for a user within Spring Security
|
|
and you will see it used throughout the framework whenever information on a user is
|
|
required.</para>
|
|
<para> On successful authentication, <interfacename>UserDetails</interfacename> is used to
|
|
build the <interfacename>Authentication</interfacename> object that is stored in the
|
|
<classname>SecurityContextHolder</classname> (more on this <link
|
|
xlink:href="#tech-intro-authentication-mgr">below</link>). The good news is that we
|
|
provide a number of <interfacename>UserDetailsService</interfacename> implementations,
|
|
including one that uses an in-memory map (<classname>InMemoryDaoImpl</classname>) and
|
|
another that uses JDBC (<classname>JdbcDaoImpl</classname>). Most users tend to
|
|
write their own, though, with their implementations often simply sitting on top of an
|
|
existing Data Access Object (DAO) that represents their employees, customers, or other users
|
|
of the application. Remember the advantage that whatever your
|
|
<interfacename>UserDetailsService</interfacename> returns can always be obtained from the
|
|
<classname>SecurityContextHolder</classname> using 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 <quote>roles</quote>, 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>
|
|
</section>
|
|
<section>
|
|
<title>Summary</title>
|
|
<para>Just to recap, the major building blocks of Spring Security that we've seen so far
|
|
are:</para>
|
|
<itemizedlist spacing="compact">
|
|
<listitem>
|
|
<para><classname>SecurityContextHolder</classname>, to provide 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><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 or other source source of
|
|
security data.</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 the like).</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="tech-intro-authentication">
|
|
<info>
|
|
<title>Authentication</title>
|
|
</info>
|
|
<para>Spring Security can participate in many different authentication environments. While 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. </para>
|
|
<section>
|
|
<title>What is authentication in Spring Security?</title>
|
|
<para> Let's consider a standard authentication scenario that everyone is familiar with. <orderedlist>
|
|
<listitem>
|
|
<para>A user is prompted to log in with a username and password.</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>The system (successfully) verifies that the password is correct for the
|
|
username.</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>The context information for that user is obtained (their list of roles and so
|
|
on).</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>A security context is established for the user</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>The user proceeds, potentially to perform some operation which is potentially
|
|
protected by an access control mechanism which checks the required permissions for the
|
|
operation against the current security context information. </para>
|
|
</listitem>
|
|
</orderedlist> The first three items constitute the authentication process so we'll take a
|
|
look at how these take place within Spring Security.<orderedlist>
|
|
<listitem>
|
|
<para>The username and password are obtained and combined into an instance of
|
|
<classname>UsernamePasswordAuthenticationToken</classname> (an instance of the
|
|
<interfacename>Authentication</interfacename> interface, which we saw
|
|
earlier).</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>The token is passed to an instance of
|
|
<interfacename>AuthenticationManager</interfacename> for validation.</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>The <interfacename>AuthenticationManager</interfacename> returns a fully populated
|
|
<interfacename>Authentication</interfacename> instance on successful
|
|
authentication.</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>The security context is established by calling
|
|
<code>SecurityContextHolder.getContext().setAuthentication(...)</code>, passing in
|
|
the returned authentication object.</para>
|
|
</listitem>
|
|
</orderedlist>From that point on, the user is considered to be authenticated. Let's look at
|
|
some code as an example.
|
|
<programlisting language="java">import org.springframework.security.authentication.*;
|
|
import org.springframework.security.core.*;
|
|
import org.springframework.security.core.authority.GrantedAuthorityImpl;
|
|
import org.springframework.security.core.context.SecurityContextHolder;
|
|
|
|
public class AuthenticationExample {
|
|
private static AuthenticationManager am = new SampleAuthenticationManager();
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
|
|
|
|
while(true) {
|
|
System.out.println("Please enter your username:");
|
|
String name = in.readLine();
|
|
System.out.println("Please enter your password:");
|
|
String password = in.readLine();
|
|
try {
|
|
Authentication request = new UsernamePasswordAuthenticationToken(name, password);
|
|
Authentication result = am.authenticate(request);
|
|
SecurityContextHolder.getContext().setAuthentication(result);
|
|
break;
|
|
} catch(AuthenticationException e) {
|
|
System.out.println("Authentication failed: " + e.getMessage());
|
|
}
|
|
}
|
|
System.out.println("Successfully authenticated. Security context contains: " +
|
|
SecurityContextHolder.getContext().getAuthentication());
|
|
}
|
|
}
|
|
|
|
class SampleAuthenticationManager implements AuthenticationManager {
|
|
static final List<GrantedAuthority> AUTHORITIES = new ArrayList<GrantedAuthority>();
|
|
|
|
static {
|
|
AUTHORITIES.add(new GrantedAuthorityImpl("ROLE_USER"));
|
|
}
|
|
|
|
public Authentication authenticate(Authentication auth) throws AuthenticationException {
|
|
if (auth.getName().equals(auth.getCredentials())) {
|
|
return new UsernamePasswordAuthenticationToken(auth.getName(),
|
|
auth.getCredentials(), AUTHORITIES);
|
|
}
|
|
throw new BadCredentialsException("Bad Credentials");
|
|
}
|
|
}</programlisting>Here
|
|
we have written a little program that asks the user to enter a username and password and
|
|
performs the above sequence. The <interfacename>AuthenticationManager</interfacename> which
|
|
we've implemented here will authenticate any user whose username and password are the same.
|
|
It assigns a single role to every user. The output from the above will be something
|
|
like:<programlisting>
|
|
Please enter your username:
|
|
bob
|
|
Please enter your password:
|
|
password
|
|
Authentication failed: Bad Credentials
|
|
Please enter your username:
|
|
bob
|
|
Please enter your password:
|
|
bob
|
|
Successfully authenticated. Security context contains: \
|
|
org.springframework.security.authentication.UsernamePasswordAuthenticationToken@441d0230: \
|
|
Principal: bob; Password: [PROTECTED]; \
|
|
Authenticated: true; Details: null; \
|
|
Granted Authorities: ROLE_USER
|
|
</programlisting></para>
|
|
<para>Note that you don't normally need to write any code like this. The process will normally
|
|
occur internally, in a web authentication filter for example. We've just included the code
|
|
here to show that the question of what actually constitutes authentication in Spring
|
|
Security has quite a simple answer. A user is authenticated when the
|
|
<classname>SecurityContextHolder</classname> contains a fully populated
|
|
<interfacename>Authentiation</interfacename> object.</para>
|
|
<section>
|
|
<title>Setting the SecurityContextHolder Contents Directly</title>
|
|
<para>In fact, Spring Security doesn't mind how you put the
|
|
<interfacename>Authentication</interfacename> object 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> (which we'll see more about later)
|
|
needs to authorize a user operation.</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 situations like this 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 <interfacename>Authentication</interfacename> object, and put it
|
|
onto the <classname>SecurityContextHolder</classname>.</para>
|
|
<para> If you're wondering how the <interfacename>AuthenticationManager</interfacename>
|
|
manager is implemented in a real world example, we'll look at that in </para>
|
|
</section>
|
|
</section>
|
|
</section>
|
|
<section xml:id="tech-intro-web-authentication">
|
|
<title>Authentication in a Web Application</title>
|
|
<para> Now let's explore the situation where you are using Spring Security in a web application
|
|
(without <filename>web.xml</filename> security enabled). How is a user authenticated and the
|
|
security context established?</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 (via a BASIC authentication dialogue box, a cookie, a X.509 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> and an <quote>authentication
|
|
mechanism</quote>, which is resposible for calling the
|
|
<classname>AuthenticationManager</classname> which we saw in the previous section.</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-intro-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 typically
|
|
performs one of the actions described in step 3.</para>
|
|
</section>
|
|
<section>
|
|
<title>Authentication Mechanism</title>
|
|
<para>Once your browser submits your authentication credentials (either as an HTTP form post
|
|
or HTTP header) there needs to be something on the server that <quote>collects</quote> 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), referring to it as the <quote>authentication mechanism</quote>.
|
|
Examples are form-base login and Basic authentication. Once the authentication details have
|
|
been collected from the user agent, an <interfacename>Authentication</interfacename>
|
|
<quote>request</quote> object is built and then presented to the
|
|
<interfacename>AuthenticationManager</interfacename>.</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>AuthenticationManager</classname>
|
|
rejected the request, the authentication mechanism will ask the user agent to retry (step
|
|
two above).</para>
|
|
</section>
|
|
<section xml:id="tech-intro-sec-context-persistence">
|
|
<title>Storing the <interfacename>SecurityContext</interfacename> between requests</title>
|
|
<para>Depending on the type of application, there may need to be a strategy in place to store
|
|
the security context between user operations. In a typical web application, a user logs in
|
|
once and is subsequently identified by their session Id. The server caches the principal
|
|
information for the duration session. In Spring Security, the responsibility for storing the
|
|
<interfacename>SecurityContext</interfacename> between requests falls to the
|
|
<classname>SecurityContextPersistenceFilter</classname>, which by default stores the
|
|
context as an <literal>HttpSession</literal> attribute between HTTP requests. It restores
|
|
the context to the <classname>SecurityContextHolder</classname> for each request and,
|
|
crucially, clears the <classname>SecurityContextHolder</classname> when the request
|
|
completes. You shouldn't 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>
|
|
<para> Many other types of application (for example, a stateless RESTful web service) do not
|
|
use HTTP sessions and will re-authenticate on every request. However, it is still important
|
|
that the <classname>SecurityContextPersistenceFilter</classname> is included in the chain to
|
|
make sure that the <classname>SecurityContextHolder</classname> is cleared after each
|
|
request.</para>
|
|
</section>
|
|
</section>
|
|
<section xml:id="tech-intro-access-control">
|
|
<title>Access-Control (Authorization) in Spring Security</title>
|
|
<para> The main interface resposible for making access-control decisions in Spring Security is
|
|
the <interfacename>AccessDecisionMananger</interfacename>. It has a
|
|
<methodname>decide</methodname> method which takes an
|
|
<interfacename>Authentication</interfacename> object representing the principal requesting
|
|
access, a <quote>secure object</quote> (see below) and a list of security metadata attributes
|
|
which apply for the object (such as a list of roles which are required for access to be
|
|
granted). </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. If you just need
|
|
to secure method invocations in the services layer, Spring's standard AOP 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 pattern is to perform some web
|
|
request authorization, coupled with some Spring AOP method invocation authorization on the
|
|
services layer.</para>
|
|
</section>
|
|
<section xml:id="secure-objects">
|
|
<title>Secure Objects and the <classname>AbstractSecurityInterceptor</classname></title>
|
|
<para>So what <emphasis>is</emphasis> a <quote>secure object</quote> anyway? Spring Security
|
|
uses the term 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>
|
|
<para>Each supported secure object type has its own interceptor 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 <quote>configuration attributes</quote> 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 invocation to proceed (assuming access was granted)</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>Call the <interfacename>AfterInvocationManager</interfacename> if configured, once
|
|
the invocation has returned.</para>
|
|
</listitem>
|
|
</orderedlist></para>
|
|
<section xml:id="tech-intro-config-attributes">
|
|
<title>What are Configuration Attributes?</title>
|
|
<para> A <quote>configuration attribute</quote> can be thought of as a String that has
|
|
special meaning to the classes used by <classname>AbstractSecurityInterceptor</classname>.
|
|
They are represented by the interface <interfacename>ConfigAttribute</interfacename>
|
|
within the framework. 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><intercept-url></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 <quote>on the way in</quote> 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 as it chooses.</para>
|
|
<para><classname>AbstractSecurityInterceptor</classname> and its related objects are shown
|
|
in <xref linkend="abstract-security-interceptor"/>. <figure
|
|
xml:id="abstract-security-interceptor">
|
|
<title>Security interceptors and the <quote>secure object</quote> model</title>
|
|
<mediaobject>
|
|
<imageobject>
|
|
<imagedata align="center" fileref="images/security-interception.png" format="PNG"/>
|
|
</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 <classname>JoinPoint</classname>
|
|
and web request <classname>FilterInvocation</classname>) with complete
|
|
transparency.</para>
|
|
</section>
|
|
</section>
|
|
</section>
|
|
<section xml:id="localization">
|
|
<title>Localization</title>
|
|
<para>Spring Security supports localization of exception messages that end users are likely to
|
|
see. If your application is designed for English-speaking users, you don't need to do anything
|
|
as by default all Security Security messages are in English. If you need to support other
|
|
locales, everything you need to know is contained in this section.</para>
|
|
<para>All exception messages can be localized, including messages related to authentication
|
|
failures and access being denied (authorization failures). Exceptions and logging that is
|
|
focused on developers or system deployers (including incorrect attributes, interface contract
|
|
violations, using incorrect constructors, startup time validation, debug-level logging) etc
|
|
are not localized and instead are hard-coded in English within Spring Security's code.</para>
|
|
<para>Shipping in the <literal>spring-security-core-xx.jar</literal> you will find an
|
|
<literal>org.springframework.security</literal> package that in turn contains a
|
|
<literal>messages.properties</literal> file. This should be referred to by your
|
|
<literal>ApplicationContext</literal>, as Spring Security classes implement Spring's
|
|
<literal>MessageSourceAware</literal> interface and expect the message resolver to be
|
|
dependency injected at application context startup time. Usually all you need to do is
|
|
register a bean inside your application context to refer to the messages. An example is shown
|
|
below:</para>
|
|
<para>
|
|
<programlisting><![CDATA[
|
|
<bean id="messageSource"
|
|
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
|
|
<property name="basename" value="org/springframework/security/messages"/>
|
|
</bean>
|
|
]]></programlisting>
|
|
</para>
|
|
<para>The <literal>messages.properties</literal> is named in accordance with standard resource
|
|
bundles and represents the default language supported by Spring Security messages. This
|
|
default file is in English. If you do not register a message source, Spring Security will
|
|
still work correctly and fallback to hard-coded English versions of the messages.</para>
|
|
<para>If you wish to customize the <literal>messages.properties</literal> file, or support other
|
|
languages, you should copy the file, rename it accordingly, and register it inside the above
|
|
bean definition. There are not a large number of message keys inside this file, so
|
|
localization should not be considered a major initiative. If you do perform localization of
|
|
this file, please consider sharing your work with the community by logging a JIRA task and
|
|
attaching your appropriately-named localized version of
|
|
<literal>messages.properties</literal>.</para>
|
|
<para>Rounding out the discussion on localization is the Spring <literal>ThreadLocal</literal>
|
|
known as <classname>org.springframework.context.i18n.LocaleContextHolder</classname>. You
|
|
should set the <classname>LocaleContextHolder</classname> to represent the preferred
|
|
<literal>Locale</literal> of each user. Spring Security will attempt to locate a message
|
|
from the message source using the <literal>Locale</literal> obtained from this
|
|
<literal>ThreadLocal</literal>. Please refer to the Spring Framework documentation for
|
|
further details on using <literal>LocaleContextHolder</literal>.</para>
|
|
</section>
|
|
</chapter>
|