[JAVA-29171] Upgrade spring-security-core module to Spring Boot 3 (#15487)

This commit is contained in:
Amit Pandey 2024-01-05 13:40:00 +05:30 committed by GitHub
parent 67c4875aff
commit ff11a92e8a
13 changed files with 106 additions and 123 deletions

View File

@ -10,8 +10,9 @@
<parent> <parent>
<groupId>com.baeldung</groupId> <groupId>com.baeldung</groupId>
<artifactId>spring-security-modules</artifactId> <artifactId>parent-boot-3</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<relativePath>../../parent-boot-3</relativePath>
</parent> </parent>
<dependencies> <dependencies>
@ -66,6 +67,10 @@
</plugins> </plugins>
</build> </build>
<properties>
<start-class>com.baeldung.app.App</start-class>
</properties>
<profiles> <profiles>
<profile> <profile>
<id>live</id> <id>live</id>

View File

@ -1,13 +1,10 @@
package com.baeldung.app; package com.baeldung.app;
import javax.servlet.Filter;
import jakarta.servlet.Filter;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.web.filter.DelegatingFilterProxy; import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
@ -20,7 +17,7 @@ public class App extends SpringBootServletInitializer {
public static class ApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { public static class ApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override @Override
protected javax.servlet.Filter[] getServletFilters() { protected Filter[] getServletFilters() {
DelegatingFilterProxy delegateFilterProxy = new DelegatingFilterProxy(); DelegatingFilterProxy delegateFilterProxy = new DelegatingFilterProxy();
delegateFilterProxy.setTargetBeanName("loggingFilter"); delegateFilterProxy.setTargetBeanName("loggingFilter");
return new Filter[] { delegateFilterProxy }; return new Filter[] { delegateFilterProxy };

View File

@ -2,13 +2,14 @@ package com.baeldung.app.auditing;
import org.springframework.boot.actuate.audit.AuditEvent; import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.boot.actuate.security.AbstractAuthorizationAuditListener; import org.springframework.boot.actuate.security.AbstractAuthorizationAuditListener;
import org.springframework.security.access.event.AbstractAuthorizationEvent; import org.springframework.security.authorization.event.AuthorizationDeniedEvent;
import org.springframework.security.access.event.AuthorizationFailureEvent; import org.springframework.security.authorization.event.AuthorizationEvent;
import org.springframework.security.web.FilterInvocation; import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.HashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.function.Supplier;
@Component @Component
public class ExposeAttemptedPathAuthorizationAuditListener extends AbstractAuthorizationAuditListener { public class ExposeAttemptedPathAuthorizationAuditListener extends AbstractAuthorizationAuditListener {
@ -16,21 +17,37 @@ public class ExposeAttemptedPathAuthorizationAuditListener extends AbstractAutho
public static final String AUTHORIZATION_FAILURE = "AUTHORIZATION_FAILURE"; public static final String AUTHORIZATION_FAILURE = "AUTHORIZATION_FAILURE";
@Override @Override
public void onApplicationEvent(AbstractAuthorizationEvent event) { public void onApplicationEvent(AuthorizationEvent event) {
if (event instanceof AuthorizationFailureEvent) { if (event instanceof AuthorizationDeniedEvent) {
onAuthorizationFailureEvent((AuthorizationFailureEvent) event); onAuthorizationFailureEvent((AuthorizationDeniedEvent) event);
} }
} }
private void onAuthorizationFailureEvent(AuthorizationFailureEvent event) { private void onAuthorizationFailureEvent(AuthorizationDeniedEvent event) {
Map<String, Object> data = new HashMap<>(); String name = this.getName(event.getAuthentication());
data.put("type", event.getAccessDeniedException().getClass().getName()); Map<String, Object> data = new LinkedHashMap();
data.put("message", event.getAccessDeniedException().getMessage()); Object details = this.getDetails(event.getAuthentication());
data.put("requestUrl", ((FilterInvocation)event.getSource()).getRequestUrl() ); if (details != null) {
if (event.getAuthentication().getDetails() != null) { data.put("details", details);
data.put("details", event.getAuthentication().getDetails());
} }
publish(new AuditEvent(event.getAuthentication().getName(), AUTHORIZATION_FAILURE,
data)); publish(new AuditEvent(name, "AUTHORIZATION_FAILURE", data));
} }
private String getName(Supplier<Authentication> authentication) {
try {
return authentication.get().getName();
} catch (Exception var3) {
return "<unknown>";
}
}
private Object getDetails(Supplier<Authentication> authentication) {
try {
return (authentication.get()).getDetails();
} catch (Exception var3) {
return null;
}
}
} }

View File

@ -2,6 +2,7 @@ package com.baeldung.app.config;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; 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.EnableWebSecurity;
@ -18,18 +19,14 @@ public class WebSecurityConfig {
@Bean @Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests() http.authorizeHttpRequests(auth -> auth
.antMatchers("/css/**", "/js/**", "/loggedout") .requestMatchers("/css/**", "/js/**", "/loggedout")
.permitAll() .permitAll()
.anyRequest() .anyRequest()
.authenticated() .authenticated())
.and() .httpBasic(Customizer.withDefaults())
.httpBasic() .logout(logout -> logout.disable())
.and() .csrf(csrf -> csrf.disable());
.logout()
.disable()
.csrf()
.disable();
return http.build(); return http.build();
} }

View File

@ -1,23 +1,14 @@
package com.baeldung.app.controller; package com.baeldung.app.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.baeldung.app.entity.Task; import com.baeldung.app.entity.Task;
import com.baeldung.app.service.TaskService; import com.baeldung.app.service.TaskService;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.http.HttpServletRequest; import org.springframework.http.ResponseEntity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller @Controller
@RequestMapping("api/tasks") @RequestMapping("api/tasks")

View File

@ -1,8 +1,8 @@
package com.baeldung.app.entity; package com.baeldung.app.entity;
import javax.persistence.Entity; import jakarta.persistence.Entity;
import javax.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue;
import javax.persistence.Id; import jakarta.persistence.Id;
@Entity @Entity
public class Task { public class Task {

View File

@ -1,19 +1,13 @@
package com.baeldung.app.filter; package com.baeldung.app.filter;
import java.io.IOException; import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.io.IOException;
@Component("loggingFilter") @Component("loggingFilter")
public class CustomFilter implements Filter { public class CustomFilter implements Filter {

View File

@ -1,5 +1,6 @@
package com.baeldung.checkrolejava; package com.baeldung.checkrolejava;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
@ -10,7 +11,6 @@ import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import javax.servlet.http.HttpServletRequest;
@Controller @Controller
public class UserController { public class UserController {

View File

@ -9,6 +9,8 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.method.AbstractFallbackMethodSecurityMetadataSource; import org.springframework.security.access.method.AbstractFallbackMethodSecurityMetadataSource;
import org.springframework.security.access.prepost.PostAuthorize; import org.springframework.security.access.prepost.PostAuthorize;
@ -23,22 +25,20 @@ public class CustomPermissionAllowedMethodSecurityMetadataSource extends Abstrac
@Override @Override
protected Collection<ConfigAttribute> findAttributes(Method method, Class<?> targetClass) { protected Collection<ConfigAttribute> findAttributes(Method method, Class<?> targetClass) {
Annotation[] annotations = AnnotationUtils.getAnnotations(method); MergedAnnotations annotations = MergedAnnotations.from(method,
MergedAnnotations.SearchStrategy.DIRECT);
List<ConfigAttribute> attributes = new ArrayList<>(); List<ConfigAttribute> attributes = new ArrayList<>();
MergedAnnotations classAnnotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.DIRECT);
// if the class is annotated as @Controller we should by default deny access to every method // if the class is annotated as @Controller we should by default deny access to every method
if (AnnotationUtils.findAnnotation(targetClass, Controller.class) != null) { if (classAnnotations.get(Controller.class).isPresent()) {
attributes.add(DENY_ALL_ATTRIBUTE); attributes.add(DENY_ALL_ATTRIBUTE);
} }
if (annotations != null) { if (annotations.get(PreAuthorize.class).isPresent() || annotations.get(PostAuthorize.class).isPresent()) {
for (Annotation a : annotations) {
// but not if the method has at least a PreAuthorize or PostAuthorize annotation
if (a instanceof PreAuthorize || a instanceof PostAuthorize) {
return null; return null;
} }
}
}
return attributes; return attributes;
} }

View File

@ -3,6 +3,7 @@ package com.baeldung.filterresponse.config;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; 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.EnableWebSecurity;
import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.User;
@ -37,13 +38,11 @@ public class AppConfig implements WebMvcConfigurer {
@Bean @Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf() http.authorizeHttpRequests(auth -> auth
.disable()
.authorizeRequests()
.anyRequest() .anyRequest()
.authenticated() .authenticated())
.and() .httpBasic(Customizer.withDefaults())
.httpBasic(); .csrf(csrf -> csrf.disable());
return http.build(); return http.build();
} }

View File

@ -1,10 +1,9 @@
package com.baeldung.methodsecurity.service; package com.baeldung.methodsecurity.service;
import java.util.List; import com.baeldung.methodsecurity.annotation.IsViewer;
import java.util.stream.Collectors; import com.baeldung.methodsecurity.entity.CustomUser;
import com.baeldung.methodsecurity.repository.UserRoleRepository;
import javax.annotation.security.RolesAllowed; import jakarta.annotation.security.RolesAllowed;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.annotation.Secured; import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PostAuthorize; import org.springframework.security.access.prepost.PostAuthorize;
@ -15,9 +14,8 @@ import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.baeldung.methodsecurity.annotation.IsViewer; import java.util.List;
import com.baeldung.methodsecurity.entity.CustomUser; import java.util.stream.Collectors;
import com.baeldung.methodsecurity.repository.UserRoleRepository;
@Service @Service
public class UserRoleService { public class UserRoleService {

View File

@ -1,14 +1,9 @@
package com.baeldung.denyonmissing; package com.baeldung.denyonmissing;
import static org.hamcrest.core.Is.isA; import jakarta.servlet.ServletException;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.jupiter.api.Assertions;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
@ -19,12 +14,13 @@ import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = DenyApplication.class) @SpringBootTest(classes = DenyApplication.class)
public class DenyOnMissingControllerIntegrationTest { public class DenyOnMissingControllerIntegrationTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Autowired @Autowired
private WebApplicationContext context; private WebApplicationContext context;
private MockMvc mockMvc; private MockMvc mockMvc;
@ -46,8 +42,11 @@ public class DenyOnMissingControllerIntegrationTest {
@Test @Test
@WithMockUser(username = "user") @WithMockUser(username = "user")
public void givenANormalUser_whenCallingBye_thenAccessDenied() throws Exception { public void givenANormalUser_whenCallingBye_thenAccessDenied() throws Exception {
expectedException.expectCause(isA(AccessDeniedException.class)); ServletException exception = Assertions.assertThrows(ServletException.class, () -> {
mockMvc.perform(get("/bye")); mockMvc.perform(get("/bye"));
});
Assertions.assertNotNull(exception);
Assertions.assertEquals(exception.getCause().getClass(), AccessDeniedException.class);
} }
} }

View File

@ -1,12 +1,10 @@
package com.baeldung.filterresponse; package com.baeldung.filterresponse;
import com.baeldung.filterresponse.config.AppConfig; import com.baeldung.filterresponse.config.AppConfig;
import org.hamcrest.BaseMatcher; import jakarta.servlet.ServletException;
import org.hamcrest.Description;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.jupiter.api.Assertions;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.test.context.support.WithMockUser; import org.springframework.security.test.context.support.WithMockUser;
@ -16,8 +14,8 @@ import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.util.NestedServletException;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -32,9 +30,6 @@ public class SpringSecurityJsonViewIntegrationTest {
private MockMvc mvc; private MockMvc mvc;
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Before @Before
public void setup() { public void setup() {
mvc = MockMvcBuilders mvc = MockMvcBuilders
@ -65,21 +60,12 @@ public class SpringSecurityJsonViewIntegrationTest {
@Test @Test
@WithMockUser(username = "user", password = "userPass", roles = {"ADMIN", "USER"}) @WithMockUser(username = "user", password = "userPass", roles = {"ADMIN", "USER"})
public void whenMultipleRoles_thenExceptionIsThrown() throws Exception { public void whenMultipleRoles_thenExceptionIsThrown() throws Exception {
expectedException.expect(new BaseMatcher<NestedServletException>() { ServletException exception = Assertions.assertThrows(ServletException.class, () -> {
@Override
public boolean matches(Object o) {
NestedServletException exception = (NestedServletException) o;
return exception.getCause() instanceof IllegalArgumentException && exception.getCause().getMessage().equals("Ambiguous @JsonView declaration for roles ROLE_ADMIN,ROLE_USER");
}
@Override
public void describeTo(Description description) {
}
});
mvc.perform(get("/items")) mvc.perform(get("/items"))
.andExpect(status().isOk()); .andExpect(status().isOk());
});
Assertions.assertEquals(exception.getCause().getClass(), IllegalArgumentException.class);
assertTrue(exception.getCause().getMessage().equals("Ambiguous @JsonView declaration for roles ROLE_ADMIN,ROLE_USER"));
} }
} }