diff --git a/spring-security-modules/pom.xml b/spring-security-modules/pom.xml
index 223f0894d5..030f1ef876 100644
--- a/spring-security-modules/pom.xml
+++ b/spring-security-modules/pom.xml
@@ -33,6 +33,7 @@
spring-security-web-boot-2
spring-security-web-boot-3
spring-security-web-boot-4
+ spring-security-web-boot-5
spring-security-web-digest-auth
spring-security-web-login
spring-security-web-login-2
diff --git a/spring-security-modules/spring-security-web-boot-5/pom.xml b/spring-security-modules/spring-security-web-boot-5/pom.xml
new file mode 100644
index 0000000000..f4e2e3ad92
--- /dev/null
+++ b/spring-security-modules/spring-security-web-boot-5/pom.xml
@@ -0,0 +1,51 @@
+
+
+ 4.0.0
+ spring-security-web-boot-5
+ 0.0.1-SNAPSHOT
+ spring-security-web-boot-5
+ jar
+ Spring Security Custom Auth Application
+
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-2
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-web-boot-5/src/main/java/com/baeldung/customauth/SpringSecurityApplication.java b/spring-security-modules/spring-security-web-boot-5/src/main/java/com/baeldung/customauth/SpringSecurityApplication.java
new file mode 100644
index 0000000000..500b8fbef8
--- /dev/null
+++ b/spring-security-modules/spring-security-web-boot-5/src/main/java/com/baeldung/customauth/SpringSecurityApplication.java
@@ -0,0 +1,12 @@
+package com.baeldung.customauth;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SpringSecurityApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringSecurityApplication.class, args);
+ }
+}
diff --git a/spring-security-modules/spring-security-web-boot-5/src/main/java/com/baeldung/customauth/authprovider/RequestHeaderAuthenticationProvider.java b/spring-security-modules/spring-security-web-boot-5/src/main/java/com/baeldung/customauth/authprovider/RequestHeaderAuthenticationProvider.java
new file mode 100644
index 0000000000..e9741f7a7a
--- /dev/null
+++ b/spring-security-modules/spring-security-web-boot-5/src/main/java/com/baeldung/customauth/authprovider/RequestHeaderAuthenticationProvider.java
@@ -0,0 +1,36 @@
+package com.baeldung.customauth.authprovider;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+
+@Service
+public class RequestHeaderAuthenticationProvider implements AuthenticationProvider {
+
+ @Value("${api.auth.secret}")
+ private String apiAuthSecret;
+
+
+ @Override
+ public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+ String authSecretKey = String.valueOf(authentication.getPrincipal());
+
+ if(StringUtils.isBlank(authSecretKey) || !authSecretKey.equals(apiAuthSecret)) {
+ throw new BadCredentialsException("Bad Request Header Credentials");
+ }
+
+ return new PreAuthenticatedAuthenticationToken(authentication.getPrincipal(), null, new ArrayList<>());
+ }
+
+ @Override
+ public boolean supports(Class> authentication) {
+ return authentication.equals(PreAuthenticatedAuthenticationToken.class);
+ }
+}
diff --git a/spring-security-modules/spring-security-web-boot-5/src/main/java/com/baeldung/customauth/configuration/SecurityConfig.java b/spring-security-modules/spring-security-web-boot-5/src/main/java/com/baeldung/customauth/configuration/SecurityConfig.java
new file mode 100644
index 0000000000..53ab890792
--- /dev/null
+++ b/spring-security-modules/spring-security-web-boot-5/src/main/java/com/baeldung/customauth/configuration/SecurityConfig.java
@@ -0,0 +1,68 @@
+package com.baeldung.customauth.configuration;
+
+import com.baeldung.customauth.authprovider.RequestHeaderAuthenticationProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.ProviderManager;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter;
+import org.springframework.security.web.header.HeaderWriterFilter;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.Collections;
+
+@Configuration
+@EnableWebSecurity
+public class SecurityConfig {
+
+ private final RequestHeaderAuthenticationProvider requestHeaderAuthenticationProvider;
+
+ @Value("${api.auth.header.name}")
+ private String apiAuthHeaderName;
+
+ @Autowired
+ public SecurityConfig( RequestHeaderAuthenticationProvider requestHeaderAuthenticationProvider){
+ this.requestHeaderAuthenticationProvider = requestHeaderAuthenticationProvider;
+ }
+
+ @Bean
+ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+ http.cors().and().csrf()
+ .disable()
+ .sessionManagement()
+ .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+ .and()
+ .addFilterAfter(requestHeaderAuthenticationFilter(), HeaderWriterFilter.class)
+ .authorizeHttpRequests()
+ .antMatchers(HttpMethod.GET,"/health").permitAll()
+ .antMatchers("/api/**").authenticated().and()
+ .exceptionHandling().authenticationEntryPoint((request, response, authException) ->
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED));
+
+ return http.build();
+ }
+
+ @Bean
+ public RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter() {
+ RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();
+ filter.setPrincipalRequestHeader(apiAuthHeaderName);
+ filter.setExceptionIfHeaderMissing(false);
+ filter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/api/**"));
+ filter.setAuthenticationManager(authenticationManager());
+
+ return filter;
+ }
+
+ @Bean
+ protected AuthenticationManager authenticationManager() {
+ return new ProviderManager(Collections.singletonList(requestHeaderAuthenticationProvider));
+ }
+}
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-web-boot-5/src/main/java/com/baeldung/customauth/controller/ApiController.java b/spring-security-modules/spring-security-web-boot-5/src/main/java/com/baeldung/customauth/controller/ApiController.java
new file mode 100644
index 0000000000..89a66b6a0e
--- /dev/null
+++ b/spring-security-modules/spring-security-web-boot-5/src/main/java/com/baeldung/customauth/controller/ApiController.java
@@ -0,0 +1,13 @@
+package com.baeldung.customauth.controller;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class ApiController {
+
+ @GetMapping(path = "/api/hello")
+ public String hello(){
+ return "hello";
+ }
+}
diff --git a/spring-security-modules/spring-security-web-boot-5/src/main/java/com/baeldung/customauth/controller/HealthCheckController.java b/spring-security-modules/spring-security-web-boot-5/src/main/java/com/baeldung/customauth/controller/HealthCheckController.java
new file mode 100644
index 0000000000..38d96b9b3d
--- /dev/null
+++ b/spring-security-modules/spring-security-web-boot-5/src/main/java/com/baeldung/customauth/controller/HealthCheckController.java
@@ -0,0 +1,13 @@
+package com.baeldung.customauth.controller;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class HealthCheckController {
+
+ @GetMapping(path = "/health")
+ public String getHealthStatus(){
+ return "OK";
+ }
+}
diff --git a/spring-security-modules/spring-security-web-boot-5/src/main/resources/application.properties b/spring-security-modules/spring-security-web-boot-5/src/main/resources/application.properties
new file mode 100644
index 0000000000..3a431720c2
--- /dev/null
+++ b/spring-security-modules/spring-security-web-boot-5/src/main/resources/application.properties
@@ -0,0 +1,5 @@
+spring.application.name=spring-security-app
+server.servlet.context-path=/app
+server.port=8085
+api.auth.header.name=x-auth-secret-key
+logging.level.org.springframework.security=DEBUG
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-web-boot-5/src/test/java/com/baeldung/customauth/SpringContextTest.java b/spring-security-modules/spring-security-web-boot-5/src/test/java/com/baeldung/customauth/SpringContextTest.java
new file mode 100644
index 0000000000..a63d9d2bce
--- /dev/null
+++ b/spring-security-modules/spring-security-web-boot-5/src/test/java/com/baeldung/customauth/SpringContextTest.java
@@ -0,0 +1,16 @@
+package com.baeldung.customauth;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+
+@ExtendWith(SpringExtension.class)
+@SpringBootTest(classes = SpringSecurityApplication.class)
+class SpringContextTest {
+
+ @Test
+ void whenSpringContextIsBootstrapped_thenNoExceptions() {
+ }
+}
diff --git a/spring-security-modules/spring-security-web-boot-5/src/test/java/com/baeldung/customauth/controller/ApiControllerIntegrationTest.java b/spring-security-modules/spring-security-web-boot-5/src/test/java/com/baeldung/customauth/controller/ApiControllerIntegrationTest.java
new file mode 100644
index 0000000000..f04dcc30d4
--- /dev/null
+++ b/spring-security-modules/spring-security-web-boot-5/src/test/java/com/baeldung/customauth/controller/ApiControllerIntegrationTest.java
@@ -0,0 +1,68 @@
+package com.baeldung.customauth.controller;
+
+import com.baeldung.customauth.SpringSecurityApplication;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.http.*;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.net.URI;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+
+@SpringBootTest(classes = SpringSecurityApplication.class,
+ webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
+@ExtendWith(SpringExtension.class)
+class ApiControllerIntegrationTest {
+
+ private final TestRestTemplate restTemplate = new TestRestTemplate();
+
+ private static final String API_ENDPOINT = "http://localhost:8080/app/api/hello";
+
+ @Test
+ void givenAuthHeaderSecretIsValid_whenApiControllerCalled_thenReturnOk() throws Exception {
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("x-auth-secret-key", "test-secret");
+
+ ResponseEntity response = restTemplate.exchange(new URI(API_ENDPOINT), HttpMethod.GET,
+ new HttpEntity<>(headers), String.class);
+
+ assertEquals(HttpStatus.OK, response.getStatusCode());
+ assertEquals("hello", response.getBody());
+ }
+
+ @Test
+ void givenAAuthHeaderIsInvalid_whenApiControllerCalled_thenReturnUnAuthorised() throws Exception {
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("x-auth-secret-key", "invalid");
+
+ ResponseEntity response = restTemplate.exchange(new URI(API_ENDPOINT), HttpMethod.GET,
+ new HttpEntity<>(headers), String.class);
+
+ assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
+ }
+
+ @Test
+ void givenAuthHeaderNameIsInValid_whenApiControllerCalled_thenReturnUnAuthorised() throws Exception {
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("x-auth-secret", "test-secret");
+
+ ResponseEntity response = restTemplate.exchange(new URI(API_ENDPOINT), HttpMethod.GET,
+ new HttpEntity<>(headers), String.class);
+
+ assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
+ }
+
+ @Test
+ void givenAuthHeaderIsMissing_whenApiControllerCalled_thenReturnUnAuthorised() throws Exception {
+ HttpHeaders headers = new HttpHeaders();
+
+ ResponseEntity response = restTemplate.exchange(new URI(API_ENDPOINT), HttpMethod.GET,
+ new HttpEntity<>(headers), String.class);
+
+ assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
+ }
+}
diff --git a/spring-security-modules/spring-security-web-boot-5/src/test/java/com/baeldung/customauth/controller/HealthCheckControllerIntegrationTest.java b/spring-security-modules/spring-security-web-boot-5/src/test/java/com/baeldung/customauth/controller/HealthCheckControllerIntegrationTest.java
new file mode 100644
index 0000000000..ea6fdcf292
--- /dev/null
+++ b/spring-security-modules/spring-security-web-boot-5/src/test/java/com/baeldung/customauth/controller/HealthCheckControllerIntegrationTest.java
@@ -0,0 +1,34 @@
+package com.baeldung.customauth.controller;
+
+import com.baeldung.customauth.SpringSecurityApplication;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.http.*;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.net.URI;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@SpringBootTest(classes = SpringSecurityApplication.class,
+ webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
+@ExtendWith(SpringExtension.class)
+class HealthCheckControllerIntegrationTest {
+
+ private final TestRestTemplate restTemplate = new TestRestTemplate();
+
+ private static final String HEALTH_CHECK_ENDPOINT = "http://localhost:8080/app/health";
+
+ @Test
+ void givenApplicationIsRunning_whenHealthCheckControllerCalled_thenReturnOk() throws Exception {
+ HttpHeaders headers = new HttpHeaders();
+
+ ResponseEntity response = restTemplate.exchange(new URI(HEALTH_CHECK_ENDPOINT), HttpMethod.GET,
+ new HttpEntity<>(headers), String.class);
+
+ assertEquals(HttpStatus.OK, response.getStatusCode());
+ assertEquals("OK", response.getBody());
+ }
+}
diff --git a/spring-security-modules/spring-security-web-boot-5/src/test/resources/application.properties b/spring-security-modules/spring-security-web-boot-5/src/test/resources/application.properties
new file mode 100644
index 0000000000..27e9cad28a
--- /dev/null
+++ b/spring-security-modules/spring-security-web-boot-5/src/test/resources/application.properties
@@ -0,0 +1,5 @@
+spring.application.name=spring-security-app
+server.servlet.context-path=/app
+server.port=8080
+api.auth.header.name=x-auth-secret-key
+api.auth.secret=test-secret