暂存提交

This commit is contained in:
2026-07-30 18:01:36 +08:00
commit 2f4e159356
368 changed files with 20370 additions and 0 deletions
@@ -0,0 +1,42 @@
package com.baiyue.common.security;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import java.time.Instant;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.assertj.core.api.Assertions.assertThat;
class JwtAuthenticationFilterTest {
@AfterEach
void clearContext() { SecurityContextHolder.clearContext(); }
@Test
void authenticatesValidBearerDuringFilterChain() throws Exception {
Jwt jwt = Jwt.withTokenValue("valid").header("alg", "none").subject("alice")
.issuedAt(Instant.now()).expiresAt(Instant.now().plusSeconds(60))
.claim("user_id", 1L).build();
JwtDecoder decoder = token -> jwt;
JwtAuthenticationFilter filter = new JwtAuthenticationFilter(decoder, new JwtClaimsMapper(),
new JsonAuthenticationEntryPoint(new ObjectMapper()));
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader(HttpHeaders.AUTHORIZATION, "Bearer valid");
MockHttpServletResponse response = new MockHttpServletResponse();
AtomicBoolean invoked = new AtomicBoolean();
filter.doFilter(request, response, (request1, response1) -> {
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();
invoked.set(true);
});
assertThat(invoked).isTrue();
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();
}
}
@@ -0,0 +1,50 @@
package com.baiyue.common.security;
import org.junit.jupiter.api.Test;
import org.springframework.security.oauth2.jwt.Jwt;
import java.time.Instant;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
class JwtClaimsMapperTest {
@Test
void mapsTenantUserRolesAndPermissions() {
Jwt jwt = Jwt.withTokenValue("token")
.header("alg", "none")
.subject("subject")
.issuedAt(Instant.now())
.expiresAt(Instant.now().plusSeconds(60))
.claim("user_id", 42L)
.claim("tenant_id", "7")
.claim("username", "alice")
.claim("app_code", "MERCHANT")
.claim("client_type", "MERCHANT")
.claim("permissions", List.of("order:read", "order:create"))
.claim("roles", List.of("ADMIN"))
.build();
BaiyueJwtAuthenticationToken authentication = new JwtClaimsMapper().map(jwt);
assertThat(authentication.getPrincipal().userId()).isEqualTo(42L);
assertThat(authentication.getPrincipal().tenantId()).isEqualTo(7L);
assertThat(authentication.getAuthorities()).extracting("authority")
.contains("order:read", "order:create", "ROLE_ADMIN", "APP_MERCHANT");
}
@Test
void fallsBackToNumericSubjectForUserId() {
Jwt jwt = Jwt.withTokenValue("token")
.header("alg", "none")
.subject("99")
.issuedAt(Instant.now())
.expiresAt(Instant.now().plusSeconds(60))
.claim("tenant_id", 0L)
.claim("app_code", "PLATFORM")
.build();
BaiyueJwtAuthenticationToken authentication = new JwtClaimsMapper().map(jwt);
assertThat(authentication.getPrincipal().userId()).isEqualTo(99L);
assertThat(authentication.getAuthorities()).extracting("authority").contains("APP_PLATFORM");
}
}
@@ -0,0 +1,53 @@
package com.baiyue.common.security;
import org.junit.jupiter.api.Test;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.oauth2.jwt.Jwt;
import java.time.Instant;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
class RedisAccessTokenSessionValidatorTest {
@Test
void acceptsActiveSession() {
StringRedisTemplate redis = mock(StringRedisTemplate.class);
when(redis.hasKey("baiyue:auth:access:jti-1")).thenReturn(true);
assertThatCode(() -> new RedisAccessTokenSessionValidator(redis, "baiyue:auth")
.validate(jwt("jti-1"))).doesNotThrowAnyException();
}
@Test
void rejectsRevokedSession() {
StringRedisTemplate redis = mock(StringRedisTemplate.class);
when(redis.hasKey("baiyue:auth:access:jti-1")).thenReturn(false);
assertThatThrownBy(() -> new RedisAccessTokenSessionValidator(redis, "baiyue:auth")
.validate(jwt("jti-1")))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("revoked or expired");
}
@Test
void failsClosedWhenRedisIsUnavailable() {
StringRedisTemplate redis = mock(StringRedisTemplate.class);
when(redis.hasKey("baiyue:auth:access:jti-1"))
.thenThrow(new RedisConnectionFailureException("offline"));
assertThatThrownBy(() -> new RedisAccessTokenSessionValidator(redis, "baiyue:auth")
.validate(jwt("jti-1")))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("unavailable");
}
private Jwt jwt(String jwtId) {
return Jwt.withTokenValue("token").header("alg", "none")
.subject("1").claim("jti", jwtId).issuedAt(Instant.now())
.expiresAt(Instant.now().plusSeconds(60)).build();
}
}