Spring Security Authentication
Security is one of the most vital concerns for any organization.
Join the DZone community and get the full member experience.
Join For FreeAuthentication
One of the fundamental ways to secure a resource is to make sure that the caller is who they claim to be. This process of checking credentials and making sure that they are genuine is called authentication.
This article will delve into the technical capabilities of Spring Security, specifically authentication. To find the complete code for this article, check out this GitHub repository.
The following diagram shows the fundamental process Spring Security uses to address this core security requirement. The figure is generic and can be used to explain all the various authentication methods that the framework supports:
Spring Security has a series of servlet filters (a filter chain). When a request reaches the server, it is intercepted by this series of filters (Step 1 in the preceding diagram). In the reactive world, with the new Spring WebFlux web application framework, filters are written quite differently from traditional filters, such as those used in the Spring MVC web application framework. Having said that, the fundamental mechanism remains the same for both.
The Servlet filter code execution in the filter chain keeps skipping until the right filter is reached. Once it reaches the right authentication filter based on the authentication mechanism used, it extracts the supplied credentials — most commonly a username and password — from the caller.
Using the supplied values (here, you have a username and password), the filter UsernamePasswordAuthenticationFilter
creates an Authentication object. In the preceding diagram, the UsernamePasswordAuthenticationToken
is created with the username and password supplied in Step 2. The Authentication object created in Step 2 is then used to call the authenticate method in the AuthenticationManager
interface:
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication) throwsAuthenticationException;
}
The actual implementation is provided by the ProviderManager
, which has a list of the configured AuthenticationProvider
.
public interface AuthenticationProvider {
Authentication authenticate(Authentication authentication) throwsAuthenticationException;
boolean supports(Class<?> authentication);
}
The request passes through various providers and, in due course, tries to authenticate the request. There are a number of AuthenticationProvider
interfaces as part of Spring Security.
In the diagram above, AuthenticationProvider
requires user details (some providers require this, but some don’t), which are provided in UserDetailsService
:
public interface UserDetailsService {
UserDetailsloadUserByUsername(String username) throws UsernameNotFoundException;
}
UserDetailsService
retrieves UserDetails
and implements the User interface using the supplied username.
If all goes well, Spring Security creates a fully populated Authentication object (authenticate: true, granted authority list, and username), which will contain various necessary details. The Authentication object is stored in the SecurityContext
object by the filter for future use.
- An Authentication object with authenticated=true if Spring Security can validate the supplied user credentials
- An
AuthenticationException
if Spring Security finds that the supplied user credentials are invalid -
null
if Spring Security cannot decide whether it is true or false (confused state)
Setting Up the Authentication Manager
There are a number of built-inAuthenticationManager
methods in Spring Security that can be easily used in your application. Spring Security also has a number of helper classes, which you can set up using AuthenticationManager
. One helper class is theAuthenticationManagerBuilder
.
Using this class, it's quite easy to set up theUserDetailsService
against a database, in memory, in LDAP, and so on. If the need arises, you could also have your own custom UserDetailsService
(maybe a custom single sign-on solution is already there in your organization).
You can make anAuthenticationManager
global, so it will be accessible by your entire application. It will be available for method security and other WebSecurityConfigurerAdapter
instances.
WebSecurityConfigurerAdapter
is a class that is extended by your Spring configuration file, making it quite easy to bring Spring Security into your Spring application. This is how you set up a global AuthenticationManager
using the @Autowired
annotation:
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
public void confGlobalAuthManager(AuthenticationManagerBuilderauth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("admin").password("admin@password").roles("ROLE_ADMIN");
}
}
You can also create local the AuthenticationManager
, which is only available for this particular WebSecurityConfigurerAdapter
by overriding the configure method, as shown in the following code:
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilderauth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("admin").password("admin@password").roles("ROLE_ADMIN");
}
Another option is to expose the AuthenticationManager
bean by overriding the authenticationManagerBean
method:
@Override
public AuthenticationManagerauthenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
You can also expose various AuthenticationManager
, AuthenticationProvider
, or the UserDetailsService
as beans, which will override the default ones.
The preceding code example has used AuthenticationManagerBuilder
to configure in-memory authentication.
AuthenticationProvider
AuthenticationProvider
provides a mechanism for getting the user details with which authentication can be performed. Spring Security provides a number of AuthenticationProvider
implementations, as shown in the following diagram:
Custom AuthenticationProvider
You can also write a custom supports like the AuthenticationProvider
by implementing the AuthenticationProvider
interface. You have to implement two methods, namely, authenticate
( Authentication) and Class<
?
>aClass
:
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
if ("user".equals(username) && "password".equals(password)) {
return new UsernamePasswordAuthenticationToken(username, password, Collections.emptyList());
} else {
throw new BadCredentialsException("Authentication failed");
}
}
@Override
public boolean supports(Class<?>aClass) {
return aClass.equals(UsernamePasswordAuthenticationToken.class);
}
}
On the GitHub page, navigate to the jetty-in-memory-basic-custom-authentication project to see the full source code of this class.
Multiple AuthenticationProvider
Spring Security allows you to declare multiple AuthenticationProvider
implementations in your application. They are executed according to the order in which they are declared in the configuration.
The in jetty-in-memory-basic-custom-authentication project is modified further, and you have used the newly created CustomAuthenticationProvider
as an AuthenticationProvider
(Order 1) and the existing as your second AuthenticationProvider
(Order 2) — MemoryAuthentication
:
@EnableWebSecurity
@ComponentScan(basePackageClasses = CustomAuthenticationProvider.class)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
CustomAuthenticationProvidercustomAuthenticationProvider;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/**")
.authenticated(); // Use Basic authentication
}
@Override
protected void configure(AuthenticationManagerBuilderauth) throws Exception {
// Custom authentication provider - Order 1
auth.authenticationProvider(customAuthenticationProvider);
// Built-in authentication provider - Order 2
auth.inMemoryAuthentication()
.withUser("admin")
.password("{noop}admin@password")
//{noop} makes sure that the password encoder doesn't do anything
.roles("ADMIN") // Role of the user
.and()
.withUser("user")
.password("{noop}user@password")
.credentialsExpired(true)
.accountExpired(true)
.accountLocked(true)
.roles("USER");
}
}
Whenever the authenticate method executes without error, the controls return, and, thereafter, the configured AuthenticationProvider
doesn’t get executed.
If you found this article interesting, you can explore Hands-On Spring Security 5 for Reactive Applications to secure your Java applications by integrating the Spring Security framework in your code. Hands-On Spring Security 5 for Reactive Applications will guide you in integrating add-ons that will add value to any Spring Security module.
If you enjoyed this post, see more of Mohamed's articles here.
Published at DZone with permission of Mohamed Labouardy, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments