feat: extend operation log service and repository with pagination support

This commit is contained in:
张翔
2026-03-18 22:34:43 +08:00
parent 157aee2ffc
commit 8a0cd64829
81 changed files with 8842 additions and 509 deletions
+25
View File
@@ -65,6 +65,11 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
@@ -76,6 +81,26 @@
<mainClass>cn.novalon.manage.gateway.GatewayApplication</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.12</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1 @@
cn.novalon.manage.gateway.config.RateLimitConfig
@@ -0,0 +1,309 @@
package cn.novalon.manage.gateway.filter;
import cn.novalon.manage.gateway.util.JwtUtil;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class GatewayJwtAuthenticationFilterTest {
@Mock
private JwtUtil jwtUtil;
@Mock
private GatewayFilterChain chain;
private JwtAuthenticationFilter filter;
private ServerWebExchange exchange;
@BeforeEach
void setUp() {
filter = new JwtAuthenticationFilter(jwtUtil);
}
@Test
void testPublicPath_AllowAccess() {
MockServerHttpRequest request = MockServerHttpRequest.get("/api/auth/login").build();
exchange = MockServerWebExchange.from(request);
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
verify(chain).filter(any(ServerWebExchange.class));
verify(jwtUtil, never()).validateToken(anyString());
}
@Test
void testPublicPath_Register() {
MockServerHttpRequest request = MockServerHttpRequest.post("/api/auth/register").build();
exchange = MockServerWebExchange.from(request);
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
verify(chain).filter(any(ServerWebExchange.class));
verify(jwtUtil, never()).validateToken(anyString());
}
@Test
void testPublicPath_ActuatorHealth() {
MockServerHttpRequest request = MockServerHttpRequest.get("/actuator/health").build();
exchange = MockServerWebExchange.from(request);
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
verify(chain).filter(any(ServerWebExchange.class));
verify(jwtUtil, never()).validateToken(anyString());
}
@Test
void testPublicPath_ActuatorInfo() {
MockServerHttpRequest request = MockServerHttpRequest.get("/actuator/info").build();
exchange = MockServerWebExchange.from(request);
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
verify(chain).filter(any(ServerWebExchange.class));
verify(jwtUtil, never()).validateToken(anyString());
}
@Test
void testProtectedPath_NoAuthHeader() {
MockServerHttpRequest request = MockServerHttpRequest.get("/api/users").build();
exchange = MockServerWebExchange.from(request);
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
assert exchange.getResponse().getStatusCode() == HttpStatus.UNAUTHORIZED;
verify(chain, never()).filter(any(ServerWebExchange.class));
verify(jwtUtil, never()).validateToken(anyString());
}
@Test
void testProtectedPath_InvalidAuthHeader() {
MockServerHttpRequest request = MockServerHttpRequest.get("/api/users")
.header(HttpHeaders.AUTHORIZATION, "InvalidToken")
.build();
exchange = MockServerWebExchange.from(request);
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
assert exchange.getResponse().getStatusCode() == HttpStatus.UNAUTHORIZED;
verify(chain, never()).filter(any(ServerWebExchange.class));
verify(jwtUtil, never()).validateToken(anyString());
}
@Test
void testProtectedPath_WithBearerPrefix() {
String validToken = "valid.jwt.token";
MockServerHttpRequest request = MockServerHttpRequest.get("/api/users")
.header(HttpHeaders.AUTHORIZATION, "Bearer " + validToken)
.build();
exchange = MockServerWebExchange.from(request);
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
when(jwtUtil.validateToken(validToken)).thenReturn(true);
when(jwtUtil.isTokenExpired(validToken)).thenReturn(false);
when(jwtUtil.getUsernameFromToken(validToken)).thenReturn("testuser");
when(jwtUtil.getUserIdFromToken(validToken)).thenReturn(1L);
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
verify(jwtUtil).validateToken(validToken);
verify(jwtUtil).isTokenExpired(validToken);
verify(jwtUtil).getUsernameFromToken(validToken);
verify(jwtUtil).getUserIdFromToken(validToken);
verify(chain).filter(any(ServerWebExchange.class));
}
@Test
void testProtectedPath_InvalidToken() {
String invalidToken = "invalid.jwt.token";
MockServerHttpRequest request = MockServerHttpRequest.get("/api/users")
.header(HttpHeaders.AUTHORIZATION, "Bearer " + invalidToken)
.build();
exchange = MockServerWebExchange.from(request);
when(jwtUtil.validateToken(invalidToken)).thenReturn(false);
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
assert exchange.getResponse().getStatusCode() == HttpStatus.UNAUTHORIZED;
verify(jwtUtil).validateToken(invalidToken);
verify(jwtUtil, never()).isTokenExpired(anyString());
verify(chain, never()).filter(any(ServerWebExchange.class));
}
@Test
void testProtectedPath_ExpiredToken() {
String expiredToken = "expired.jwt.token";
MockServerHttpRequest request = MockServerHttpRequest.get("/api/users")
.header(HttpHeaders.AUTHORIZATION, "Bearer " + expiredToken)
.build();
exchange = MockServerWebExchange.from(request);
when(jwtUtil.validateToken(expiredToken)).thenReturn(true);
when(jwtUtil.isTokenExpired(expiredToken)).thenReturn(true);
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
assert exchange.getResponse().getStatusCode() == HttpStatus.UNAUTHORIZED;
verify(jwtUtil).validateToken(expiredToken);
verify(jwtUtil).isTokenExpired(expiredToken);
verify(chain, never()).filter(any(ServerWebExchange.class));
}
@Test
void testProtectedPath_ValidToken() {
String validToken = "valid.jwt.token";
MockServerHttpRequest request = MockServerHttpRequest.get("/api/users/1")
.header(HttpHeaders.AUTHORIZATION, "Bearer " + validToken)
.build();
exchange = MockServerWebExchange.from(request);
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
when(jwtUtil.validateToken(validToken)).thenReturn(true);
when(jwtUtil.isTokenExpired(validToken)).thenReturn(false);
when(jwtUtil.getUsernameFromToken(validToken)).thenReturn("testuser");
when(jwtUtil.getUserIdFromToken(validToken)).thenReturn(1L);
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
verify(jwtUtil).validateToken(validToken);
verify(jwtUtil).isTokenExpired(validToken);
verify(jwtUtil).getUsernameFromToken(validToken);
verify(jwtUtil).getUserIdFromToken(validToken);
verify(chain).filter(any(ServerWebExchange.class));
}
@Test
void testHeadersAdded_ValidToken() {
String validToken = "valid.jwt.token";
MockServerHttpRequest request = MockServerHttpRequest.get("/api/users")
.header(HttpHeaders.AUTHORIZATION, "Bearer " + validToken)
.build();
exchange = MockServerWebExchange.from(request);
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
when(jwtUtil.validateToken(validToken)).thenReturn(true);
when(jwtUtil.isTokenExpired(validToken)).thenReturn(false);
when(jwtUtil.getUsernameFromToken(validToken)).thenReturn("testuser");
when(jwtUtil.getUserIdFromToken(validToken)).thenReturn(1L);
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
ServerHttpRequest modifiedRequest = exchange.getRequest();
assert modifiedRequest.getHeaders().getFirst("X-User-Id").equals("1");
assert modifiedRequest.getHeaders().getFirst("X-Username").equals("testuser");
verify(chain).filter(any(ServerWebExchange.class));
}
@Test
void testMixedPath_AuthPath() {
MockServerHttpRequest request = MockServerHttpRequest.get("/api/auth/logout").build();
exchange = MockServerWebExchange.from(request);
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
verify(chain).filter(any(ServerWebExchange.class));
verify(jwtUtil, never()).validateToken(anyString());
}
@Test
void testActuatorPath_Metrics() {
String validToken = "valid.jwt.token";
MockServerHttpRequest request = MockServerHttpRequest.get("/actuator/metrics")
.header(HttpHeaders.AUTHORIZATION, "Bearer " + validToken)
.build();
exchange = MockServerWebExchange.from(request);
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
when(jwtUtil.validateToken(validToken)).thenReturn(true);
when(jwtUtil.isTokenExpired(validToken)).thenReturn(false);
when(jwtUtil.getUsernameFromToken(validToken)).thenReturn("testuser");
when(jwtUtil.getUserIdFromToken(validToken)).thenReturn(1L);
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
verify(jwtUtil).validateToken(validToken);
verify(jwtUtil).isTokenExpired(validToken);
verify(jwtUtil).getUsernameFromToken(validToken);
verify(jwtUtil).getUserIdFromToken(validToken);
verify(chain).filter(any(ServerWebExchange.class));
}
}
@@ -0,0 +1,255 @@
package cn.novalon.manage.gateway.filter;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class RbacAuthorizationFilterTest {
@Mock
private GatewayFilterChain chain;
private RbacAuthorizationFilter filter;
private ServerWebExchange exchange;
@BeforeEach
void setUp() {
filter = new RbacAuthorizationFilter();
}
@Test
void testPublicPath_AllowAccess() {
MockServerHttpRequest request = MockServerHttpRequest.get("/api/auth/login").build();
exchange = MockServerWebExchange.from(request);
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
verify(chain).filter(any(ServerWebExchange.class));
}
@Test
void testPublicPath_Register() {
MockServerHttpRequest request = MockServerHttpRequest.post("/api/auth/register").build();
exchange = MockServerWebExchange.from(request);
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
verify(chain).filter(any(ServerWebExchange.class));
}
@Test
void testPublicPath_ActuatorHealth() {
MockServerHttpRequest request = MockServerHttpRequest.get("/actuator/health").build();
exchange = MockServerWebExchange.from(request);
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
verify(chain).filter(any(ServerWebExchange.class));
}
@Test
void testPublicPath_ActuatorInfo() {
MockServerHttpRequest request = MockServerHttpRequest.get("/actuator/info").build();
exchange = MockServerWebExchange.from(request);
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
verify(chain).filter(any(ServerWebExchange.class));
}
@Test
void testProtectedPath_NoUserId() {
MockServerHttpRequest request = MockServerHttpRequest.get("/api/users").build();
exchange = MockServerWebExchange.from(request);
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
assert exchange.getResponse().getStatusCode() == HttpStatus.UNAUTHORIZED;
verify(chain, never()).filter(any(ServerWebExchange.class));
}
@Test
void testProtectedPath_WithUserId() {
MockServerHttpRequest request = MockServerHttpRequest.get("/api/users")
.header("X-User-Id", "1")
.build();
exchange = MockServerWebExchange.from(request);
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
verify(chain).filter(any(ServerWebExchange.class));
}
@Test
void testProtectedPath_PostMethod() {
MockServerHttpRequest request = MockServerHttpRequest.post("/api/users")
.header("X-User-Id", "1")
.build();
exchange = MockServerWebExchange.from(request);
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
verify(chain).filter(any(ServerWebExchange.class));
}
@Test
void testProtectedPath_PutMethod() {
MockServerHttpRequest request = MockServerHttpRequest.put("/api/users/1")
.header("X-User-Id", "1")
.build();
exchange = MockServerWebExchange.from(request);
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
verify(chain).filter(any(ServerWebExchange.class));
}
@Test
void testProtectedPath_DeleteMethod() {
MockServerHttpRequest request = MockServerHttpRequest.delete("/api/users/1")
.header("X-User-Id", "1")
.build();
exchange = MockServerWebExchange.from(request);
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
verify(chain).filter(any(ServerWebExchange.class));
}
@Test
void testProtectedPath_EmptyUserId() {
MockServerHttpRequest request = MockServerHttpRequest.get("/api/users")
.header("X-User-Id", "")
.build();
exchange = MockServerWebExchange.from(request);
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
verify(chain).filter(any(ServerWebExchange.class));
}
@Test
void testMixedPath_AuthPath() {
MockServerHttpRequest request = MockServerHttpRequest.get("/api/auth/logout").build();
exchange = MockServerWebExchange.from(request);
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
verify(chain).filter(any(ServerWebExchange.class));
}
@Test
void testMixedPath_UserPath() {
MockServerHttpRequest request = MockServerHttpRequest.get("/api/users/profile")
.header("X-User-Id", "1")
.build();
exchange = MockServerWebExchange.from(request);
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
verify(chain).filter(any(ServerWebExchange.class));
}
@Test
void testActuatorPath_Metrics() {
MockServerHttpRequest request = MockServerHttpRequest.get("/actuator/metrics")
.header("X-User-Id", "1")
.build();
exchange = MockServerWebExchange.from(request);
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
.filter(exchange, chain);
StepVerifier.create(result)
.verifyComplete();
verify(chain).filter(any(ServerWebExchange.class));
}
}