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:

  1. You visit the home page, and click on a link.

  2. A request goes to the server, and the server decides that you've asked for a protected resource.

  3. 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.

  4. 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).

  5. 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.

  6. 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).

  7. 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

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).

AuthenticationEntryPoint

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.

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 AuthenticationProviders 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).

Setting the SecurityContextHolder Contents Directly

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.