diff --git a/spring-security-modules/spring-security-oauth2-testing/README.md b/spring-security-modules/spring-security-oauth2-testing/README.md deleted file mode 100644 index 82728ff450..0000000000 --- a/spring-security-modules/spring-security-oauth2-testing/README.md +++ /dev/null @@ -1,7 +0,0 @@ -## Spring-Security 6 OAuth2 testing samples - -This module contains articles about testing spring-security 6 access-control with OAuth2 - -## Relevant articles: - -- [Testing Spring OAuth2 Access-Control](https://drafts.baeldung.com/?p=154767&preview=true) diff --git a/spring-security-modules/spring-security-oauth2-testing/pom.xml b/spring-security-modules/spring-security-oauth2-testing/pom.xml index f634b6105c..93348cb48c 100644 --- a/spring-security-modules/spring-security-oauth2-testing/pom.xml +++ b/spring-security-modules/spring-security-oauth2-testing/pom.xml @@ -1,32 +1,32 @@ - 4.0.0 - spring-security-oauth2-testing - spring-security-oauth2-testing - pom - spring-security 6 oauth testing sample project - - com.baeldung - parent-boot-3 - 0.0.1-SNAPSHOT - ../../parent-boot-3 - - - 6.0.14 - - - - - com.c4-soft.springaddons - spring-addons-oauth2-test - ${spring-addons.version} - - - - - reactive-resource-server - servlet-resource-server - + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + spring-security-oauth2-testing + spring-security-oauth2-testing + pom + spring-security 6 oauth testing sample project + + com.baeldung + parent-boot-3 + 0.0.1-SNAPSHOT + ../../parent-boot-3 + + + 6.1.0 + + + + + com.c4-soft.springaddons + spring-addons-oauth2-test + ${spring-addons.version} + + + + + reactive-resource-server + servlet-resource-server + \ No newline at end of file diff --git a/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/pom.xml b/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/pom.xml index c1fa6b7c6d..86f73cfdbf 100644 --- a/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/pom.xml +++ b/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/pom.xml @@ -1,58 +1,58 @@ - 4.0.0 - - com.baeldung - spring-security-oauth2-testing - 0.0.1-SNAPSHOT - - com.baeldung.spring-security-modules.testing - reactive-resource-server - reactive-resource-server - Demo project for Spring Boot reactive resource-server - - 17 - - - - org.springframework.boot - spring-boot-starter-oauth2-resource-server - - - org.springframework.boot - spring-boot-starter-webflux - - - org.projectlombok - lombok - true - - - org.springframework.boot - spring-boot-starter-test - test - - - com.c4-soft.springaddons - spring-addons-oauth2-test - test - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.projectlombok - lombok - - - - - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + com.baeldung + spring-security-oauth2-testing + 0.0.1-SNAPSHOT + + com.baeldung.spring-security-modules.testing + reactive-resource-server + reactive-resource-server + Demo project for Spring Boot reactive resource-server + + 17 + + + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + + org.springframework.boot + spring-boot-starter-webflux + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + com.c4-soft.springaddons + spring-addons-oauth2-test + test + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + \ No newline at end of file diff --git a/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/src/main/java/com/baeldung/ReactiveResourceServerApplication.java b/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/src/main/java/com/baeldung/ReactiveResourceServerApplication.java index 608038331a..500d876bc4 100644 --- a/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/src/main/java/com/baeldung/ReactiveResourceServerApplication.java +++ b/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/src/main/java/com/baeldung/ReactiveResourceServerApplication.java @@ -49,19 +49,32 @@ public class ReactiveResourceServerApplication { public class SecurityConfig { @Bean SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, Converter>> authoritiesConverter) { - http.oauth2ResourceServer().jwt() - .jwtAuthenticationConverter(jwt -> authoritiesConverter.convert(jwt).map(authorities -> new JwtAuthenticationToken(jwt, authorities))); - http.securityContextRepository(NoOpServerSecurityContextRepository.getInstance()).csrf().disable(); - http.exceptionHandling().accessDeniedHandler((var exchange, var ex) -> exchange.getPrincipal().flatMap(principal -> { - final var response = exchange.getResponse(); - response.setStatusCode(principal instanceof AnonymousAuthenticationToken ? HttpStatus.UNAUTHORIZED : HttpStatus.FORBIDDEN); - response.getHeaders().setContentType(MediaType.TEXT_PLAIN); - final var dataBufferFactory = response.bufferFactory(); - final var buffer = dataBufferFactory.wrap(ex.getMessage().getBytes(Charset.defaultCharset())); - return response.writeWith(Mono.just(buffer)).doOnError(error -> DataBufferUtils.release(buffer)); - })); + http.oauth2ResourceServer() + .jwt() + .jwtAuthenticationConverter(jwt -> authoritiesConverter.convert(jwt) + .map(authorities -> new JwtAuthenticationToken(jwt, authorities))); + http.securityContextRepository(NoOpServerSecurityContextRepository.getInstance()) + .csrf() + .disable(); + http.exceptionHandling() + .accessDeniedHandler((var exchange, var ex) -> exchange.getPrincipal() + .flatMap(principal -> { + final var response = exchange.getResponse(); + response.setStatusCode(principal instanceof AnonymousAuthenticationToken ? HttpStatus.UNAUTHORIZED : HttpStatus.FORBIDDEN); + response.getHeaders() + .setContentType(MediaType.TEXT_PLAIN); + final var dataBufferFactory = response.bufferFactory(); + final var buffer = dataBufferFactory.wrap(ex.getMessage() + .getBytes(Charset.defaultCharset())); + return response.writeWith(Mono.just(buffer)) + .doOnError(error -> DataBufferUtils.release(buffer)); + })); - http.authorizeExchange().pathMatchers("/secured-route").hasRole("AUTHORIZED_PERSONNEL").anyExchange().authenticated(); + http.authorizeExchange() + .pathMatchers("/secured-route") + .hasRole("AUTHORIZED_PERSONNEL") + .anyExchange() + .authenticated(); return http.build(); } @@ -72,10 +85,14 @@ public class ReactiveResourceServerApplication { @Bean AuthoritiesConverter realmRoles2AuthoritiesConverter() { return (Jwt jwt) -> { - final var realmRoles = Optional.of(jwt.getClaimAsMap("realm_access")).orElse(Map.of()); + final var realmRoles = Optional.of(jwt.getClaimAsMap("realm_access")) + .orElse(Map.of()); @SuppressWarnings("unchecked") final var roles = (List) realmRoles.getOrDefault("roles", List.of()); - return Mono.just(roles.stream().map(SimpleGrantedAuthority::new).map(GrantedAuthority.class::cast).toList()); + return Mono.just(roles.stream() + .map(SimpleGrantedAuthority::new) + .map(GrantedAuthority.class::cast) + .toList()); }; } } @@ -84,13 +101,13 @@ public class ReactiveResourceServerApplication { public static class MessageService { public Mono greet() { - return ReactiveSecurityContextHolder.getContext().map(ctx -> { - final var who = (JwtAuthenticationToken) ctx.getAuthentication(); - final var claims = who.getTokenAttributes(); - return "Hello %s! You are granted with %s.".formatted( - claims.getOrDefault(StandardClaimNames.PREFERRED_USERNAME,claims.get(StandardClaimNames.SUB)), - who.getAuthorities()); - }).switchIfEmpty(Mono.error(new AuthenticationCredentialsNotFoundException("Security context is empty"))); + return ReactiveSecurityContextHolder.getContext() + .map(ctx -> { + final var who = (JwtAuthenticationToken) ctx.getAuthentication(); + final var claims = who.getTokenAttributes(); + return "Hello %s! You are granted with %s.".formatted(claims.getOrDefault(StandardClaimNames.PREFERRED_USERNAME, claims.get(StandardClaimNames.SUB)), who.getAuthorities()); + }) + .switchIfEmpty(Mono.error(new AuthenticationCredentialsNotFoundException("Security context is empty"))); } @PreAuthorize("hasRole('AUTHORIZED_PERSONNEL')") @@ -106,18 +123,21 @@ public class ReactiveResourceServerApplication { @GetMapping("/greet") public Mono> greet() { - return messageService.greet().map(ResponseEntity::ok); + return messageService.greet() + .map(ResponseEntity::ok); } @GetMapping("/secured-route") public Mono> securedRoute() { - return messageService.getSecret().map(ResponseEntity::ok); + return messageService.getSecret() + .map(ResponseEntity::ok); } @GetMapping("/secured-method") @PreAuthorize("hasRole('AUTHORIZED_PERSONNEL')") public Mono> securedMethod() { - return messageService.getSecret().map(ResponseEntity::ok); + return messageService.getSecret() + .map(ResponseEntity::ok); } } diff --git a/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/src/test/java/com/baeldung/MessageServiceUnitTest.java b/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/src/test/java/com/baeldung/MessageServiceUnitTest.java index 92896754ef..ecaf270f10 100644 --- a/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/src/test/java/com/baeldung/MessageServiceUnitTest.java +++ b/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/src/test/java/com/baeldung/MessageServiceUnitTest.java @@ -32,25 +32,29 @@ class MessageServiceUnitTest { @Test void givenSecurityContextIsEmpty_whenGreet_thenThrowsAuthenticationCredentialsNotFoundException() { - assertThrows(AuthenticationCredentialsNotFoundException.class, () -> messageService.greet().block()); + assertThrows(AuthenticationCredentialsNotFoundException.class, () -> messageService.greet() + .block()); } @Test @WithAnonymousUser void givenUserIsNotAuthenticated_whenGreet_thenThrowsClassCastException() { - assertThrows(ClassCastException.class, () -> messageService.greet().block()); + assertThrows(ClassCastException.class, () -> messageService.greet() + .block()); } @Test @WithMockJwtAuth(authorities = { "admin", "ROLE_AUTHORIZED_PERSONNEL" }, claims = @OpenIdClaims(preferredUsername = "ch4mpy")) void givenSecurityContextIsPopulatedWithJwtAuthenticationToken_whenGreet_thenReturnGreetingWithPreferredUsernameAndAuthorities() { - assertEquals("Hello ch4mpy! You are granted with [admin, ROLE_AUTHORIZED_PERSONNEL].", messageService.greet().block()); + assertEquals("Hello ch4mpy! You are granted with [admin, ROLE_AUTHORIZED_PERSONNEL].", messageService.greet() + .block()); } @Test @WithMockUser(authorities = { "admin", "ROLE_AUTHORIZED_PERSONNEL" }, username = "ch4mpy") void givenSecurityContextIsPopulatedWithUsernamePasswordAuthenticationToken_whenGreet_thenThrowsClassCastException() { - assertThrows(ClassCastException.class, () -> messageService.greet().block()); + assertThrows(ClassCastException.class, () -> messageService.greet() + .block()); } /*--------------------------------------------------------------------*/ @@ -61,19 +65,22 @@ class MessageServiceUnitTest { @Test @WithAnonymousUser void givenUserIsNotAuthenticated_whenGetSecret_thenThrowsAccessDeniedException() { - assertThrows(AccessDeniedException.class, () -> messageService.getSecret().block()); + assertThrows(AccessDeniedException.class, () -> messageService.getSecret() + .block()); } @Test @WithMockJwtAuth(authorities = { "admin", "ROLE_AUTHORIZED_PERSONNEL" }, claims = @OpenIdClaims(preferredUsername = "ch4mpy")) void givenUserIsGrantedWithRoleAuthorizedPersonnel_whenGetSecret_thenReturnSecret() { - assertEquals("Only authorized personnel can read that", messageService.getSecret().block()); + assertEquals("Only authorized personnel can read that", messageService.getSecret() + .block()); } @Test @WithMockJwtAuth(authorities = { "admin" }, claims = @OpenIdClaims(preferredUsername = "ch4mpy")) void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecret_thenThrowsAccessDeniedException() { - assertThrows(AccessDeniedException.class, () -> messageService.getSecret().block()); + assertThrows(AccessDeniedException.class, () -> messageService.getSecret() + .block()); } } diff --git a/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/src/test/java/com/baeldung/ReactiveResourceServerApplicationIntegrationTest.java b/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/src/test/java/com/baeldung/ReactiveResourceServerApplicationIntegrationTest.java index d464aa37ad..dd64eb2109 100644 --- a/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/src/test/java/com/baeldung/ReactiveResourceServerApplicationIntegrationTest.java +++ b/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/src/test/java/com/baeldung/ReactiveResourceServerApplicationIntegrationTest.java @@ -25,22 +25,23 @@ class ReactiveResourceServerApplicationIntegrationTest { @Test @WithAnonymousUser void givenUserIsAnonymous_whenGetGreet_thenUnauthorized() throws Exception { - // @formatter:off - api.get().uri("/greet").exchange() - .expectStatus().isUnauthorized(); - // @formatter:on + api.get() + .uri("/greet") + .exchange() + .expectStatus() + .isUnauthorized(); } @Test - @WithMockJwtAuth( - authorities = {"admin", "ROLE_AUTHORIZED_PERSONNEL"}, - claims = @OpenIdClaims(preferredUsername = "ch4mpy")) + @WithMockJwtAuth(authorities = { "admin", "ROLE_AUTHORIZED_PERSONNEL" }, claims = @OpenIdClaims(preferredUsername = "ch4mpy")) void givenUserIsAuthenticated_whenGetGreet_thenOk() throws Exception { - // @formatter:off - api.get().uri("/greet").exchange() - .expectStatus().isOk() - .expectBody(String.class).isEqualTo("Hello ch4mpy! You are granted with [admin, ROLE_AUTHORIZED_PERSONNEL]."); - // @formatter:on + api.get() + .uri("/greet") + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo("Hello ch4mpy! You are granted with [admin, ROLE_AUTHORIZED_PERSONNEL]."); } /*---------------------------------------------------------------------------------------------------------------------*/ @@ -51,31 +52,35 @@ class ReactiveResourceServerApplicationIntegrationTest { @Test @WithAnonymousUser void givenUserIsAnonymous_whenGetSecuredRoute_thenUnauthorized() throws Exception { - // @formatter:off - api.get().uri("/secured-route").exchange() - .expectStatus().isUnauthorized(); - // @formatter:on + api.get() + .uri("/secured-route") + .exchange() + .expectStatus() + .isUnauthorized(); } @Test @WithMockJwtAuth("ROLE_AUTHORIZED_PERSONNEL") void givenUserIsGrantedWithRoleAuthorizedPersonnel_whenGetSecuredRoute_thenOk() throws Exception { - // @formatter:off - api.get().uri("/secured-route").exchange() - .expectStatus().isOk() - .expectBody(String.class).isEqualTo("Only authorized personnel can read that"); - // @formatter:on + api.get() + .uri("/secured-route") + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo("Only authorized personnel can read that"); } @Test @WithMockJwtAuth("admin") void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecuredRoute_thenForbidden() throws Exception { - // @formatter:off - api.get().uri("/secured-route").exchange() - .expectStatus().isForbidden(); - // @formatter:on + api.get() + .uri("/secured-route") + .exchange() + .expectStatus() + .isForbidden(); } - + /*---------------------------------------------------------------------------------------------------------*/ /* /secured-method */ /* This end-point is secured with "@PreAuthorize("hasRole('AUTHORIZED_PERSONNEL')")" on @Controller method */ @@ -84,28 +89,32 @@ class ReactiveResourceServerApplicationIntegrationTest { @Test @WithAnonymousUser void givenUserIsAnonymous_whenGetSecuredMethod_thenUnauthorized() throws Exception { - // @formatter:off - api.get().uri("/secured-method").exchange() - .expectStatus().isUnauthorized(); - // @formatter:on + api.get() + .uri("/secured-method") + .exchange() + .expectStatus() + .isUnauthorized(); } @Test @WithMockJwtAuth("ROLE_AUTHORIZED_PERSONNEL") void givenUserIsGrantedWithRoleAuthorizedPersonnel_whenGetSecuredMethod_thenOk() throws Exception { - // @formatter:off - api.get().uri("/secured-method").exchange() - .expectStatus().isOk() - .expectBody(String.class).isEqualTo("Only authorized personnel can read that"); - // @formatter:on + api.get() + .uri("/secured-method") + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo("Only authorized personnel can read that"); } @Test @WithMockJwtAuth("admin") void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecuredMethod_thenForbidden() throws Exception { - // @formatter:off - api.get().uri("/secured-method").exchange() - .expectStatus().isForbidden(); - // @formatter:on + api.get() + .uri("/secured-method") + .exchange() + .expectStatus() + .isForbidden(); } } diff --git a/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/src/test/java/com/baeldung/SpringAddonsGreetingControllerUnitTest.java b/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/src/test/java/com/baeldung/SpringAddonsGreetingControllerUnitTest.java index 38bbb4d0bd..0da6701781 100644 --- a/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/src/test/java/com/baeldung/SpringAddonsGreetingControllerUnitTest.java +++ b/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/src/test/java/com/baeldung/SpringAddonsGreetingControllerUnitTest.java @@ -35,22 +35,26 @@ class SpringAddonsGreetingControllerUnitTest { @Test @WithAnonymousUser void givenUserIsAnonymous_whenGetGreet_thenUnauthorized() throws Exception { - api.get().uri("/greet").exchange().expectStatus().isUnauthorized(); + api.get() + .uri("/greet") + .exchange() + .expectStatus() + .isUnauthorized(); } @Test - @WithMockJwtAuth( - authorities = {"admin", "ROLE_AUTHORIZED_PERSONNEL"}, - claims = @OpenIdClaims(preferredUsername = "ch4mpy")) + @WithMockJwtAuth(authorities = { "admin", "ROLE_AUTHORIZED_PERSONNEL" }, claims = @OpenIdClaims(preferredUsername = "ch4mpy")) void givenUserIsAuthenticated_whenGetGreet_thenOk() throws Exception { final var greeting = "Whatever the service returns"; when(messageService.greet()).thenReturn(Mono.just(greeting)); - // @formatter:off - api.get().uri("/greet").exchange() - .expectStatus().isOk() - .expectBody(String.class).isEqualTo(greeting); - // @formatter:on + api.get() + .uri("/greet") + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo(greeting); verify(messageService, times(1)).greet(); } @@ -63,7 +67,11 @@ class SpringAddonsGreetingControllerUnitTest { @Test @WithAnonymousUser void givenUserIsAnonymous_whenGetSecuredRoute_thenUnauthorized() throws Exception { - api.get().uri("/secured-route").exchange().expectStatus().isUnauthorized(); + api.get() + .uri("/secured-route") + .exchange() + .expectStatus() + .isUnauthorized(); } @Test @@ -72,22 +80,25 @@ class SpringAddonsGreetingControllerUnitTest { final var secret = "Secret!"; when(messageService.getSecret()).thenReturn(Mono.just(secret)); - // @formatter:off - api.get().uri("/secured-route").exchange() - .expectStatus().isOk() - .expectBody(String.class).isEqualTo(secret); - // @formatter:on + api.get() + .uri("/secured-route") + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo(secret); } @Test @WithMockJwtAuth("admin") void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecuredRoute_thenForbidden() throws Exception { - // @formatter:off - api.get().uri("/secured-route").exchange() - .expectStatus().isForbidden(); - // @formatter:on + api.get() + .uri("/secured-route") + .exchange() + .expectStatus() + .isForbidden(); } - + /*---------------------------------------------------------------------------------------------------------*/ /* /secured-method */ /* This end-point is secured with "@PreAuthorize("hasRole('AUTHORIZED_PERSONNEL')")" on @Controller method */ @@ -96,7 +107,11 @@ class SpringAddonsGreetingControllerUnitTest { @Test @WithAnonymousUser void givenUserIsAnonymous_whenGetSecuredMethod_thenUnauthorized() throws Exception { - api.get().uri("/secured-method").exchange().expectStatus().isUnauthorized(); + api.get() + .uri("/secured-method") + .exchange() + .expectStatus() + .isUnauthorized(); } @Test @@ -105,21 +120,23 @@ class SpringAddonsGreetingControllerUnitTest { final var secret = "Secret!"; when(messageService.getSecret()).thenReturn(Mono.just(secret)); - // @formatter:off - api.get().uri("/secured-method").exchange() - .expectStatus().isOk() - .expectBody(String.class).isEqualTo(secret); - // @formatter:on + api.get() + .uri("/secured-method") + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo(secret); } @Test @WithMockJwtAuth("admin") void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecuredMethod_thenForbidden() throws Exception { - // @formatter:off - api.get().uri("/secured-method").exchange() - .expectStatus().isForbidden(); - // @formatter:on + api.get() + .uri("/secured-method") + .exchange() + .expectStatus() + .isForbidden(); } } - diff --git a/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/src/test/java/com/baeldung/SpringSecurityTestGreetingControllerUnitTest.java b/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/src/test/java/com/baeldung/SpringSecurityTestGreetingControllerUnitTest.java index 40ba700c21..b15c1304e6 100644 --- a/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/src/test/java/com/baeldung/SpringSecurityTestGreetingControllerUnitTest.java +++ b/spring-security-modules/spring-security-oauth2-testing/reactive-resource-server/src/test/java/com/baeldung/SpringSecurityTestGreetingControllerUnitTest.java @@ -25,10 +25,7 @@ import reactor.core.publisher.Mono; @WebFluxTest(controllers = GreetingController.class, properties = { "server.ssl.enabled=false" }) class SpringSecurityTestGreetingControllerUnitTest { - private static final AnonymousAuthenticationToken ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken( - "anonymous", - "anonymousUser", - AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); + private static final AnonymousAuthenticationToken ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken("anonymous", "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); @MockBean MessageService messageService; @@ -43,11 +40,12 @@ class SpringSecurityTestGreetingControllerUnitTest { @Test void givenUserIsAnonymous_whenGetGreet_thenUnauthorized() throws Exception { - // @formatter:off api.mutateWith(mockAuthentication(ANONYMOUS_AUTHENTICATION)) - .get().uri("/greet").exchange() - .expectStatus().isUnauthorized(); - // @formatter:on + .get() + .uri("/greet") + .exchange() + .expectStatus() + .isUnauthorized(); } @Test @@ -55,14 +53,15 @@ class SpringSecurityTestGreetingControllerUnitTest { final var greeting = "Whatever the service returns"; when(messageService.greet()).thenReturn(Mono.just(greeting)); - // @formatter:off - api.mutateWith(mockJwt() - .authorities(List.of(new SimpleGrantedAuthority("admin"), new SimpleGrantedAuthority("ROLE_AUTHORIZED_PERSONNEL"))) - .jwt(jwt -> jwt.claim(StandardClaimNames.PREFERRED_USERNAME, "ch4mpy"))) - .get().uri("/greet").exchange() - .expectStatus().isOk() - .expectBody(String.class).isEqualTo(greeting); - // @formatter:on + api.mutateWith(mockJwt().authorities(List.of(new SimpleGrantedAuthority("admin"), new SimpleGrantedAuthority("ROLE_AUTHORIZED_PERSONNEL"))) + .jwt(jwt -> jwt.claim(StandardClaimNames.PREFERRED_USERNAME, "ch4mpy"))) + .get() + .uri("/greet") + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo(greeting); verify(messageService, times(1)).greet(); } @@ -74,35 +73,39 @@ class SpringSecurityTestGreetingControllerUnitTest { @Test void givenUserIsAnonymous_whenGetSecuredRoute_thenUnauthorized() throws Exception { - // @formatter:off api.mutateWith(mockAuthentication(ANONYMOUS_AUTHENTICATION)) - .get().uri("/secured-route").exchange() - .expectStatus().isUnauthorized(); - // @formatter:on + .get() + .uri("/secured-route") + .exchange() + .expectStatus() + .isUnauthorized(); } - + @Test void givenUserIsGrantedWithRoleAuthorizedPersonnel_whenGetSecuredRoute_thenOk() throws Exception { final var secret = "Secret!"; when(messageService.getSecret()).thenReturn(Mono.just(secret)); - - // @formatter:off + api.mutateWith(mockJwt().authorities(new SimpleGrantedAuthority("ROLE_AUTHORIZED_PERSONNEL"))) - .get().uri("/secured-route").exchange() - .expectStatus().isOk() - .expectBody(String.class).isEqualTo(secret); - // @formatter:on + .get() + .uri("/secured-route") + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo(secret); } - + @Test void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecuredRoute_thenForbidden() throws Exception { - // @formatter:off api.mutateWith(mockJwt().authorities(new SimpleGrantedAuthority("admin"))) - .get().uri("/secured-route").exchange() - .expectStatus().isForbidden(); - // @formatter:on + .get() + .uri("/secured-route") + .exchange() + .expectStatus() + .isForbidden(); } - + /*---------------------------------------------------------------------------------------------------------*/ /* /secured-method */ /* This end-point is secured with "@PreAuthorize("hasRole('AUTHORIZED_PERSONNEL')")" on @Controller method */ @@ -110,11 +113,12 @@ class SpringSecurityTestGreetingControllerUnitTest { @Test void givenUserIsAnonymous_whenGetSecuredMethod_thenUnauthorized() throws Exception { - // @formatter:off api.mutateWith(mockAuthentication(ANONYMOUS_AUTHENTICATION)) - .get().uri("/secured-method").exchange() - .expectStatus().isUnauthorized(); - // @formatter:on + .get() + .uri("/secured-method") + .exchange() + .expectStatus() + .isUnauthorized(); } @Test @@ -122,22 +126,24 @@ class SpringSecurityTestGreetingControllerUnitTest { final var secret = "Secret!"; when(messageService.getSecret()).thenReturn(Mono.just(secret)); - // @formatter:off api.mutateWith(mockJwt().authorities(new SimpleGrantedAuthority("ROLE_AUTHORIZED_PERSONNEL"))) - .get().uri("/secured-method").exchange() - .expectStatus().isOk() - .expectBody(String.class).isEqualTo(secret); - // @formatter:on + .get() + .uri("/secured-method") + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo(secret); } @Test void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecuredMethod_thenForbidden() throws Exception { - // @formatter:off api.mutateWith(mockJwt().authorities(new SimpleGrantedAuthority("admin"))) - .get().uri("/secured-method").exchange() - .expectStatus().isForbidden(); - // @formatter:on + .get() + .uri("/secured-method") + .exchange() + .expectStatus() + .isForbidden(); } } - diff --git a/spring-security-modules/spring-security-oauth2-testing/servlet-resource-server/pom.xml b/spring-security-modules/spring-security-oauth2-testing/servlet-resource-server/pom.xml index 8de154b934..271cc7dc18 100644 --- a/spring-security-modules/spring-security-oauth2-testing/servlet-resource-server/pom.xml +++ b/spring-security-modules/spring-security-oauth2-testing/servlet-resource-server/pom.xml @@ -1,58 +1,58 @@ - 4.0.0 - - com.baeldung - spring-security-oauth2-testing - 0.0.1-SNAPSHOT - - com.baeldung.spring-security-modules.testing - servlet-resource-server - servlet-resource-server - Demo project for Spring Boot servlet resource-server - - 17 - - - - org.springframework.boot - spring-boot-starter-oauth2-resource-server - - - org.springframework.boot - spring-boot-starter-web - - - org.projectlombok - lombok - true - - - org.springframework.boot - spring-boot-starter-test - test - - - com.c4-soft.springaddons - spring-addons-oauth2-test - test - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.projectlombok - lombok - - - - - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + com.baeldung + spring-security-oauth2-testing + 0.0.1-SNAPSHOT + + com.baeldung.spring-security-modules.testing + servlet-resource-server + servlet-resource-server + Demo project for Spring Boot servlet resource-server + + 17 + + + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + + org.springframework.boot + spring-boot-starter-web + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + com.c4-soft.springaddons + spring-addons-oauth2-test + test + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + diff --git a/spring-security-modules/spring-security-oauth2-testing/servlet-resource-server/src/main/java/com/baeldung/ServletResourceServerApplication.java b/spring-security-modules/spring-security-oauth2-testing/servlet-resource-server/src/main/java/com/baeldung/ServletResourceServerApplication.java index ba55944c29..a30c60eab0 100644 --- a/spring-security-modules/spring-security-oauth2-testing/servlet-resource-server/src/main/java/com/baeldung/ServletResourceServerApplication.java +++ b/spring-security-modules/spring-security-oauth2-testing/servlet-resource-server/src/main/java/com/baeldung/ServletResourceServerApplication.java @@ -44,18 +44,25 @@ public class ServletResourceServerApplication { static class SecurityConf { @Bean SecurityFilterChain filterChain(HttpSecurity http, Converter> authoritiesConverter) throws Exception { - // @formatter:off - http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(jwt -> new JwtAuthenticationToken(jwt, authoritiesConverter.convert(jwt))); - http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().csrf().disable(); - http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> { - response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"Restricted Content\""); - response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase()); - }); - + http.oauth2ResourceServer() + .jwt() + .jwtAuthenticationConverter(jwt -> new JwtAuthenticationToken(jwt, authoritiesConverter.convert(jwt))); + http.sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() + .csrf() + .disable(); + http.exceptionHandling() + .authenticationEntryPoint((request, response, authException) -> { + response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"Restricted Content\""); + response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase()); + }); + http.authorizeHttpRequests() - .requestMatchers("/secured-route").hasRole("AUTHORIZED_PERSONNEL") - .anyRequest().authenticated(); - // @formatter:on + .requestMatchers("/secured-route") + .hasRole("AUTHORIZED_PERSONNEL") + .anyRequest() + .authenticated(); return http.build(); } @@ -66,10 +73,14 @@ public class ServletResourceServerApplication { @Bean AuthoritiesConverter realmRoles2AuthoritiesConverter() { return (Jwt jwt) -> { - final var realmRoles = Optional.of(jwt.getClaimAsMap("realm_access")).orElse(Map.of()); + final var realmRoles = Optional.of(jwt.getClaimAsMap("realm_access")) + .orElse(Map.of()); @SuppressWarnings("unchecked") final var roles = (List) realmRoles.getOrDefault("roles", List.of()); - return roles.stream().map(SimpleGrantedAuthority::new).map(GrantedAuthority.class::cast).toList(); + return roles.stream() + .map(SimpleGrantedAuthority::new) + .map(GrantedAuthority.class::cast) + .toList(); }; } } @@ -78,11 +89,10 @@ public class ServletResourceServerApplication { public static class MessageService { public String greet() { - final var who = (JwtAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); + final var who = (JwtAuthenticationToken) SecurityContextHolder.getContext() + .getAuthentication(); final var claims = who.getTokenAttributes(); - return "Hello %s! You are granted with %s.".formatted( - claims.getOrDefault(StandardClaimNames.PREFERRED_USERNAME, claims.get(StandardClaimNames.SUB)), - who.getAuthorities()); + return "Hello %s! You are granted with %s.".formatted(claims.getOrDefault(StandardClaimNames.PREFERRED_USERNAME, claims.get(StandardClaimNames.SUB)), who.getAuthorities()); } @PreAuthorize("hasRole('AUTHORIZED_PERSONNEL')") diff --git a/spring-security-modules/spring-security-oauth2-testing/servlet-resource-server/src/test/java/com/baeldung/ServletResourceServerApplicationIntegrationTest.java b/spring-security-modules/spring-security-oauth2-testing/servlet-resource-server/src/test/java/com/baeldung/ServletResourceServerApplicationIntegrationTest.java index eba908da75..60b74e93be 100644 --- a/spring-security-modules/spring-security-oauth2-testing/servlet-resource-server/src/test/java/com/baeldung/ServletResourceServerApplicationIntegrationTest.java +++ b/spring-security-modules/spring-security-oauth2-testing/servlet-resource-server/src/test/java/com/baeldung/ServletResourceServerApplicationIntegrationTest.java @@ -29,22 +29,16 @@ class ServletResourceServerApplicationIntegrationTest { @Test @WithAnonymousUser void givenUserIsNotAuthenticated_whenGetGreet_thenUnauthorized() throws Exception { - // @formatter:off api.perform(get("/greet")) .andExpect(status().isUnauthorized()); - // @formatter:on } @Test - @WithMockJwtAuth( - authorities = {"admin", "ROLE_AUTHORIZED_PERSONNEL"}, - claims = @OpenIdClaims(preferredUsername = "ch4mpy")) + @WithMockJwtAuth(authorities = { "admin", "ROLE_AUTHORIZED_PERSONNEL" }, claims = @OpenIdClaims(preferredUsername = "ch4mpy")) void givenUserIsAuthenticated_whenGetGreet_thenOk() throws Exception { - // @formatter:off api.perform(get("/greet")) - .andExpect(status().isOk()) - .andExpect(content().string("Hello ch4mpy! You are granted with [admin, ROLE_AUTHORIZED_PERSONNEL].")); - // @formatter:on + .andExpect(status().isOk()) + .andExpect(content().string("Hello ch4mpy! You are granted with [admin, ROLE_AUTHORIZED_PERSONNEL].")); } /*---------------------------------------------------------------------------------------------------------------------*/ @@ -55,31 +49,25 @@ class ServletResourceServerApplicationIntegrationTest { @Test @WithAnonymousUser void givenUserIsNotAuthenticated_whenGetSecuredRoute_thenUnauthorized() throws Exception { - // @formatter:off api.perform(get("/secured-route")) .andExpect(status().isUnauthorized()); - // @formatter:on } @Test @WithMockJwtAuth("ROLE_AUTHORIZED_PERSONNEL") void givenUserIsGrantedWithRoleAuthorizedPersonnel_whenGetSecuredRoute_thenOk() throws Exception { - // @formatter:off api.perform(get("/secured-route")) - .andExpect(status().isOk()) - .andExpect(content().string("Only authorized personnel can read that")); - // @formatter:on + .andExpect(status().isOk()) + .andExpect(content().string("Only authorized personnel can read that")); } @Test @WithMockJwtAuth("admin") void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecuredRoute_thenForbidden() throws Exception { - // @formatter:off api.perform(get("/secured-route")) .andExpect(status().isForbidden()); - // @formatter:on } - + /*---------------------------------------------------------------------------------------------------------*/ /* /secured-method */ /* This end-point is secured with "@PreAuthorize("hasRole('AUTHORIZED_PERSONNEL')")" on @Controller method */ @@ -88,29 +76,23 @@ class ServletResourceServerApplicationIntegrationTest { @Test @WithAnonymousUser void givenUserIsNotAuthenticated_whenGetSecuredMethod_thenUnauthorized() throws Exception { - // @formatter:off api.perform(get("/secured-method")) .andExpect(status().isUnauthorized()); - // @formatter:on } @Test @WithMockJwtAuth("ROLE_AUTHORIZED_PERSONNEL") void givenUserIsGrantedWithRoleAuthorizedPersonnel_whenGetSecuredMethod_thenOk() throws Exception { - // @formatter:off api.perform(get("/secured-method")) .andExpect(status().isOk()) .andExpect(content().string("Only authorized personnel can read that")); - // @formatter:on } @Test @WithMockJwtAuth("admin") void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecuredMethod_thenForbidden() throws Exception { - // @formatter:off api.perform(get("/secured-method")) .andExpect(status().isForbidden()); - // @formatter:on } } diff --git a/spring-security-modules/spring-security-oauth2-testing/servlet-resource-server/src/test/java/com/baeldung/SpringAddonsGreetingControllerUnitTest.java b/spring-security-modules/spring-security-oauth2-testing/servlet-resource-server/src/test/java/com/baeldung/SpringAddonsGreetingControllerUnitTest.java index f18ad85832..949a8aa04c 100644 --- a/spring-security-modules/spring-security-oauth2-testing/servlet-resource-server/src/test/java/com/baeldung/SpringAddonsGreetingControllerUnitTest.java +++ b/spring-security-modules/spring-security-oauth2-testing/servlet-resource-server/src/test/java/com/baeldung/SpringAddonsGreetingControllerUnitTest.java @@ -36,25 +36,19 @@ class SpringAddonsGreetingControllerUnitTest { @Test @WithAnonymousUser void givenUserIsNotAuthenticated_whenGetGreet_thenUnauthorized() throws Exception { - // @formatter:off api.perform(get("/greet")) .andExpect(status().isUnauthorized()); - // @formatter:on } @Test - @WithMockJwtAuth( - authorities = {"admin", "ROLE_AUTHORIZED_PERSONNEL"}, - claims = @OpenIdClaims(preferredUsername = "ch4mpy")) + @WithMockJwtAuth(authorities = { "admin", "ROLE_AUTHORIZED_PERSONNEL" }, claims = @OpenIdClaims(preferredUsername = "ch4mpy")) void givenUserIsAuthenticated_whenGetGreet_thenOk() throws Exception { final var greeting = "Whatever the service returns"; when(messageService.greet()).thenReturn(greeting); - // @formatter:off api.perform(get("/greet")) .andExpect(status().isOk()) .andExpect(content().string(greeting)); - // @formatter:on verify(messageService, times(1)).greet(); } @@ -67,10 +61,8 @@ class SpringAddonsGreetingControllerUnitTest { @Test @WithAnonymousUser void givenUserIsNotAuthenticated_whenGetSecuredRoute_thenUnauthorized() throws Exception { - // @formatter:off api.perform(get("/secured-route")) .andExpect(status().isUnauthorized()); - // @formatter:on } @Test @@ -79,22 +71,18 @@ class SpringAddonsGreetingControllerUnitTest { final var secret = "Secret!"; when(messageService.getSecret()).thenReturn(secret); - // @formatter:off api.perform(get("/secured-route")) .andExpect(status().isOk()) .andExpect(content().string(secret)); - // @formatter:on } @Test @WithMockJwtAuth({ "admin" }) void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecuredRoute_thenForbidden() throws Exception { - // @formatter:off api.perform(get("/secured-route")) .andExpect(status().isForbidden()); - // @formatter:on } - + /*---------------------------------------------------------------------------------------------------------*/ /* /secured-method */ /* This end-point is secured with "@PreAuthorize("hasRole('AUTHORIZED_PERSONNEL')")" on @Controller method */ @@ -103,10 +91,8 @@ class SpringAddonsGreetingControllerUnitTest { @Test @WithAnonymousUser void givenUserIsNotAuthenticated_whenGetSecuredMethod_thenUnauthorized() throws Exception { - // @formatter:off api.perform(get("/secured-method")) .andExpect(status().isUnauthorized()); - // @formatter:on } @Test @@ -115,20 +101,16 @@ class SpringAddonsGreetingControllerUnitTest { final var secret = "Secret!"; when(messageService.getSecret()).thenReturn(secret); - // @formatter:off api.perform(get("/secured-method")) .andExpect(status().isOk()) .andExpect(content().string(secret)); - // @formatter:on } @Test @WithMockJwtAuth(authorities = { "admin" }) void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecuredMethod_thenForbidden() throws Exception { - // @formatter:off api.perform(get("/secured-method")) .andExpect(status().isForbidden()); - // @formatter:on } } diff --git a/spring-security-modules/spring-security-oauth2-testing/servlet-resource-server/src/test/java/com/baeldung/SpringSecurityTestGreetingControllerUnitTest.java b/spring-security-modules/spring-security-oauth2-testing/servlet-resource-server/src/test/java/com/baeldung/SpringSecurityTestGreetingControllerUnitTest.java index 83a95cf508..2bbd294401 100644 --- a/spring-security-modules/spring-security-oauth2-testing/servlet-resource-server/src/test/java/com/baeldung/SpringSecurityTestGreetingControllerUnitTest.java +++ b/spring-security-modules/spring-security-oauth2-testing/servlet-resource-server/src/test/java/com/baeldung/SpringSecurityTestGreetingControllerUnitTest.java @@ -38,10 +38,8 @@ class SpringSecurityTestGreetingControllerUnitTest { @Test void givenUserIsNotAuthenticated_whenGetGreet_thenUnauthorized() throws Exception { - // @formatter:off api.perform(get("/greet").with(anonymous())) .andExpect(status().isUnauthorized()); - // @formatter:on } @Test @@ -49,13 +47,10 @@ class SpringSecurityTestGreetingControllerUnitTest { final var greeting = "Whatever the service returns"; when(messageService.greet()).thenReturn(greeting); - // @formatter:off - api.perform(get("/greet").with(jwt() - .authorities(List.of(new SimpleGrantedAuthority("admin"), new SimpleGrantedAuthority("ROLE_AUTHORIZED_PERSONNEL"))) - .jwt(jwt -> jwt.claim(StandardClaimNames.PREFERRED_USERNAME, "ch4mpy")))) + api.perform(get("/greet").with(jwt().authorities(List.of(new SimpleGrantedAuthority("admin"), new SimpleGrantedAuthority("ROLE_AUTHORIZED_PERSONNEL"))) + .jwt(jwt -> jwt.claim(StandardClaimNames.PREFERRED_USERNAME, "ch4mpy")))) .andExpect(status().isOk()) .andExpect(content().string(greeting)); - // @formatter:on verify(messageService, times(1)).greet(); } @@ -67,10 +62,8 @@ class SpringSecurityTestGreetingControllerUnitTest { @Test void givenUserIsNotAuthenticated_whenGetSecuredRoute_thenUnauthorized() throws Exception { - // @formatter:off api.perform(get("/secured-route").with(anonymous())) .andExpect(status().isUnauthorized()); - // @formatter:on } @Test @@ -78,21 +71,17 @@ class SpringSecurityTestGreetingControllerUnitTest { final var secret = "Secret!"; when(messageService.getSecret()).thenReturn(secret); - // @formatter:off api.perform(get("/secured-route").with(jwt().authorities(new SimpleGrantedAuthority("ROLE_AUTHORIZED_PERSONNEL")))) .andExpect(status().isOk()) .andExpect(content().string(secret)); - // @formatter:on } @Test void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecuredRoute_thenForbidden() throws Exception { - // @formatter:off api.perform(get("/secured-route").with(jwt().authorities(new SimpleGrantedAuthority("admin")))) .andExpect(status().isForbidden()); - // @formatter:on } - + /*---------------------------------------------------------------------------------------------------------*/ /* /secured-method */ /* This end-point is secured with "@PreAuthorize("hasRole('AUTHORIZED_PERSONNEL')")" on @Controller method */ @@ -100,10 +89,8 @@ class SpringSecurityTestGreetingControllerUnitTest { @Test void givenUserIsNotAuthenticated_whenGetSecuredMethod_thenUnauthorized() throws Exception { - // @formatter:off api.perform(get("/secured-method").with(anonymous())) .andExpect(status().isUnauthorized()); - // @formatter:on } @Test @@ -111,19 +98,15 @@ class SpringSecurityTestGreetingControllerUnitTest { final var secret = "Secret!"; when(messageService.getSecret()).thenReturn(secret); - // @formatter:off api.perform(get("/secured-method").with(jwt().authorities(new SimpleGrantedAuthority("ROLE_AUTHORIZED_PERSONNEL")))) .andExpect(status().isOk()) .andExpect(content().string(secret)); - // @formatter:on } @Test void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecuredMethod_thenForbidden() throws Exception { - // @formatter:off api.perform(get("/secured-method").with(jwt().authorities(new SimpleGrantedAuthority("admin")))) .andExpect(status().isForbidden()); - // @formatter:on } }