This commit is contained in:
2026-09-29 17:55:54 +08:00
parent 41d8387cd5
commit 54ad26c607
111 changed files with 1837 additions and 962 deletions
@@ -1,49 +0,0 @@
package com.baiyue.common.security;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.oauth2.jwt.Jwt;
import java.time.Instant;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
class GatewayAccessTokenSessionValidatorTest {
private final GatewayAccessTokenSessionValidator validator =
new GatewayAccessTokenSessionValidator("gateway-secret");
@Test void acceptsOnlyGatewayAuthenticatedAuthorizationSnapshot() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader(GatewayAccessTokenSessionValidator.TOKEN_HEADER, "gateway-secret");
request.addHeader(GatewayAccessTokenSessionValidator.ROLES_HEADER, "PLATFORM_ADMIN");
request.addHeader(GatewayAccessTokenSessionValidator.AUTHORITIES_HEADER,
"platform:overseas-warehouse:last-mile-order:list,platform:finance:fee-source:view");
AccessTokenSession session = validator.validate(jwt(), request);
assertThat(session.userId()).isEqualTo(42L);
assertThat(session.roles()).containsExactly("PLATFORM_ADMIN");
assertThat(session.permissions()).containsExactlyInAnyOrder(
"platform:overseas-warehouse:last-mile-order:list", "platform:finance:fee-source:view");
}
@Test void rejectsForgedOrMissingGatewayToken() {
MockHttpServletRequest forged = new MockHttpServletRequest();
forged.addHeader(GatewayAccessTokenSessionValidator.TOKEN_HEADER, "forged");
forged.addHeader(GatewayAccessTokenSessionValidator.AUTHORITIES_HEADER,
"platform:overseas-warehouse:last-mile-order:list");
assertThatThrownBy(() -> validator.validate(jwt(), forged))
.isInstanceOf(IllegalArgumentException.class).hasMessageContaining("Invalid overseas gateway");
assertThatThrownBy(() -> validator.validate(jwt(), new MockHttpServletRequest()))
.isInstanceOf(IllegalArgumentException.class).hasMessageContaining("Missing overseas gateway");
}
private Jwt jwt() {
return Jwt.withTokenValue("small-overseas-token").header("alg", "RS256").subject("42")
.issuedAt(Instant.now()).expiresAt(Instant.now().plusSeconds(300))
.claim("tenant_id", 0L).claim("app_code", "PLATFORM")
.claim("permission_version", 7L).claim("token_version", 1).build();
}
}
@@ -29,7 +29,8 @@ class JwtAuthenticationFilterTest {
.claim("user_id", 1L).build();
JwtDecoder decoder = token -> jwt;
JwtAuthenticationFilter filter = new JwtAuthenticationFilter(decoder, new JwtClaimsMapper(),
new JsonAuthenticationEntryPoint(new ObjectMapper()));
new JsonAuthenticationEntryPoint(new ObjectMapper()),
token -> new AccessTokenSession(1, 1L, java.util.Set.of(), java.util.Set.of()));
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader(HttpHeaders.AUTHORIZATION, "Bearer valid");
MockHttpServletResponse response = new MockHttpServletResponse();
@@ -43,20 +44,25 @@ class JwtAuthenticationFilterTest {
}
@Test
void authenticatesMinimalOverseasTokenWithGatewaySnapshot() throws Exception {
void authenticatesUnifiedTokenWithServerSessionIgnoringGatewayHeaders() throws Exception {
Jwt jwt = Jwt.withTokenValue("overseas").header("alg", "RS256").subject("42")
.issuedAt(Instant.now()).expiresAt(Instant.now().plusSeconds(60))
.claim("tenant_id", 0L).claim("app_code", "PLATFORM")
.claim("jti", "session-42").claim("user_id", 42L).claim("tenant_id", 0L).claim("app_code", "PLATFORM")
.claim("permission_version", 7L).claim("token_version", 1).build();
var redis = org.mockito.Mockito.mock(org.springframework.data.redis.core.StringRedisTemplate.class);
var values = org.mockito.Mockito.mock(org.springframework.data.redis.core.ValueOperations.class);
org.mockito.Mockito.when(redis.opsForValue()).thenReturn(values);
org.mockito.Mockito.when(values.get("baiyue:auth:access:session-42"))
.thenReturn("{\"version\":1,\"userId\":42,\"roles\":[\"PLATFORM_ADMIN\"],\"permissions\":[\"platform:lastleg:view\"]}");
JwtAuthenticationFilter filter = new JwtAuthenticationFilter(token -> jwt, new JwtClaimsMapper(),
new JsonAuthenticationEntryPoint(new ObjectMapper()),
new GatewayAccessTokenSessionValidator("gateway-secret"));
new RedisAccessTokenSessionValidator(redis, "baiyue:auth"));
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI("/api/platform/v1/last-leg/orders");
request.addHeader(HttpHeaders.AUTHORIZATION, "Bearer overseas");
request.addHeader(GatewayAccessTokenSessionValidator.TOKEN_HEADER, "gateway-secret");
request.addHeader(GatewayAccessTokenSessionValidator.ROLES_HEADER, "PLATFORM_ADMIN");
request.addHeader(GatewayAccessTokenSessionValidator.AUTHORITIES_HEADER, "platform:lastleg:view");
request.addHeader("X-Internal-Service-Token", "gateway-secret");
request.addHeader("X-Overseas-Roles", "FORGED_ADMIN");
request.addHeader("X-Overseas-Authorities", "platform:wallet:write");
MockHttpServletResponse response = new MockHttpServletResponse();
AtomicBoolean invoked = new AtomicBoolean();
@@ -64,7 +70,8 @@ class JwtAuthenticationFilterTest {
var authentication = SecurityContextHolder.getContext().getAuthentication();
assertThat(authentication).isNotNull();
assertThat(authentication.getAuthorities()).extracting("authority")
.contains("APP_PLATFORM", "ROLE_PLATFORM_ADMIN", "platform:lastleg:view");
.contains("APP_PLATFORM", "ROLE_PLATFORM_ADMIN", "platform:lastleg:view")
.doesNotContain("ROLE_FORGED_ADMIN", "platform:wallet:write");
invoked.set(true);
});
@@ -0,0 +1,22 @@
package com.baiyue.common.security;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.support.StaticListableBeanFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
class UnifiedSessionModeTest {
@Test
void rejectsLegacySessionModesAndMissingRedisInsteadOfTrustingClaimsOrHeaders() {
var configuration = new BaiyueSecurityAutoConfiguration();
var beans = new StaticListableBeanFactory();
var redis = beans.getBeanProvider(StringRedisTemplate.class);
for (String mode : new String[]{"claims", "gateway", "unknown", "redis"}) {
assertThatThrownBy(() -> configuration.jwtAuthenticationFilter(token -> null,
new JwtClaimsMapper(), new JsonAuthenticationEntryPoint(new ObjectMapper()),
redis, new ObjectMapper(), "baiyue:auth", mode))
.isInstanceOf(IllegalStateException.class);
}
}
}