123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719 |
- <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 context 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 xml:id="tech-userdetailsservice">
- <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 core 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
- linkend="tech-intro-authentication">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>
- <note>
- <para>There is often some confusion about <interfacename>UserDetailsService</interfacename>.
- It is purely a DAO for user data and performs no other function other than to supply that data
- to other components within the framework. In particular, it <emphasis>does not</emphasis>
- authenticate the user, which is done by the <interfacename>AuthenticationManager</interfacename>.
- In many cases it makes more sense to
- <link linkend="core-services-authentication-manager">implement <interfacename>AuthenticationProvider</interfacename></link>
- directly if you require a custom authentication process.
- </para>
- </note>
- </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 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.SimpleGrantedAuthority;
- 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 SimpleGrantedAuthority("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 language="txt">
- 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>Authentication</interfacename> object.</para>
- </section>
- <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> which 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 into the
- <classname>SecurityContextHolder</classname>. In this case you also need to think
- about things which are normally taken care of automatically by the built-in authentication
- infrastructure. For example, you might need to pre-emptively create an HTTP session to
- <link xlink:href="tech-intro-sec-context-persistence">cache the context between requests</link>,
- before you write the response to the client<footnote><para>It isn't possible to create a session once the
- response has been committed.</para></footnote>.
- </para>
- <para> If you're wondering how the <interfacename>AuthenticationManager</interfacename>
- is implemented in a real world example, we'll look at that in the <link
- linkend="core-services-authentication-manager">core services
- chapter</link>.</para>
- </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 responsible 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>
- <note>
- <para>In an application which receives concurrent requests in a single session, the
- same <interfacename>SecurityContext</interfacename> instance will be shared
- between threads. Even though a <classname>ThreadLocal</classname> is being used,
- it is the same instance that is retrieved from the
- <interfacename>HttpSession</interfacename> for each thread. This has
- implications if you wish to temporarily change the context under which a thread
- is running. If you just use <code>SecurityContextHolder.getContext()</code>, and
- call <code>setAuthentication(anAuthentication)</code> on the returned context
- object, then the <interfacename>Authentication</interfacename> object will
- change in <emphasis>all</emphasis> concurrent threads which share the same
- <interfacename>SecurityContext</interfacename> instance. You can customize the
- behaviour of <classname>SecurityContextPersistenceFilter</classname> to create a
- completely new <interfacename>SecurityContext</interfacename> for each request,
- preventing changes in one thread from affecting another. Alternatively you can
- create a new instance just at the point where you temporarily change the
- context. The method <code>SecurityContextHolder.createEmptyContext()</code>
- always returns a new context instance.</para>
- </note>
- </section>
- </section>
- <section xml:id="tech-intro-access-control">
- <title>Access-Control (Authorization) in Spring Security</title>
- <para> The main interface responsible for making access-control decisions in Spring Security
- is the <interfacename>AccessDecisionManager</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. If the invocation raised an
- exception, the <interfacename>AfterInvocationManager</interfacename>
- will not be invoked.</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. For
- example, when we saw something like <literal><intercept-url
- pattern='/secure/**' access='ROLE_A,ROLE_B'/></literal> in the namespace
- introduction, this is saying that the configuration attributes
- <literal>ROLE_A</literal> and <literal>ROLE_B</literal> apply to web requests
- matching the given pattern. In practice, with the default
- <interfacename>AccessDecisionManager</interfacename> configuration, this means
- that anyone who has a <interfacename>GrantedAuthority</interfacename> matching
- either of these two attributes will be allowed access. Strictly speaking though,
- they are just attributes and the interpretation is dependent on the
- <interfacename>AccessDecisionManager</interfacename> implementation. The use of
- the prefix <literal>ROLE_</literal> is a marker to indicate that these
- attributes are roles and should be consumed by Spring Security's
- <classname>RoleVoter</classname>. This is only relevant when a voter-based
- <interfacename>AccessDecisionManager</interfacename> is in use. We'll see how
- the <interfacename>AccessDecisionManager</interfacename> is implemented in the
- <link linkend="authz-arch">authorization chapter</link>.</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 invocation 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. The after-invocation checks will only
- be executed if the invocation is successful. If an exception occurs, the additional
- checks will be skipped.
- </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" scale="75"/>
- </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
- messages that are focused on developers or system deployers (including incorrect
- attributes, interface contract violations, using incorrect constructors, startup time
- validation, debug-level logging) 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, as well as localized versions for some
- common languages. 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 language="xml"><![CDATA[
- <bean id="messageSource"
- class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
- <property name="basename" value="classpath: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. </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>Spring Security relies on Spring's localization support in order to actually lookup
- the appropriate message. In order for this to work, you have to make sure that the
- locale from the incoming request is stored in Spring's
- <classname>org.springframework.context.i18n.LocaleContextHolder</classname>. Spring
- MVC's <classname>DispatcherServlet</classname> does this for your application
- automatically, but since Spring Security's filters are invoked before this, the
- <classname>LocaleContextHolder</classname> needs to be set up to contain the correct
- <literal>Locale</literal> before the filters are called. You can either do this in a
- filter yourself (which must come before the Spring Security filters in
- <filename>web.xml</filename>) or you can use Spring's
- <classname>RequestContextFilter</classname>. Please refer to the Spring Framework
- documentation for further details on using localization with Spring. </para>
- <para>The <quote>contacts</quote> sample application is set up to use localized messages.
- </para>
- </section>
- </chapter>
|