[JAVA] Create API key authentication for Web API in Spring Security

Thing you want to do

Authenticate with the API key in the header for each request when calling Web API with Spring Security.

An example in Java is posted in a personal blog post [Spring] Authorizing Web API using Spring Security. This article also includes an example of permission checking by @PreAuthorize.

Realization method

  1. Make settings to perform authentication for each request without using a session
  2. Create filters and services to perform authentication processing by API key in request header
  3. Register the filter and service created in 2.

1. Setting to perform authentication for each request

Set the session policy to stateless with the configure method of WebSecurityConfigurerAdapter. Specifically, set SessionCreationPolicy.STATELESS as follows.

@Configuration
@EnableWebSecurity
class SecurityConfig: WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity?) {
        http
                ?.authorizeRequests()
                ?.antMatchers("/hello/**")?.hasAnyRole("USER", "ADMIN")
                ?.antMatchers("/admin/**")?.hasRole("ADMIN")
                ?.anyRequest()
                ?.authenticated()
                ?.and()
                ?.addFilter(preAuthenticatedProcessingFilter())
                ?.exceptionHandling()
                ?.authenticationEntryPoint(http401UnauthorizedEntryPoint())

        //Authenticate for each request
        http?.sessionManagement()?.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    }
    ....

2. Create authentication process by API key in request header

Use Spring Security's Pre-Authentication (https://docs.spring.io/spring-security/site/docs/4.2.x-SNAPSHOT/reference/html/preauth.html). Create a filter that uses AbstractPreAuthenticatedProcessingFilter and AuthenticationUserDetailsService to get the API key from the request header and a service that authenticates with the API key.

Creating a filter

Create a class that inherits AbstractPreAuthenticatedProcessingFilter and create a process to extract API key in request header in getPreAuthenticatedCredentials method.

 class MyPreAuthenticatedProcessingFilter: AbstractPreAuthenticatedProcessingFilter() {

    //Get API key
    override fun getPreAuthenticatedCredentials(request: HttpServletRequest?): Any {
        return request?.getHeader("X-Auth-Token") ?: ""
    }

    override fun getPreAuthenticatedPrincipal(request: HttpServletRequest?): Any {
        return ""
    }
}

Creating a service

Create a class that inherits AuthenticationUserDetailsService as shown below, and create an authentication process using the acquired API key. Specify PreAuthenticatedAuthenticationToken for the type parameter. Here, the appropriate process is written for testing, but the process to verify the API key using an external API or DB is written.

class MyAuthenticationUserDetailService: AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {

    override fun loadUserDetails(token: PreAuthenticatedAuthenticationToken?): UserDetails {
    
        //API key obtained by MyPreAuthenticatedProcessingFilter
        val credential = token?.credentials
        
        if (credential.toString() == "") {
            throw UsernameNotFoundException("User not found")
        }

        return when (credential) {
            "token1" -> User("user", "", AuthorityUtils.createAuthorityList("ROLE_USER"))
            "token2" -> User("admin", "", AuthorityUtils.createAuthorityList("ROLE_ADMIN"))
            else -> User("user", "", AuthorityUtils.createAuthorityList("ROLE_INVALID") )
        }
    }
}

3. Register filters and services

Add Bean definition as follows so that the created filter and service will be DI.

@Configuration
@EnableWebSecurity
class SecurityConfig: WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity?) {
        http
                ?.authorizeRequests()
                ?.antMatchers("/hello/**")?.hasAnyRole("USER", "ADMIN")
                ?.antMatchers("/admin/**")?.hasRole("ADMIN")
                ?.anyRequest()
                ?.authenticated()
                ?.and()
                ?.addFilter(preAuthenticatedProcessingFilter())

        http?.sessionManagement()?.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    }

    //service
    @Bean
    fun authenticationUserDetailsService(): AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {
        return MyAuthenticationUserDetailService()
    }

    //Filter registration
    @Bean
    fun preAuthenticatedAuthenticationProvider(): PreAuthenticatedAuthenticationProvider {
        return PreAuthenticatedAuthenticationProvider().apply {
            setPreAuthenticatedUserDetailsService(authenticationUserDetailsService())
            setUserDetailsChecker(AccountStatusUserDetailsChecker())
        }
    }

    //filter
    @Bean
    fun preAuthenticatedProcessingFilter(): AbstractPreAuthenticatedProcessingFilter {
        return MyPreAuthenticatedProcessingFilter().apply {
            setAuthenticationManager(authenticationManager())
        }
    }
}

Recommended Posts

Create API key authentication for Web API in Spring Security
Create a web api server with spring boot
Security in web applications
Java: Timed token management class for Web API authentication
Create API to send and receive Json data in Spring
Spring Security usage memo Authentication / authorization
Implemented authentication function with Spring Security ②
Spring Boot Tutorial Using Spring Security Authentication
Implemented authentication function with Spring Security ①
Learn Spring Security authentication processing architecture
Implement REST API in Spring Boot
Set HTTP headers (X-Frame-Options) only for specific URLs in Spring Security
Settings when calling API using CSRF measures of Spring Security in JMeter
Create Java Spring Boot project in IntelliJ
Achieve BASIC authentication with Spring Boot + Spring Security
# 6 show, create implementation to build bulletin board API with authentication authorization in Rails 6
Create a Docker container for your development web server in Ansible on MacOS
Implementation sample when Form authentication and Request-Header authentication are used together in Spring Security
Create login / logout function with Spring Security according to Spring official guide [For beginners]
Nuxt.js × Create an application in Rails API mode
SameSite cookie in Spring Boot (Spring Web MVC + Tomcat)
Create authentication function in Rails application using devise
Try LDAP authentication with Spring Security (Spring Boot) + OpenLDAP
Create a tool for name identification in Salesforce
Add your own authentication items with Spring Security
Spring --Error / Error avoidance when creating API for POST
[Introduction to Spring Boot] Authentication function with Spring Security
Create Spring Cloud Config Server with security with Spring Boot 2.0
Create a web app that is just right for learning [Spring Boot + Thymeleaf + PostgreSQL]