Learn how to build a token-based authentication service using Spring Boot and Spring Security

Madhusudhanan
Level Up Coding
Published in
7 min readMay 9, 2020

--

Hey folks, what’s up? Let’s build an authentication service using Spring Boot and Spring Security. Before that, the reason I’m writing this article is I struggled a lot in understanding the whole architecture of Spring Security when I worked on it. So, I thought I can share this with everyone. And by the way, I’m not gonna explain the whole architecture here as the title says. So, if you want to know about the architecture, I have given the official documentation link at the end of this blog. Getting onto the topic, we’ll be using JWT, which is fast and stateless, and also has its equivalent disadvantages, as auth token to authenticate our clients.

Typically, this type of authentication mechanism will be used in microservices architecture where the entire application is broken up into small and individual modules that work independently. Of course, they are deployed on the distributed system, and the common data among the services will be shared either via on-demand HTTP requests or using messaging tools like Apache Kafka. Alright, let’s get started.

First things first. We’ll create a project from start.spring.io and add the list of dependencies that are required for the project. Besides the Spring dependencies, I have also included the Java library for JWT as part of the dependencies. Just for brevity, I have included only the required dependencies. This is captured in the below image.

build.gradle

Since it’s a Spring Boot application, with just one annotation, called @EnableWebSecurity, and with a basic setup, Spring will give the default configuration for Spring Security. Although we need to enable it, we don’t wanna go with the default one. We will customize the configuration such that it supports “stateless authentication”. This will become apparent soon.

Three main components are involved in configuring Spring Security for our application. They are,

  1. A security configuration — Holds authentication and authorization-related configuration
  2. Authentication Provider — Handles the authentication part or the logic that authenticates the user. For example, fetch the user by username passed in the request. If not successful, throw the corresponding AuthenticationException
  3. Authentication Filter — Validates the token and bootstraps the authentication process

There is a root component above all of these which is called AuthenticationManager.java. This is solely responsible for doing the authentication. It identifies the right provider and calls the authenticate() method. Spring Security intercepts the HTTP request with a list of Filters in a particular order. Keep in mind that the order is important. This set of filters together is called FilterChainProxy. There is a detailed explanation regarding the Spring Security Filters in this StackOverflow link. Now let’s start implementing them one by one.

Step 1: Create a class and annotate it with @Configuration and let’s call it SecurityConfig.java

SecurityConfig.java

As you can see, we have defined a public method that will create a bean of SecurityFilterChain. We’ll get to this soon.

Step 2: Create a class that extends OncePerRequestFilter.java to create a servlet filter. Remember that this filter should intercept all the HTTP endpoints in the application except for “/authenticate” and other public endpoints, if you have any and it will be included as part of Spring Security filters in our configuration

Step 3: Before going into the filter, we also need to create two more classes.

  1. JwtAuthenticationTokenProvider.java — An implementation of AuthenticationProvider.java that processes the type of Authentication.java. This will have two methods namely authenticate() and supports().
  2. JwtAuthenticationToken.java — Implementation of Authentication.java that holds the credentials, principal, and granted authorities and maintains the authentication state. Technically, this class can extend AbstractAuthenticationToken.java which is the base class for all authentication objects, and also our class should be immutable. A sample image for both is shown below.
JwtAuthenticationToken.java
JwtAuthenticationProvider.java

The supports() method should return true if the Class parameter is of type JwtAuthenticationToken.java. Because, the AuthenticationManager will loop the list of providers, that we’ll give it in security configuration, to identify the right provider to authenticate this particular request. It is designed such that your application may support different types of authentication mechanisms like OAuth2.0 or traditional username-password-based login etc.

Once our authentication is successful, an instance of JwtAuthenticationToken class will be stored in a thread-local variable of Spring Security like below.

But what if our authentication fails? How Spring Security handles the negative scenario? Well, there’s this guy in Spring Security filters, called ExceptionTranslationFilter.java, who handles this part. It tries to continue the chain of filters and if it catches any Exception, this filter has something called AuthenticationEntrypoint.java which we configure in our security configuration. This entry point is the place for commencing our response. Here we get the current request (HttpServletRequest) and response (HttpServletResponse) objects and also the corresponding AuthenticationException object that was thrown during the authentication process. So we use them to send the response, usually with a non-successful HTTP status code based on the exception thrown.

Now getting back to our Filter class. We take the token from the header passed in the HTTP request and validate the JWT token with our JWT Java library. If it is a valid token, we bootstrap the authentication process by constructing the JwtAuthenticationToken object and putting it in SecurityContextHolder. Again, the reason we are doing this is that the authentication manager will loop the list of authentication providers and identify the right provider by calling the supports() method. Once it identifies the right provider, it will call the authenticate() method of that implementation. Only here the actual authentication happens and this time we use another constructor of JwtAuthenticationToken class that takes in the granted authorities of the authenticable user. Our filter class is shown in the below image.

JwtTokenAuthenticationFilter.java

As you can see, we use JwtParser to parse the token. I used an unhealthy secret key which is not recommended for production. This should be something very strong. Last but not the least, we need to create the security configuration with all the above classes. Let’s cook that now.

SecurityConfig.java

So there are two beans created. The first one (authenticationManager()) registers our Authentication Provider to the Authentication Manager via its builder object. The latter (filterChain()) configures the security part. This type of continuous method call is called “method chaining” in Java. It looks like a lot of calls happening inside filterChain() method, right? Let’s break it down.

  1. disable() — HTTP basic and CSRF support. Since we are dealing with our authentication system, we don’t want these. So use this method to disable those
  2. cors() — enable the CORS filter which will take care of CORS errors
  3. STATELESS session — we disable the session-based authentication that creates JSESSIONID to maintain the authentication state
  4. addFilter() — As I said earlier, there is a set of Spring Security filters and one of them is UsernamePasswordAuthenticationFilter.java. We place our filter before this filter, because from the set of filters, at this filter only the authentication process starts. So we need to place it right before it as we update the SecurityContextHolder thread-local object.
  5. authorizeRequests() — restrict the access of resources by matching the request patterns. Here just for the sake of the tutorial, I have mentioned only one URI pattern. permitAll() method will permit the path that matches the given pattern to be able to access it publicly. In a typical production application, there can be numerous URI patterns to permit or forbid users. For example, if there is an API that registers a new user which logically does not require authentication, then you can allow it by doing like .antMatchers(“**/register”).permitAll()
  6. .anyRequest().authenticated() — the rest of all the APIs that are available in this application should be protected. Meaning that only a user with valid authentication can access those APIs

So, I guess that’s it. It’s a very basic configuration. You can create more complex configurations like ROLE-based authentication where you can allow some of the endpoints to be accessible only for those having the role ADMIN. There is good documentation from which you can refer to build a custom authentication server application. You can learn more about Spring Security here.

Thanks and have a good day!

--

--