Spring Custom Token Authentication Example

Learn to add custom token-based authentication to REST APIs using created with Spring REST and Spring security 5. In the given example, a request with the header name “AUTH_API_KEY” with a predefined value will pass through. All other requests will return HTTP 403 response.

1. Spring security dependencies

Include the following dependencies to work with spring security classes and interfaces.

<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-core</artifactId>
  <version>5.1.5.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-config</artifactId>
  <version>5.1.5.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-web</artifactId>
  <version>5.1.5.RELEASE</version>
</dependency>

2. Extend AbstractPreAuthenticatedProcessingFilter

Create a class and extend AbstractPreAuthenticatedProcessingFilter. It is a base class for processing filters that handle pre-authenticated authentication requests, where it is assumed that the principal has already been authenticated by an external system.

By default, the filter chain will proceed when an authentication attempt fails in order to allow other authentication mechanisms to process the request. It helps pass the request to other security filters (e.g. form login) if the token is found invalid.

It’s getPreAuthenticatedPrincipal() method helps to read the auth header value from the current request.

import javax.servlet.http.HttpServletRequest;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
 
public class PreAuthTokenHeaderFilter extends AbstractPreAuthenticatedProcessingFilter {
 
  private String authHeaderName;
 
  public PreAuthTokenHeaderFilter(String authHeaderName) {
    this.authHeaderName = authHeaderName;
  }
 
  @Override
  protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
    return request.getHeader(authHeaderName);
  }
 
  @Override
  protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
    return "N/A";
  }
}

It is an optional approach. An application may decide to return auth failed error immediately as well.

3. Configure AuthenticationManager with HttpSecurity

We need to set the authentication manager which will handle the auth process and decide how to process the success and failure scenarios.

After adding the authentication manager, we can add PreAuthTokenHeaderFilter to HttpSecurity.

If any auth error is raised, it will be handled by default ExceptionTranslationFilter which forwards it to the default auth error page in spring. If you want to show the auth error response differently, you need to create a custom ExceptionTranslationFilter class.

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
 
@Configuration
@EnableWebSecurity
@PropertySource("classpath:application.properties")
@Order(1)
public class AuthTokenSecurityConfig extends WebSecurityConfigurerAdapter {
 
    @Value("${howtodoinjava.http.auth.tokenName}")
    private String authHeaderName;
 
    //TODO: retrieve this token value from data source
    @Value("${howtodoinjava.http.auth.tokenValue}")
    private String authHeaderValue;
 
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception 
    {
      PreAuthTokenHeaderFilter filter = new PreAuthTokenHeaderFilter(authHeaderName);
         
        filter.setAuthenticationManager(new AuthenticationManager() 
        {
            @Override
            public Authentication authenticate(Authentication authentication) 
                              throws AuthenticationException 
            {
                String principal = (String) authentication.getPrincipal();
                 
                if (!authHeaderValue.equals(principal))
                {
                    throw new BadCredentialsException("The API key was not found "
                                  + "or not the expected value.");
                }
                authentication.setAuthenticated(true);
                return authentication;
            }
        });
         
        httpSecurity.
            antMatcher("/api/**")
            .csrf()
              .disable()
            .sessionManagement()
              .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
              .addFilter(filter)
              .addFilterBefore(new ExceptionTranslationFilter(
                    new Http403ForbiddenEntryPoint()), 
                  filter.getClass()
                )
              .authorizeRequests()
                .anyRequest()
                .authenticated();
    }
 
}

4. Register Spring Security Filter

Traditionally, spring security had starting point in web.xml file in XML based configuration with DelegatingFilterProxy.

<!-- Spring Security -->
<filter>
  <filter-name>springSecurityFilterChain</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy
  </filter-class>
</filter>
 
<filter-mapping>
  <filter-name>springSecurityFilterChain</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

In Java config, we can achieve the same effect by exteding the class AbstractSecurityWebApplicationInitializer.

import org.springframework.security.web.context
      .AbstractSecurityWebApplicationInitializer;
public class SpringSecurityInitializer 
      extends AbstractSecurityWebApplicationInitializer {
    //no code needed
}

4. Spring REST Custom Token Authentication Demo

4.1. Without auth token in the header

HTTP GET http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/
HTTP Status - 403 – Forbidden
Type Status - Report
Message Access - Denied
Description - The server understood the request but refuses to authorize it.

4.2. Incorrect auth token in header

HTTP GET http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/
 
AUTH_API_KEY: xyz123
HTTP Status - 403 – Forbidden
Type Status - Report
Message Access - Denied
Description - The server understood the request but refuses to authorize it.

4.2. Valid auth token in header

HTTP GET http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/
 
AUTH_API_KEY: abcd123456
HTTP Status - 200 OK
 
{
  //response body
}

Happy Learning !!

Sourcecode Download

Comments

Subscribe
Notify of
guest
4 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode