Shared Components

Let's explore some of the most important shared components in Spring Security. Components are considered "shared" if they are central to the framework and the framework cannot operate without them. These Java types represent the building blocks of the remaining system, so it's important to understand that they're there, even if you don't need to directly interact with them.

SecurityContextHolder, SecurityContext and Authentication Objects

The most fundamental object is SecurityContextHolder. This is where we store details of the present security context of the application, which includes details of the principal currently using the application. By default the SecurityContextHolder uses a ThreadLocal to store these details, which means that the security context is always available to methods in the same thread of execution, even if the security context is not explicitly passed around as an argument to those methods. Using a ThreadLocal in this way is quite safe if care is taken to clear the thread after the present principal's request is processed. Of course, Spring Security takes care of this for you automatically so there is no need to worry about it.

Some applications aren't entirely suitable for using a ThreadLocal, because of the specific way they work with threads. For example, a Swing client might want all threads in a Java Virtual Machine to use the same security context. For this situation you would use the SecurityContextHolder.MODE_GLOBAL. Other applications might want to have threads spawned by the secure thread also assume the same security identity. This is achieved by using SecurityContextHolder.MODE_INHERITABLETHREADLOCAL. You can change the mode from the default SecurityContextHolder.MODE_THREADLOCAL in two ways. The first is to set a system property. Alternatively, call a static method on SecurityContextHolder. Most applications won't need to change from the default, but if you do, take a look at the JavaDocs for SecurityContextHolder to learn more.

Inside the SecurityContextHolder we store details of the principal currently interacting with the application. Spring Security uses an Authentication object to represent this information. Whilst you won't normally need to create an Authentication object yourself, it is fairly common for users to query the Authentication object. You can use the following code block - from anywhere in your application - to obtain the name of the authenticated user, for example:

Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

if (obj instanceof UserDetails) {
  String username = ((UserDetails)obj).getUsername();
} else {
  String username = obj.toString();
}

The above code introduces a number of interesting relationships and key objects. First, you will notice that there is an intermediate object between SecurityContextHolder and Authentication. The SecurityContextHolder.getContext() method is actually returning a SecurityContext.

The UserDetailsService

Another item to note from the above code fragment is that you can obtain a principal from the Authentication object. The principal is just an Object. Most of the time this can be cast into a UserDetails object. UserDetails is a central interface in Spring Security. It represents a principal, but in an extensible and application-specific way. Think of UserDetails as the adapter between your own user database and what Spring Security needs inside the SecurityContextHolder. Being a representation of something from your own user database, quite often you will cast the UserDetails to the original object that your application provided, so you can call business-specific methods (like getEmail(), getEmployeeNumber() and so on).

By now you're probably wondering, so when do I provide a UserDetails object? How do I do that? I thought you said this thing was declarative and I didn't need to write any Java code - what gives? The short answer is that there is a special interface called UserDetailsService. The only method on this interface accepts a String-based username argument and returns a UserDetails. Most authentication providers that ship with Spring Security delegate to a UserDetailsService as part of the authentication process. The UserDetailsService is used to build the Authentication object that is stored in the SecurityContextHolder. The good news is that we provide a number of UserDetailsService implementations, including one that uses an in-memory map and another that uses JDBC. Most users tend to write their own, though, with such implementations often simply sitting on top of an existing Data Access Object (DAO) that represents their employees, customers, or other users of the enterprise application. Remember the advantage that whatever your UserDetailsService returns can always be obtained from the SecurityContextHolder, as per the above code fragment.

GrantedAuthority

Besides the principal, another important method provided by Authentication is getAuthorities(). This method provides an array of GrantedAuthority objects. A GrantedAuthority is, not surprisingly, an authority that is granted to the principal. Such authorities are usually "roles", such as ROLE_ADMINISTRATOR or ROLE_HR_SUPERVISOR. These roles are later on configured for web authorization, method authorization and domain object authorization. Other parts of Spring Security are capable of interpreting these authorities, and expect them to be present. GrantedAuthority objects are usually loaded by the UserDetailsService.

Usually the GrantedAuthority objects are application-wide permissions. They are not specific to a given domain object. Thus, you wouldn't likely have a GrantedAuthority to represent a permission to Employee object number 54, because if there are thousands of such authorities you would quickly run out of memory (or, at the very least, cause the application to take a long time to authenticate a user). Of course, Spring Security is expressly designed to handle this common requirement, but you'd instead use the project's domain object security capabilities for this purpose.

Last but not least, sometimes you will need to store the SecurityContext between HTTP requests. Other times the principal will re-authenticate on every request, although most of the time it will be stored. The HttpSessionContextIntegrationFilter is responsible for storing a SecurityContext between HTTP requests. As suggested by the name of the class, the HttpSession is used to store this information. You should never interact directly with the HttpSession for security purposes. There is simply no justification for doing so - always use the SecurityContextHolder instead.

Summary

Just to recap, the major building blocks of Spring Security are:

  • SecurityContextHolder, to provide any type access to the SecurityContext.

  • SecurityContext, to hold the Authentication and possibly request-specific security information.

  • HttpSessionContextIntegrationFilter, to store the SecurityContext in the HttpSession between web requests.

  • Authentication, to represent the principal in a Spring Security-specific manner.

  • GrantedAuthority, to reflect the application-wide permissions granted to a principal.

  • UserDetails, to provide the necessary information to build an Authentication object from your application's DAOs.

  • UserDetailsService, to create a UserDetails when passed in a String-based username (or certificate ID or alike).

Now that you've gained an understanding of these repeatedly-used components, let's take a closer look at the process of authentication.