2.0.0.RELEASE
8
diff --git a/spring-security-modules/pom.xml b/spring-security-modules/pom.xml
index 9fdfde282d..eb643c78c7 100644
--- a/spring-security-modules/pom.xml
+++ b/spring-security-modules/pom.xml
@@ -22,6 +22,7 @@
spring-security-acl
spring-security-auth0
spring-security-core
+ spring-security-core-2
spring-security-ldap
spring-security-legacy-oidc
spring-security-oauth2-sso
@@ -46,6 +47,7 @@
spring-security-web-x509
spring-session
spring-social-login
+ spring-security-opa
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-core-2/.gitignore b/spring-security-modules/spring-security-core-2/.gitignore
new file mode 100644
index 0000000000..9f970225ad
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/.gitignore
@@ -0,0 +1 @@
+target/
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-core-2/README.md b/spring-security-modules/spring-security-core-2/README.md
new file mode 100644
index 0000000000..9ce12af8ef
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/README.md
@@ -0,0 +1,10 @@
+## Spring Security Core
+
+This module contains articles about core Spring Security
+
+### Relevant Articles:
+- [Handle Spring Security Exceptions](https://www.baeldung.com/spring-security-exceptions)
+
+### Build the Project
+
+`mvn clean install`
diff --git a/spring-security-modules/spring-security-core-2/pom.xml b/spring-security-modules/spring-security-core-2/pom.xml
new file mode 100644
index 0000000000..cf150bfd42
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/pom.xml
@@ -0,0 +1,103 @@
+
+
+ 4.0.0
+ spring-security-core-2
+ 0.1-SNAPSHOT
+ spring-security-core-2
+ war
+
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-2
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-devtools
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ com.h2database
+ h2
+ runtime
+
+
+ org.springframework.security
+ spring-security-test
+
+
+
+
+ spring-security-core
+
+
+ src/main/resources
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+
+
+
+
+
+
+ live
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ integration-test
+
+ test
+
+
+
+ none
+
+
+ **/*LiveTest.java
+
+
+ cargo
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/AppInitializer.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/AppInitializer.java
new file mode 100644
index 0000000000..43921f71a2
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/AppInitializer.java
@@ -0,0 +1,11 @@
+package com.baeldung.exceptionhandler;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class AppInitializer {
+ public static void main(String[] args) {
+ SpringApplication.run(AppInitializer.class, args);
+ }
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/controller/AccessDeniedController.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/controller/AccessDeniedController.java
new file mode 100644
index 0000000000..0973fe1ad3
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/controller/AccessDeniedController.java
@@ -0,0 +1,15 @@
+package com.baeldung.exceptionhandler.controller;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+@Controller
+@RequestMapping("/access-denied")
+public class AccessDeniedController {
+
+ @GetMapping
+ public String accessDenied() {
+ return "/denied.html";
+ }
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/controller/CustomErrorController.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/controller/CustomErrorController.java
new file mode 100644
index 0000000000..8b65a623eb
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/controller/CustomErrorController.java
@@ -0,0 +1,15 @@
+package com.baeldung.exceptionhandler.controller;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+@Controller
+@RequestMapping
+public class CustomErrorController {
+
+ @GetMapping("/customError")
+ public String customErrorController() {
+ return "/error";
+ }
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/controller/HomeController.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/controller/HomeController.java
new file mode 100644
index 0000000000..d64bf7b5f3
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/controller/HomeController.java
@@ -0,0 +1,15 @@
+package com.baeldung.exceptionhandler.controller;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+@Controller
+@RequestMapping("/home")
+public class HomeController {
+
+ @GetMapping
+ public String home() {
+ return "/index.html";
+ }
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/controller/SecuredResourceController.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/controller/SecuredResourceController.java
new file mode 100644
index 0000000000..a057570e29
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/controller/SecuredResourceController.java
@@ -0,0 +1,15 @@
+package com.baeldung.exceptionhandler.controller;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+@Controller
+@RequestMapping("/secured")
+public class SecuredResourceController {
+
+ @GetMapping
+ public String secureResource() {
+ return "/admin.html";
+ }
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/security/CustomAccessDeniedHandler.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/security/CustomAccessDeniedHandler.java
new file mode 100644
index 0000000000..a3d6aca9be
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/security/CustomAccessDeniedHandler.java
@@ -0,0 +1,17 @@
+package com.baeldung.exceptionhandler.security;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.web.access.AccessDeniedHandler;
+
+public class CustomAccessDeniedHandler implements AccessDeniedHandler {
+
+ @Override
+ public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exc) throws IOException {
+ response.sendRedirect("/access-denied");
+ }
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/security/CustomAuthenticationFailureHandler.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/security/CustomAuthenticationFailureHandler.java
new file mode 100644
index 0000000000..281f9d5289
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/security/CustomAuthenticationFailureHandler.java
@@ -0,0 +1,17 @@
+package com.baeldung.exceptionhandler.security;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.AuthenticationFailureHandler;
+
+public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
+
+ @Override
+ public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException {
+ httpServletResponse.sendRedirect("/customError");
+ }
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/security/CustomAuthenticationSuccessHandler.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/security/CustomAuthenticationSuccessHandler.java
new file mode 100644
index 0000000000..62cbdf8873
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/security/CustomAuthenticationSuccessHandler.java
@@ -0,0 +1,31 @@
+package com.baeldung.exceptionhandler.security;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+
+public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
+
+ @Override
+ public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
+
+ HttpSession session = httpServletRequest.getSession();
+ User authUser = (User) SecurityContextHolder.getContext()
+ .getAuthentication()
+ .getPrincipal();
+ session.setAttribute("username", authUser.getUsername());
+ session.setAttribute("authorities", authentication.getAuthorities());
+
+ httpServletResponse.setStatus(HttpServletResponse.SC_OK);
+
+ httpServletResponse.sendRedirect("/home");
+ }
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/security/SecurityConfig.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/security/SecurityConfig.java
new file mode 100644
index 0000000000..71ded0f131
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/exceptionhandler/security/SecurityConfig.java
@@ -0,0 +1,98 @@
+package com.baeldung.exceptionhandler.security;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+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.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.crypto.factory.PasswordEncoderFactories;
+import org.springframework.security.provisioning.InMemoryUserDetailsManager;
+import org.springframework.security.web.access.AccessDeniedHandler;
+import org.springframework.security.web.authentication.AuthenticationFailureHandler;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+
+@EnableWebSecurity
+public class SecurityConfig extends WebSecurityConfigurerAdapter {
+
+ @Bean
+ public UserDetailsService userDetailsService() {
+
+ UserDetails user = User.withUsername("user")
+ .passwordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder()::encode)
+ .password("password")
+ .roles("USER")
+ .build();
+
+ UserDetails admin = User.withUsername("admin")
+ .passwordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder()::encode)
+ .password("password")
+ .roles("ADMIN")
+ .build();
+
+ InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager();
+
+ userDetailsManager.createUser(user);
+ userDetailsManager.createUser(admin);
+
+ return userDetailsManager;
+ }
+
+ @Override
+ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+ auth.inMemoryAuthentication()
+ .withUser("user")
+ .password("{noop}password")
+ .roles("USER")
+ .and()
+ .withUser("admin")
+ .password("{noop}password")
+ .roles("ADMIN");
+ }
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http.csrf()
+ .disable()
+ .httpBasic()
+ .disable()
+ .authorizeRequests()
+ .antMatchers("/login")
+ .permitAll()
+ .antMatchers("/customError")
+ .permitAll()
+ .antMatchers("/access-denied")
+ .permitAll()
+ .antMatchers("/secured")
+ .hasRole("ADMIN")
+ .anyRequest()
+ .authenticated()
+ .and()
+ .formLogin()
+ .failureHandler(authenticationFailureHandler())
+ .successHandler(authenticationSuccessHandler())
+ .and()
+ .exceptionHandling()
+ .accessDeniedHandler(accessDeniedHandler())
+ .and()
+ .logout();
+ }
+
+ @Bean
+ public AuthenticationFailureHandler authenticationFailureHandler() {
+ return new CustomAuthenticationFailureHandler();
+ }
+
+ @Bean
+ public AuthenticationSuccessHandler authenticationSuccessHandler() {
+ return new CustomAuthenticationSuccessHandler();
+ }
+
+ @Bean
+ public AccessDeniedHandler accessDeniedHandler() {
+ return new CustomAccessDeniedHandler();
+ }
+
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/resources/application.properties b/spring-security-modules/spring-security-core-2/src/main/resources/application.properties
new file mode 100644
index 0000000000..9d154c9cc0
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/resources/application.properties
@@ -0,0 +1 @@
+spring.thymeleaf.prefix=classpath:/templates/
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-core-2/src/main/resources/templates/admin.html b/spring-security-modules/spring-security-core-2/src/main/resources/templates/admin.html
new file mode 100644
index 0000000000..d7f7ec232a
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/resources/templates/admin.html
@@ -0,0 +1,5 @@
+
+
+ Hello Admin!
+
+
diff --git a/spring-security-modules/spring-security-core-2/src/main/resources/templates/denied.html b/spring-security-modules/spring-security-core-2/src/main/resources/templates/denied.html
new file mode 100644
index 0000000000..b7a8e09309
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/resources/templates/denied.html
@@ -0,0 +1,5 @@
+
+
+ You need permission to perform this action.
+
+
diff --git a/spring-security-modules/spring-security-core-2/src/main/resources/templates/error.html b/spring-security-modules/spring-security-core-2/src/main/resources/templates/error.html
new file mode 100644
index 0000000000..ce74f05d99
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/resources/templates/error.html
@@ -0,0 +1,5 @@
+
+
+ Ups! Wrong credentials
+
+
diff --git a/spring-security-modules/spring-security-core-2/src/main/resources/templates/index.html b/spring-security-modules/spring-security-core-2/src/main/resources/templates/index.html
new file mode 100644
index 0000000000..34d070b37a
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/resources/templates/index.html
@@ -0,0 +1,6 @@
+
+
+ Hello User!
+
+
+
diff --git a/spring-security-modules/spring-security-core-2/src/test/java/com/baeldung/exceptionhandler/SecurityConfigUnitTest.java b/spring-security-modules/spring-security-core-2/src/test/java/com/baeldung/exceptionhandler/SecurityConfigUnitTest.java
new file mode 100644
index 0000000000..ad35d575a5
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/test/java/com/baeldung/exceptionhandler/SecurityConfigUnitTest.java
@@ -0,0 +1,50 @@
+package com.baeldung.exceptionhandler;
+
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+
+import com.baeldung.exceptionhandler.security.SecurityConfig;
+
+@RunWith(SpringRunner.class)
+@WebMvcTest(SecurityConfig.class)
+class SecurityConfigUnitTest {
+ @Autowired
+ private MockMvc mvc;
+
+ @Test
+ void whenUserAccessLogin_shouldSucceed() throws Exception {
+ mvc.perform(get("/login"))
+ .andExpect(status().isOk());
+ }
+
+ @Test
+ void whenUserAccessWithWrongCredentials_shouldRedirectToCustomErrorPage() throws Exception {
+ mvc.perform(formLogin("/login").user("username", "wrong")
+ .password("password", "credentials"))
+ .andExpect(redirectedUrl("/customError"));
+ }
+
+ @Test
+ void whenUserAccessWithCorrectCredentials_shouldRedirectToHome() throws Exception {
+ mvc.perform(formLogin("/login").user("username", "user")
+ .password("password", "password"))
+ .andExpect(redirectedUrl("/home"));
+ }
+
+ @Test
+ @WithMockUser(username = "user", roles = { "USER" })
+ void whenUserAccessToSecuredPageWithoutUserRole_shouldRedirectToDeniedPage() throws Exception {
+ mvc.perform(get("/secured"))
+ .andExpect(redirectedUrl("/access-denied"));
+ }
+}
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-core/README.md b/spring-security-modules/spring-security-core/README.md
index 9f8e4dda53..1949b5a929 100644
--- a/spring-security-modules/spring-security-core/README.md
+++ b/spring-security-modules/spring-security-core/README.md
@@ -10,6 +10,7 @@ This module contains articles about core Spring Security
- [Deny Access on Missing @PreAuthorize to Spring Controller Methods](https://www.baeldung.com/spring-deny-access)
- [Spring Security: Check If a User Has a Role in Java](https://www.baeldung.com/spring-security-check-user-role)
- [Filtering Jackson JSON Output Based on Spring Security Role](https://www.baeldung.com/spring-security-role-filter-json)
+- [Handle Spring Security Exceptions](https://www.baeldung.com/spring-security-exceptions)
### Build the Project
diff --git a/spring-security-modules/spring-security-opa/README.md b/spring-security-modules/spring-security-opa/README.md
new file mode 100644
index 0000000000..d2c1652edb
--- /dev/null
+++ b/spring-security-modules/spring-security-opa/README.md
@@ -0,0 +1,4 @@
+
+### Relevant Articles:
+
+- [Spring Security Authorization with OPA](https://www.baeldung.com/spring-security-authorization-opa)
diff --git a/spring-security-modules/spring-security-opa/pom.xml b/spring-security-modules/spring-security-opa/pom.xml
new file mode 100644
index 0000000000..72b0574253
--- /dev/null
+++ b/spring-security-modules/spring-security-opa/pom.xml
@@ -0,0 +1,48 @@
+
+ 4.0.0
+ spring-security-opa
+ Spring Security with OPA authorization
+
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-2
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.projectlombok
+ lombok
+
+
+ com.google.guava
+ guava
+ 31.0.1-jre
+
+
+ org.springframework.boot
+ spring-boot-devtools
+
+
+ org.springframework.security
+ spring-security-test
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/Application.java b/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/Application.java
new file mode 100644
index 0000000000..789a1f803f
--- /dev/null
+++ b/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/Application.java
@@ -0,0 +1,13 @@
+package com.baeldung.security.opa;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+}
diff --git a/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/config/OpaConfiguration.java b/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/config/OpaConfiguration.java
new file mode 100644
index 0000000000..e24fdbcf35
--- /dev/null
+++ b/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/config/OpaConfiguration.java
@@ -0,0 +1,25 @@
+package com.baeldung.security.opa.config;
+
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import lombok.RequiredArgsConstructor;
+
+@Configuration
+@RequiredArgsConstructor
+@EnableConfigurationProperties(OpaProperties.class)
+public class OpaConfiguration {
+
+ private final OpaProperties opaProperties;
+
+ @Bean
+ public WebClient opaWebClient(WebClient.Builder builder) {
+
+ return builder
+ .baseUrl(opaProperties.getEndpoint())
+ .build();
+ }
+
+}
diff --git a/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/config/OpaProperties.java b/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/config/OpaProperties.java
new file mode 100644
index 0000000000..acc23a2fd2
--- /dev/null
+++ b/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/config/OpaProperties.java
@@ -0,0 +1,14 @@
+package com.baeldung.security.opa.config;
+
+import javax.annotation.Nonnull;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import lombok.Data;
+
+@ConfigurationProperties(prefix = "opa")
+@Data
+public class OpaProperties {
+ @Nonnull
+ private String endpoint = "http://localhost:8181";
+}
diff --git a/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/config/SecurityConfiguration.java b/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/config/SecurityConfiguration.java
new file mode 100644
index 0000000000..7e10cb2e8a
--- /dev/null
+++ b/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/config/SecurityConfiguration.java
@@ -0,0 +1,110 @@
+package com.baeldung.security.opa.config;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.reactivestreams.Publisher;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.MediaType;
+import org.springframework.http.client.reactive.ClientHttpRequest;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.ReactiveAuthorizationManager;
+import org.springframework.security.config.web.server.ServerHttpSecurity;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.web.server.SecurityWebFilterChain;
+import org.springframework.security.web.server.authorization.AuthorizationContext;
+import org.springframework.web.reactive.function.BodyInserter;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.reactive.function.client.ClientResponse;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.ImmutableMap;
+
+import reactor.core.publisher.Mono;
+
+@Configuration
+public class SecurityConfiguration {
+
+
+ @Bean
+ public SecurityWebFilterChain accountAuthorization(ServerHttpSecurity http, @Qualifier("opaWebClient")WebClient opaWebClient) {
+
+ // @formatter:on
+ return http
+ .httpBasic()
+ .and()
+ .authorizeExchange(exchanges -> {
+ exchanges
+ .pathMatchers("/account/*")
+ .access(opaAuthManager(opaWebClient));
+ })
+ .build();
+ // @formatter:on
+
+ }
+
+ @Bean
+ public ReactiveAuthorizationManager opaAuthManager(WebClient opaWebClient) {
+
+ return (auth, context) -> {
+ return opaWebClient.post()
+ .accept(MediaType.APPLICATION_JSON)
+ .contentType(MediaType.APPLICATION_JSON)
+ .body(toAuthorizationPayload(auth,context), Map.class)
+ .exchangeToMono(this::toDecision);
+ };
+ }
+
+ private Mono toDecision(ClientResponse response) {
+
+ if ( !response.statusCode().is2xxSuccessful()) {
+ return Mono.just(new AuthorizationDecision(false));
+ }
+
+ return response
+ .bodyToMono(ObjectNode.class)
+ .map(node -> {
+ boolean authorized = node.path("result").path("authorized").asBoolean(false);
+ return new AuthorizationDecision(authorized);
+ });
+
+ }
+
+ private Publisher
-
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-web-login/pom.xml b/spring-security-modules/spring-security-web-login/pom.xml
index 346338cbcd..c2369abc14 100644
--- a/spring-security-modules/spring-security-web-login/pom.xml
+++ b/spring-security-modules/spring-security-web-login/pom.xml
@@ -118,7 +118,7 @@