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.