This commit is contained in:
2026-08-25 14:46:06 +08:00
parent 5abc7543ae
commit f325d57fc1
106 changed files with 3682 additions and 450 deletions
@@ -0,0 +1,83 @@
package com.baiyue.gateway.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.time.Instant;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
class OverseasSecurityProjectionFilterTest {
private final OverseasSecurityValidationClient client = mock(OverseasSecurityValidationClient.class);
private final OverseasSecurityProjectionFilter filter = new OverseasSecurityProjectionFilter(client, new ObjectMapper());
@Test void disabledUserFailsWithA0401() {
when(client.validate(42, 0, 3, 8)).thenReturn(Mono.just(
new OverseasSecurityValidationClient.Result(false, "USER_DISABLED")));
var exchange = exchange("/api/platform/v1/last-leg/orders");
StepVerifier.create(filter.filter(exchange, ignored -> Mono.empty())).verifyComplete();
assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
assertThat(body(exchange)).contains("A0401");
}
@Test void disabledTenantAndStalePermissionFailWithA0403() {
for (String reason : new String[]{"TENANT_DISABLED", "PERMISSION_VERSION_STALE"}) {
when(client.validate(42, 0, 3, 8)).thenReturn(Mono.just(
new OverseasSecurityValidationClient.Result(false, reason)));
var exchange = exchange("/api/merchant/v1/last-leg/orders");
StepVerifier.create(filter.filter(exchange, ignored -> Mono.empty())).verifyComplete();
assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
assertThat(body(exchange)).contains("A0403");
}
}
@Test void projectionUnavailableFailsClosed() {
when(client.validate(42, 0, 3, 8)).thenReturn(Mono.error(new IllegalStateException("down")));
var exchange = exchange("/api/platform/v1/last-leg/orders");
StepVerifier.create(filter.filter(exchange, ignored -> Mono.empty())).verifyComplete();
assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
@Test void acceptedSecurityStateContinuesAndNonApiPathsDoNotCallProjection() {
when(client.validate(42, 0, 3, 8)).thenReturn(Mono.just(
new OverseasSecurityValidationClient.Result(true, null)));
AtomicBoolean routed = new AtomicBoolean();
StepVerifier.create(filter.filter(exchange("/api/platform/v1/last-leg/orders"), ignored -> {
routed.set(true); return Mono.empty();
})).verifyComplete();
assertThat(routed).isTrue();
var isolatedClient = mock(OverseasSecurityValidationClient.class);
var isolatedFilter = new OverseasSecurityProjectionFilter(isolatedClient, new ObjectMapper());
StepVerifier.create(isolatedFilter.filter(exchange("/actuator/health"), ignored -> Mono.empty())).verifyComplete();
verifyNoInteractions(isolatedClient);
}
private static ServerWebExchange exchange(String path) {
Jwt jwt = Jwt.withTokenValue("verified").header("alg", "RS256").subject("42")
.issuedAt(Instant.now()).expiresAt(Instant.now().plusSeconds(300))
.claim("tenant_id", 0L).claim("token_version", 3L).claim("permission_version", 8L).build();
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(path).build());
return exchange.mutate().principal(Mono.just(new JwtAuthenticationToken(jwt,
List.of(new SimpleGrantedAuthority("ROLE_PLATFORM_USER"))))).build();
}
private static String body(ServerWebExchange exchange) {
return ((org.springframework.mock.http.server.reactive.MockServerHttpResponse) exchange.getResponse())
.getBodyAsString().block();
}
}