Merge pull request #13405 from ch4mpy/BAEL-6223
Sample code for "Testing Spring OAuth2 Access-Control"
This commit is contained in:
commit
a290b5862e
1
pom.xml
1
pom.xml
|
@ -1023,6 +1023,7 @@
|
||||||
<module>spring-kafka</module>
|
<module>spring-kafka</module>
|
||||||
|
|
||||||
<module>spring-native</module>
|
<module>spring-native</module>
|
||||||
|
<module>spring-security-modules/spring-security-oauth2-testing</module>
|
||||||
<module>spring-protobuf</module>
|
<module>spring-protobuf</module>
|
||||||
<module>spring-quartz</module>
|
<module>spring-quartz</module>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
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">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>spring-security-oauth2-testing</artifactId>
|
||||||
|
<name>spring-security-oauth2-testing</name>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<description>spring-security 6 oauth testing sample project</description>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>parent-boot-3</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<relativePath>../../parent-boot-3</relativePath>
|
||||||
|
</parent>
|
||||||
|
<properties>
|
||||||
|
<spring-addons.version>6.1.0</spring-addons.version>
|
||||||
|
</properties>
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.c4-soft.springaddons</groupId>
|
||||||
|
<artifactId>spring-addons-oauth2-test</artifactId>
|
||||||
|
<version>${spring-addons.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
<modules>
|
||||||
|
<module>reactive-resource-server</module>
|
||||||
|
<module>servlet-resource-server</module>
|
||||||
|
</modules>
|
||||||
|
</project>
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>spring-security-oauth2-testing</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<groupId>com.baeldung.spring-security-modules.testing</groupId>
|
||||||
|
<artifactId>reactive-resource-server</artifactId>
|
||||||
|
<name>reactive-resource-server</name>
|
||||||
|
<description>Demo project for Spring Boot reactive resource-server</description>
|
||||||
|
<properties>
|
||||||
|
<java.version>17</java.version>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.c4-soft.springaddons</groupId>
|
||||||
|
<artifactId>spring-addons-oauth2-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<excludes>
|
||||||
|
<exclude>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
</exclude>
|
||||||
|
</excludes>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
|
@ -0,0 +1,144 @@
|
||||||
|
package com.baeldung;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||||
|
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||||
|
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
||||||
|
import org.springframework.security.oauth2.core.oidc.StandardClaimNames;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
|
||||||
|
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||||
|
import org.springframework.security.web.server.context.NoOpServerSecurityContextRepository;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class ReactiveResourceServerApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(ReactiveResourceServerApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebFluxSecurity
|
||||||
|
@EnableReactiveMethodSecurity
|
||||||
|
public class SecurityConfig {
|
||||||
|
@Bean
|
||||||
|
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, Converter<Jwt, Mono<Collection<GrantedAuthority>>> 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.authorizeExchange()
|
||||||
|
.pathMatchers("/secured-route")
|
||||||
|
.hasRole("AUTHORIZED_PERSONNEL")
|
||||||
|
.anyExchange()
|
||||||
|
.authenticated();
|
||||||
|
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
static interface AuthoritiesConverter extends Converter<Jwt, Mono<Collection<GrantedAuthority>>> {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
AuthoritiesConverter realmRoles2AuthoritiesConverter() {
|
||||||
|
return (Jwt jwt) -> {
|
||||||
|
final var realmRoles = Optional.of(jwt.getClaimAsMap("realm_access"))
|
||||||
|
.orElse(Map.of());
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final var roles = (List<String>) realmRoles.getOrDefault("roles", List.of());
|
||||||
|
return Mono.just(roles.stream()
|
||||||
|
.map(SimpleGrantedAuthority::new)
|
||||||
|
.map(GrantedAuthority.class::cast)
|
||||||
|
.toList());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public static class MessageService {
|
||||||
|
|
||||||
|
public Mono<String> 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")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasRole('AUTHORIZED_PERSONNEL')")
|
||||||
|
public Mono<String> getSecret() {
|
||||||
|
return Mono.just("Only authorized personnel can read that");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GreetingController {
|
||||||
|
private final MessageService messageService;
|
||||||
|
|
||||||
|
@GetMapping("/greet")
|
||||||
|
public Mono<ResponseEntity<String>> greet() {
|
||||||
|
return messageService.greet()
|
||||||
|
.map(ResponseEntity::ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/secured-route")
|
||||||
|
public Mono<ResponseEntity<String>> securedRoute() {
|
||||||
|
return messageService.getSecret()
|
||||||
|
.map(ResponseEntity::ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/secured-method")
|
||||||
|
@PreAuthorize("hasRole('AUTHORIZED_PERSONNEL')")
|
||||||
|
public Mono<ResponseEntity<String>> securedMethod() {
|
||||||
|
return messageService.getSecret()
|
||||||
|
.map(ResponseEntity::ok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
spring:
|
||||||
|
security:
|
||||||
|
oauth2:
|
||||||
|
resourceserver:
|
||||||
|
jwt:
|
||||||
|
issuer-uri: https://localhost:8443/realms/master
|
|
@ -0,0 +1,86 @@
|
||||||
|
package com.baeldung;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
|
||||||
|
import org.springframework.security.test.context.support.WithAnonymousUser;
|
||||||
|
import org.springframework.security.test.context.support.WithMockUser;
|
||||||
|
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
|
|
||||||
|
import com.baeldung.ReactiveResourceServerApplication.MessageService;
|
||||||
|
import com.c4_soft.springaddons.security.oauth2.test.annotations.OpenIdClaims;
|
||||||
|
import com.c4_soft.springaddons.security.oauth2.test.annotations.WithMockJwtAuth;
|
||||||
|
|
||||||
|
@Import({ MessageService.class })
|
||||||
|
@ExtendWith(SpringExtension.class)
|
||||||
|
@EnableReactiveMethodSecurity
|
||||||
|
class MessageServiceUnitTest {
|
||||||
|
@Autowired
|
||||||
|
MessageService messageService;
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
/* greet() */
|
||||||
|
/* Expects a JwtAuthenticationToken to be retrieved from the security-context */
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenSecurityContextIsEmpty_whenGreet_thenThrowsAuthenticationCredentialsNotFoundException() {
|
||||||
|
assertThrows(AuthenticationCredentialsNotFoundException.class, () -> messageService.greet()
|
||||||
|
.block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithAnonymousUser
|
||||||
|
void givenUserIsAnonymous_whenGreet_thenThrowsClassCastException() {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(authorities = { "admin", "ROLE_AUTHORIZED_PERSONNEL" }, username = "ch4mpy")
|
||||||
|
void givenSecurityContextIsPopulatedWithUsernamePasswordAuthenticationToken_whenGreet_thenThrowsClassCastException() {
|
||||||
|
assertThrows(ClassCastException.class, () -> messageService.greet()
|
||||||
|
.block());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------*/
|
||||||
|
/* getSecret() */
|
||||||
|
/* is secured with "@PreAuthorize("hasRole('AUTHORIZED_PERSONNEL')")" */
|
||||||
|
/*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithAnonymousUser
|
||||||
|
void givenUserIsAnonymous_whenGetSecret_thenThrowsAccessDeniedException() {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockJwtAuth(authorities = { "admin" }, claims = @OpenIdClaims(preferredUsername = "ch4mpy"))
|
||||||
|
void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecret_thenThrowsAccessDeniedException() {
|
||||||
|
assertThrows(AccessDeniedException.class, () -> messageService.getSecret()
|
||||||
|
.block());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
package com.baeldung;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||||
|
import org.springframework.security.test.context.support.WithAnonymousUser;
|
||||||
|
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||||
|
|
||||||
|
import com.c4_soft.springaddons.security.oauth2.test.annotations.OpenIdClaims;
|
||||||
|
import com.c4_soft.springaddons.security.oauth2.test.annotations.WithMockJwtAuth;
|
||||||
|
|
||||||
|
@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
|
||||||
|
@AutoConfigureWebTestClient
|
||||||
|
class ReactiveResourceServerApplicationIntegrationTest {
|
||||||
|
@Autowired
|
||||||
|
WebTestClient api;
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------------*/
|
||||||
|
/* /greet */
|
||||||
|
/* This end-point secured with ".anyRequest().authenticated()" in SecurityConf */
|
||||||
|
/*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithAnonymousUser
|
||||||
|
void givenRequestIsAnonymous_whenGetGreet_thenUnauthorized() throws Exception {
|
||||||
|
api.get()
|
||||||
|
.uri("/greet")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isUnauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockJwtAuth(authorities = { "admin", "ROLE_AUTHORIZED_PERSONNEL" }, claims = @OpenIdClaims(preferredUsername = "ch4mpy"))
|
||||||
|
void givenUserIsAuthenticated_whenGetGreet_thenOk() throws Exception {
|
||||||
|
api.get()
|
||||||
|
.uri("/greet")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isOk()
|
||||||
|
.expectBody(String.class)
|
||||||
|
.isEqualTo("Hello ch4mpy! You are granted with [admin, ROLE_AUTHORIZED_PERSONNEL].");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------------------------------------------*/
|
||||||
|
/* /secured-route */
|
||||||
|
/* This end-point is secured with ".requestMatchers("/secured-route").hasRole("AUTHORIZED_PERSONNEL")" in SecurityConf */
|
||||||
|
/*---------------------------------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithAnonymousUser
|
||||||
|
void givenRequestIsAnonymous_whenGetSecuredRoute_thenUnauthorized() throws Exception {
|
||||||
|
api.get()
|
||||||
|
.uri("/secured-route")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isUnauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockJwtAuth("ROLE_AUTHORIZED_PERSONNEL")
|
||||||
|
void givenUserIsGrantedWithRoleAuthorizedPersonnel_whenGetSecuredRoute_thenOk() throws Exception {
|
||||||
|
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 {
|
||||||
|
api.get()
|
||||||
|
.uri("/secured-route")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------------------------------*/
|
||||||
|
/* /secured-method */
|
||||||
|
/* This end-point is secured with "@PreAuthorize("hasRole('AUTHORIZED_PERSONNEL')")" on @Controller method */
|
||||||
|
/*---------------------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithAnonymousUser
|
||||||
|
void givenRequestIsAnonymous_whenGetSecuredMethod_thenUnauthorized() throws Exception {
|
||||||
|
api.get()
|
||||||
|
.uri("/secured-method")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isUnauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockJwtAuth("ROLE_AUTHORIZED_PERSONNEL")
|
||||||
|
void givenUserIsGrantedWithRoleAuthorizedPersonnel_whenGetSecuredMethod_thenOk() throws Exception {
|
||||||
|
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 {
|
||||||
|
api.get()
|
||||||
|
.uri("/secured-method")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isForbidden();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
package com.baeldung;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
|
||||||
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
|
import org.springframework.security.test.context.support.WithAnonymousUser;
|
||||||
|
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||||
|
|
||||||
|
import com.baeldung.ReactiveResourceServerApplication.GreetingController;
|
||||||
|
import com.baeldung.ReactiveResourceServerApplication.MessageService;
|
||||||
|
import com.c4_soft.springaddons.security.oauth2.test.annotations.OpenIdClaims;
|
||||||
|
import com.c4_soft.springaddons.security.oauth2.test.annotations.WithMockJwtAuth;
|
||||||
|
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
@WebFluxTest(controllers = GreetingController.class, properties = { "server.ssl.enabled=false" })
|
||||||
|
class SpringAddonsGreetingControllerUnitTest {
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
MessageService messageService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
WebTestClient api;
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------------*/
|
||||||
|
/* /greet */
|
||||||
|
/* This end-point secured with ".anyRequest().authenticated()" in SecurityConf */
|
||||||
|
/*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithAnonymousUser
|
||||||
|
void givenRequestIsAnonymous_whenGetGreet_thenUnauthorized() throws Exception {
|
||||||
|
api.get()
|
||||||
|
.uri("/greet")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isUnauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@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));
|
||||||
|
|
||||||
|
api.get()
|
||||||
|
.uri("/greet")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isOk()
|
||||||
|
.expectBody(String.class)
|
||||||
|
.isEqualTo(greeting);
|
||||||
|
|
||||||
|
verify(messageService, times(1)).greet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------------------------------------------*/
|
||||||
|
/* /secured-route */
|
||||||
|
/* This end-point is secured with ".requestMatchers("/secured-route").hasRole("AUTHORIZED_PERSONNEL")" in SecurityConf */
|
||||||
|
/*---------------------------------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithAnonymousUser
|
||||||
|
void givenRequestIsAnonymous_whenGetSecuredRoute_thenUnauthorized() throws Exception {
|
||||||
|
api.get()
|
||||||
|
.uri("/secured-route")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isUnauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockJwtAuth("ROLE_AUTHORIZED_PERSONNEL")
|
||||||
|
void givenUserIsGrantedWithRoleAuthorizedPersonnel_whenGetSecuredRoute_thenOk() throws Exception {
|
||||||
|
final var secret = "Secret!";
|
||||||
|
when(messageService.getSecret()).thenReturn(Mono.just(secret));
|
||||||
|
|
||||||
|
api.get()
|
||||||
|
.uri("/secured-route")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isOk()
|
||||||
|
.expectBody(String.class)
|
||||||
|
.isEqualTo(secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockJwtAuth("admin")
|
||||||
|
void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecuredRoute_thenForbidden() throws Exception {
|
||||||
|
api.get()
|
||||||
|
.uri("/secured-route")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------------------------------*/
|
||||||
|
/* /secured-method */
|
||||||
|
/* This end-point is secured with "@PreAuthorize("hasRole('AUTHORIZED_PERSONNEL')")" on @Controller method */
|
||||||
|
/*---------------------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithAnonymousUser
|
||||||
|
void givenRequestIsAnonymous_whenGetSecuredMethod_thenUnauthorized() throws Exception {
|
||||||
|
api.get()
|
||||||
|
.uri("/secured-method")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isUnauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockJwtAuth("ROLE_AUTHORIZED_PERSONNEL")
|
||||||
|
void givenUserIsGrantedWithRoleAuthorizedPersonnel_whenGetSecuredMethod_thenOk() throws Exception {
|
||||||
|
final var secret = "Secret!";
|
||||||
|
when(messageService.getSecret()).thenReturn(Mono.just(secret));
|
||||||
|
|
||||||
|
api.get()
|
||||||
|
.uri("/secured-method")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isOk()
|
||||||
|
.expectBody(String.class)
|
||||||
|
.isEqualTo(secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockJwtAuth("admin")
|
||||||
|
void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecuredMethod_thenForbidden() throws Exception {
|
||||||
|
api.get()
|
||||||
|
.uri("/secured-method")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
package com.baeldung;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockAuthentication;
|
||||||
|
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockJwt;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
|
||||||
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
|
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||||
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.oauth2.core.oidc.StandardClaimNames;
|
||||||
|
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||||
|
|
||||||
|
import com.baeldung.ReactiveResourceServerApplication.GreetingController;
|
||||||
|
import com.baeldung.ReactiveResourceServerApplication.MessageService;
|
||||||
|
|
||||||
|
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"));
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
MessageService messageService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
WebTestClient api;
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------------*/
|
||||||
|
/* /greet */
|
||||||
|
/* This end-point secured with ".anyRequest().authenticated()" in SecurityConf */
|
||||||
|
/*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenRequestIsAnonymous_whenGetGreet_thenUnauthorized() throws Exception {
|
||||||
|
api.mutateWith(mockAuthentication(ANONYMOUS_AUTHENTICATION))
|
||||||
|
.get()
|
||||||
|
.uri("/greet")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isUnauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenUserIsAuthenticated_whenGetGreet_thenOk() throws Exception {
|
||||||
|
final var greeting = "Whatever the service returns";
|
||||||
|
when(messageService.greet()).thenReturn(Mono.just(greeting));
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------------------------------------------*/
|
||||||
|
/* /secured-route */
|
||||||
|
/* This end-point is secured with ".requestMatchers("/secured-route").hasRole("AUTHORIZED_PERSONNEL")" in SecurityConf */
|
||||||
|
/*---------------------------------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenRequestIsAnonymous_whenGetSecuredRoute_thenUnauthorized() throws Exception {
|
||||||
|
api.mutateWith(mockAuthentication(ANONYMOUS_AUTHENTICATION))
|
||||||
|
.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));
|
||||||
|
|
||||||
|
api.mutateWith(mockJwt().authorities(new SimpleGrantedAuthority("ROLE_AUTHORIZED_PERSONNEL")))
|
||||||
|
.get()
|
||||||
|
.uri("/secured-route")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isOk()
|
||||||
|
.expectBody(String.class)
|
||||||
|
.isEqualTo(secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecuredRoute_thenForbidden() throws Exception {
|
||||||
|
api.mutateWith(mockJwt().authorities(new SimpleGrantedAuthority("admin")))
|
||||||
|
.get()
|
||||||
|
.uri("/secured-route")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------------------------------*/
|
||||||
|
/* /secured-method */
|
||||||
|
/* This end-point is secured with "@PreAuthorize("hasRole('AUTHORIZED_PERSONNEL')")" on @Controller method */
|
||||||
|
/*---------------------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenRequestIsAnonymous_whenGetSecuredMethod_thenUnauthorized() throws Exception {
|
||||||
|
api.mutateWith(mockAuthentication(ANONYMOUS_AUTHENTICATION))
|
||||||
|
.get()
|
||||||
|
.uri("/secured-method")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isUnauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenUserIsGrantedWithRoleAuthorizedPersonnel_whenGetSecuredMethod_thenOk() throws Exception {
|
||||||
|
final var secret = "Secret!";
|
||||||
|
when(messageService.getSecret()).thenReturn(Mono.just(secret));
|
||||||
|
|
||||||
|
api.mutateWith(mockJwt().authorities(new SimpleGrantedAuthority("ROLE_AUTHORIZED_PERSONNEL")))
|
||||||
|
.get()
|
||||||
|
.uri("/secured-method")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isOk()
|
||||||
|
.expectBody(String.class)
|
||||||
|
.isEqualTo(secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecuredMethod_thenForbidden() throws Exception {
|
||||||
|
api.mutateWith(mockJwt().authorities(new SimpleGrantedAuthority("admin")))
|
||||||
|
.get()
|
||||||
|
.uri("/secured-method")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>spring-security-oauth2-testing</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<groupId>com.baeldung.spring-security-modules.testing</groupId>
|
||||||
|
<artifactId>servlet-resource-server</artifactId>
|
||||||
|
<name>servlet-resource-server</name>
|
||||||
|
<description>Demo project for Spring Boot servlet resource-server</description>
|
||||||
|
<properties>
|
||||||
|
<java.version>17</java.version>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.c4-soft.springaddons</groupId>
|
||||||
|
<artifactId>spring-addons-oauth2-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<excludes>
|
||||||
|
<exclude>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
</exclude>
|
||||||
|
</excludes>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
|
@ -0,0 +1,126 @@
|
||||||
|
package com.baeldung;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.oauth2.core.oidc.StandardClaimNames;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class ServletResourceServerApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(ServletResourceServerApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableMethodSecurity
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class SecurityConf {
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http, Converter<Jwt, Collection<GrantedAuthority>> authoritiesConverter) throws Exception {
|
||||||
|
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();
|
||||||
|
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
static interface AuthoritiesConverter extends Converter<Jwt, Collection<GrantedAuthority>> {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
AuthoritiesConverter realmRoles2AuthoritiesConverter() {
|
||||||
|
return (Jwt jwt) -> {
|
||||||
|
final var realmRoles = Optional.of(jwt.getClaimAsMap("realm_access"))
|
||||||
|
.orElse(Map.of());
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final var roles = (List<String>) realmRoles.getOrDefault("roles", List.of());
|
||||||
|
return roles.stream()
|
||||||
|
.map(SimpleGrantedAuthority::new)
|
||||||
|
.map(GrantedAuthority.class::cast)
|
||||||
|
.toList();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public static class MessageService {
|
||||||
|
|
||||||
|
public String greet() {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasRole('AUTHORIZED_PERSONNEL')")
|
||||||
|
public String getSecret() {
|
||||||
|
return "Only authorized personnel can read that";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public static class GreetingController {
|
||||||
|
private final MessageService messageService;
|
||||||
|
|
||||||
|
@GetMapping("/greet")
|
||||||
|
public ResponseEntity<String> greet() {
|
||||||
|
return ResponseEntity.ok(messageService.greet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/secured-route")
|
||||||
|
public ResponseEntity<String> securedRoute() {
|
||||||
|
return ResponseEntity.ok(messageService.getSecret());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/secured-method")
|
||||||
|
@PreAuthorize("hasRole('AUTHORIZED_PERSONNEL')")
|
||||||
|
public ResponseEntity<String> securedMethod() {
|
||||||
|
return ResponseEntity.ok(messageService.getSecret());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://localhost:8443/realms/master
|
|
@ -0,0 +1,79 @@
|
||||||
|
package com.baeldung;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||||
|
import org.springframework.security.test.context.support.WithAnonymousUser;
|
||||||
|
import org.springframework.security.test.context.support.WithMockUser;
|
||||||
|
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
|
|
||||||
|
import com.baeldung.ServletResourceServerApplication.MessageService;
|
||||||
|
import com.c4_soft.springaddons.security.oauth2.test.annotations.OpenIdClaims;
|
||||||
|
import com.c4_soft.springaddons.security.oauth2.test.annotations.WithMockJwtAuth;
|
||||||
|
|
||||||
|
@Import({ MessageService.class })
|
||||||
|
@ExtendWith(SpringExtension.class)
|
||||||
|
@EnableMethodSecurity
|
||||||
|
class MessageServiceUnitTest {
|
||||||
|
@Autowired
|
||||||
|
MessageService messageService;
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
/* greet() */
|
||||||
|
/* Expects a JwtAuthenticationToken to be retrieved from the security-context */
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenSecurityContextIsNotSet_whenGreet_thenThrowsAuthenticationCredentialsNotFoundException() {
|
||||||
|
assertThrows(AuthenticationCredentialsNotFoundException.class, () -> messageService.getSecret());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithAnonymousUser
|
||||||
|
void givenUserIsAnonymous_whenGreet_thenThrowsAccessDeniedException() {
|
||||||
|
assertThrows(AccessDeniedException.class, () -> messageService.getSecret());
|
||||||
|
}
|
||||||
|
|
||||||
|
@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());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(authorities = { "admin", "ROLE_AUTHORIZED_PERSONNEL" }, username = "ch4mpy")
|
||||||
|
void givenSecurityContextIsPopulatedWithUsernamePasswordAuthenticationToken_whenGreet_thenThrowsClassCastException() {
|
||||||
|
assertThrows(ClassCastException.class, () -> messageService.greet());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------*/
|
||||||
|
/* getSecret() */
|
||||||
|
/* is secured with "@PreAuthorize("hasRole('AUTHORIZED_PERSONNEL')")" */
|
||||||
|
/*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithAnonymousUser
|
||||||
|
void givenUserIsAnonymous_whenGetSecret_thenThrowsAccessDeniedException() {
|
||||||
|
assertThrows(AccessDeniedException.class, () -> messageService.getSecret());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockJwtAuth(authorities = { "admin", "ROLE_AUTHORIZED_PERSONNEL" }, claims = @OpenIdClaims(preferredUsername = "ch4mpy"))
|
||||||
|
void givenUserIsGrantedWithRoleAuthorizedPersonnel_whenGetSecret_thenReturnSecret() {
|
||||||
|
assertEquals("Only authorized personnel can read that", messageService.getSecret());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockJwtAuth(authorities = { "admin" }, claims = @OpenIdClaims(preferredUsername = "ch4mpy"))
|
||||||
|
void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecret_thenThrowsAccessDeniedException() {
|
||||||
|
assertThrows(AccessDeniedException.class, () -> messageService.getSecret());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
package com.baeldung;
|
||||||
|
|
||||||
|
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.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||||
|
import org.springframework.security.test.context.support.WithAnonymousUser;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
|
import com.c4_soft.springaddons.security.oauth2.test.annotations.OpenIdClaims;
|
||||||
|
import com.c4_soft.springaddons.security.oauth2.test.annotations.WithMockJwtAuth;
|
||||||
|
|
||||||
|
@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
|
||||||
|
@AutoConfigureMockMvc
|
||||||
|
class ServletResourceServerApplicationIntegrationTest {
|
||||||
|
@Autowired
|
||||||
|
MockMvc api;
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------------*/
|
||||||
|
/* /greet */
|
||||||
|
/* This end-point secured with ".anyRequest().authenticated()" in SecurityConf */
|
||||||
|
/*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithAnonymousUser
|
||||||
|
void givenRequestIsAnonymous_whenGetGreet_thenUnauthorized() throws Exception {
|
||||||
|
api.perform(get("/greet"))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockJwtAuth(authorities = { "admin", "ROLE_AUTHORIZED_PERSONNEL" }, claims = @OpenIdClaims(preferredUsername = "ch4mpy"))
|
||||||
|
void givenUserIsAuthenticated_whenGetGreet_thenOk() throws Exception {
|
||||||
|
api.perform(get("/greet"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().string("Hello ch4mpy! You are granted with [admin, ROLE_AUTHORIZED_PERSONNEL]."));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------------------------------------------*/
|
||||||
|
/* /secured-route */
|
||||||
|
/* This end-point is secured with ".requestMatchers("/secured-route").hasRole("AUTHORIZED_PERSONNEL")" in SecurityConf */
|
||||||
|
/*---------------------------------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithAnonymousUser
|
||||||
|
void givenRequestIsAnonymous_whenGetSecuredRoute_thenUnauthorized() throws Exception {
|
||||||
|
api.perform(get("/secured-route"))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockJwtAuth("ROLE_AUTHORIZED_PERSONNEL")
|
||||||
|
void givenUserIsGrantedWithRoleAuthorizedPersonnel_whenGetSecuredRoute_thenOk() throws Exception {
|
||||||
|
api.perform(get("/secured-route"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().string("Only authorized personnel can read that"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockJwtAuth("admin")
|
||||||
|
void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecuredRoute_thenForbidden() throws Exception {
|
||||||
|
api.perform(get("/secured-route"))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------------------------------*/
|
||||||
|
/* /secured-method */
|
||||||
|
/* This end-point is secured with "@PreAuthorize("hasRole('AUTHORIZED_PERSONNEL')")" on @Controller method */
|
||||||
|
/*---------------------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithAnonymousUser
|
||||||
|
void givenRequestIsAnonymous_whenGetSecuredMethod_thenUnauthorized() throws Exception {
|
||||||
|
api.perform(get("/secured-method"))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockJwtAuth("ROLE_AUTHORIZED_PERSONNEL")
|
||||||
|
void givenUserIsGrantedWithRoleAuthorizedPersonnel_whenGetSecuredMethod_thenOk() throws Exception {
|
||||||
|
api.perform(get("/secured-method"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().string("Only authorized personnel can read that"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockJwtAuth("admin")
|
||||||
|
void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecuredMethod_thenForbidden() throws Exception {
|
||||||
|
api.perform(get("/secured-method"))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
package com.baeldung;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
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.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||||
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
|
import org.springframework.security.test.context.support.WithAnonymousUser;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
|
import com.baeldung.ServletResourceServerApplication.GreetingController;
|
||||||
|
import com.baeldung.ServletResourceServerApplication.MessageService;
|
||||||
|
import com.c4_soft.springaddons.security.oauth2.test.annotations.OpenIdClaims;
|
||||||
|
import com.c4_soft.springaddons.security.oauth2.test.annotations.WithMockJwtAuth;
|
||||||
|
|
||||||
|
@WebMvcTest(controllers = GreetingController.class, properties = { "server.ssl.enabled=false" })
|
||||||
|
class SpringAddonsGreetingControllerUnitTest {
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
MessageService messageService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
MockMvc api;
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------------*/
|
||||||
|
/* /greet */
|
||||||
|
/* This end-point secured with ".anyRequest().authenticated()" in SecurityConf */
|
||||||
|
/*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithAnonymousUser
|
||||||
|
void givenRequestIsAnonymous_whenGetGreet_thenUnauthorized() throws Exception {
|
||||||
|
api.perform(get("/greet"))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@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);
|
||||||
|
|
||||||
|
api.perform(get("/greet"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().string(greeting));
|
||||||
|
|
||||||
|
verify(messageService, times(1)).greet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------------------------------------------*/
|
||||||
|
/* /secured-route */
|
||||||
|
/* This end-point is secured with ".requestMatchers("/secured-route").hasRole("AUTHORIZED_PERSONNEL")" in SecurityConf */
|
||||||
|
/*---------------------------------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithAnonymousUser
|
||||||
|
void givenRequestIsAnonymous_whenGetSecuredRoute_thenUnauthorized() throws Exception {
|
||||||
|
api.perform(get("/secured-route"))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockJwtAuth({ "admin", "ROLE_AUTHORIZED_PERSONNEL" })
|
||||||
|
void givenUserIsGrantedWithRoleAuthorizedPersonnel_whenGetSecuredRoute_thenOk() throws Exception {
|
||||||
|
final var secret = "Secret!";
|
||||||
|
when(messageService.getSecret()).thenReturn(secret);
|
||||||
|
|
||||||
|
api.perform(get("/secured-route"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().string(secret));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockJwtAuth({ "admin" })
|
||||||
|
void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecuredRoute_thenForbidden() throws Exception {
|
||||||
|
api.perform(get("/secured-route"))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------------------------------*/
|
||||||
|
/* /secured-method */
|
||||||
|
/* This end-point is secured with "@PreAuthorize("hasRole('AUTHORIZED_PERSONNEL')")" on @Controller method */
|
||||||
|
/*---------------------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithAnonymousUser
|
||||||
|
void givenRequestIsAnonymous_whenGetSecuredMethod_thenUnauthorized() throws Exception {
|
||||||
|
api.perform(get("/secured-method"))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockJwtAuth({ "admin", "ROLE_AUTHORIZED_PERSONNEL" })
|
||||||
|
void givenUserIsGrantedWithRoleAuthorizedPersonnel_whenGetSecuredMethod_thenOk() throws Exception {
|
||||||
|
final var secret = "Secret!";
|
||||||
|
when(messageService.getSecret()).thenReturn(secret);
|
||||||
|
|
||||||
|
api.perform(get("/secured-method"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().string(secret));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockJwtAuth(authorities = { "admin" })
|
||||||
|
void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecuredMethod_thenForbidden() throws Exception {
|
||||||
|
api.perform(get("/secured-method"))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
package com.baeldung;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.anonymous;
|
||||||
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt;
|
||||||
|
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 java.util.List;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||||
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.oauth2.core.oidc.StandardClaimNames;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
|
import com.baeldung.ServletResourceServerApplication.GreetingController;
|
||||||
|
import com.baeldung.ServletResourceServerApplication.MessageService;
|
||||||
|
|
||||||
|
@WebMvcTest(controllers = GreetingController.class, properties = { "server.ssl.enabled=false" })
|
||||||
|
class SpringSecurityTestGreetingControllerUnitTest {
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
MessageService messageService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
MockMvc api;
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------------*/
|
||||||
|
/* /greet */
|
||||||
|
/* This end-point secured with ".anyRequest().authenticated()" in SecurityConf */
|
||||||
|
/*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenRequestIsAnonymous_whenGetGreet_thenUnauthorized() throws Exception {
|
||||||
|
api.perform(get("/greet").with(anonymous()))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenUserIsAuthenticated_whenGetGreet_thenOk() throws Exception {
|
||||||
|
final var greeting = "Whatever the service returns";
|
||||||
|
when(messageService.greet()).thenReturn(greeting);
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
verify(messageService, times(1)).greet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------------------------------------------*/
|
||||||
|
/* /secured-route */
|
||||||
|
/* This end-point is secured with ".requestMatchers("/secured-route").hasRole("AUTHORIZED_PERSONNEL")" in SecurityConf */
|
||||||
|
/*---------------------------------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenRequestIsAnonymous_whenGetSecuredRoute_thenUnauthorized() throws Exception {
|
||||||
|
api.perform(get("/secured-route").with(anonymous()))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenUserIsGrantedWithRoleAuthorizedPersonnel_whenGetSecuredRoute_thenOk() throws Exception {
|
||||||
|
final var secret = "Secret!";
|
||||||
|
when(messageService.getSecret()).thenReturn(secret);
|
||||||
|
|
||||||
|
api.perform(get("/secured-route").with(jwt().authorities(new SimpleGrantedAuthority("ROLE_AUTHORIZED_PERSONNEL"))))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().string(secret));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecuredRoute_thenForbidden() throws Exception {
|
||||||
|
api.perform(get("/secured-route").with(jwt().authorities(new SimpleGrantedAuthority("admin"))))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------------------------------*/
|
||||||
|
/* /secured-method */
|
||||||
|
/* This end-point is secured with "@PreAuthorize("hasRole('AUTHORIZED_PERSONNEL')")" on @Controller method */
|
||||||
|
/*---------------------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenRequestIsAnonymous_whenGetSecuredMethod_thenUnauthorized() throws Exception {
|
||||||
|
api.perform(get("/secured-method").with(anonymous()))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenUserIsGrantedWithRoleAuthorizedPersonnel_whenGetSecuredMethod_thenOk() throws Exception {
|
||||||
|
final var secret = "Secret!";
|
||||||
|
when(messageService.getSecret()).thenReturn(secret);
|
||||||
|
|
||||||
|
api.perform(get("/secured-method").with(jwt().authorities(new SimpleGrantedAuthority("ROLE_AUTHORIZED_PERSONNEL"))))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().string(secret));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenUserIsNotGrantedWithRoleAuthorizedPersonnel_whenGetSecuredMethod_thenForbidden() throws Exception {
|
||||||
|
api.perform(get("/secured-method").with(jwt().authorities(new SimpleGrantedAuthority("admin"))))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue