chore: update
Java CI with Maven / build-and-test (pull_request) Successful in 10m53s

This commit is contained in:
TakahashiNg
2026-04-09 02:54:21 +00:00
parent c5cfcd0443
commit 666d964cf9
4 changed files with 173 additions and 41 deletions
@@ -1,6 +1,7 @@
package com.drinkool.filters;
import com.drinkool.InternalValue;
import io.smallrye.mutiny.Uni;
import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.container.*;
import jakarta.ws.rs.core.*;
@@ -27,7 +28,9 @@ public class InternalHeaderGatewayFilter {
}
@ServerRequestFilter(preMatching = true)
public void handleRequestHeaders(ContainerRequestContext requestContext) {
public Uni<Response> handleRequestHeaders(
ContainerRequestContext requestContext
) {
requestContext
.getHeaders()
.keySet()
@@ -40,9 +43,10 @@ public class InternalHeaderGatewayFilter {
Map<String, Cookie> cookies = requestContext.getCookies();
Cookie authCookie = cookies.get(InternalValue.cookieName);
try {
if (authCookie == null) return;
if (authCookie == null) return Uni.createFrom().nullItem();
return Uni.createFrom().item(() -> {
try {
String token = authCookie.getValue();
JwtConsumer jwtConsumer = new JwtConsumerBuilder()
@@ -62,12 +66,13 @@ public class InternalHeaderGatewayFilter {
}
});
} catch (Exception e) {
requestContext.abortWith(
Response.status(Response.Status.UNAUTHORIZED)
return Response.status(Response.Status.UNAUTHORIZED)
.entity("InvalidToken")
.build()
);
.build();
}
return null;
});
}
@ServerResponseFilter(priority = Priorities.USER + 100)
@@ -75,20 +80,24 @@ public class InternalHeaderGatewayFilter {
JwtClaims claims = new JwtClaims();
boolean hasInternalData = false;
Map<String, List<Object>> headers = responseContext.getHeaders();
Set<String> headerNames = new HashSet<>(
responseContext.getHeaders().keySet()
);
MultivaluedMap<String, Object> headers = responseContext.getHeaders();
for (String key : headerNames) {
List<String> keysToRemove = new ArrayList<>();
for (String key : headers.keySet()) {
if (key.toLowerCase().startsWith(InternalValue.headerId.toLowerCase())) {
Object value = headers.get(key).get(0);
Object value = headers.getFirst(key);
if (value != null) {
String claimKey = key.substring(InternalValue.headerId.length());
claims.setClaim(claimKey, value.toString());
hasInternalData = true;
}
keysToRemove.add(key);
}
}
for (String key : keysToRemove) {
headers.remove(key);
}
if (hasInternalData) {
@@ -113,14 +122,7 @@ public class InternalHeaderGatewayFilter {
.maxAge(3600)
.build();
responseContext.getHeaders().add("Set-Cookie", jwtCookie);
responseContext
.getHeaders()
.keySet()
.removeIf(key ->
key.toLowerCase().startsWith(InternalValue.cookieName)
);
headers.add(HttpHeaders.SET_COOKIE, jwtCookie);
} catch (Exception e) {
e.printStackTrace();
}
@@ -0,0 +1,97 @@
package com.drinkool.filters;
import static io.restassured.RestAssured.*;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.jupiter.api.Assertions.*;
import com.drinkool.InternalValue;
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.response.Response;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.keys.HmacKey;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@QuarkusTest
public class InternalHeaderGatewayFilterTest {
@ConfigProperty(name = "gateway.jwt.secret")
String TEST_SECRET;
private String createTestToken(String userId) throws Exception {
JwtClaims claims = new JwtClaims();
claims.setExpirationTimeMinutesInTheFuture(10);
claims.setClaim("UserId", userId);
claims.setClaim("Role", "user");
JsonWebSignature jws = new JsonWebSignature();
jws.setPayload(claims.toJson());
jws.setKey(new HmacKey(TEST_SECRET.getBytes()));
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.HMAC_SHA256);
return jws.getCompactSerialization();
}
@Test
@DisplayName("Nên xóa header X-Internal nếu client tự ý gửi lên")
public void testShouldRemoveClentInternalHeaders() {
given()
.header("X-Internal-userId", "hacker-id")
.when()
.get("/test-gateway")
.then()
.statusCode(200)
.body(is("not-found")); // Filter phải xóa sạch trước khi vào Resource
}
@Test
@DisplayName("Nên trích xuất JWT từ Cookie và gắn vào Header nội bộ")
public void testValidJwtInCookie() throws Exception {
String token = createTestToken("user123");
given()
.cookie(InternalValue.cookieName, token)
.when()
.get("/test-gateway")
.then()
.statusCode(200)
.body(is("user123")); // Resource nhận được userId từ Header do Filter gắn vào
}
@Test
@DisplayName("Nên trả về 401 nếu JWT sai chữ ký")
public void testInvalidSignature() {
given()
.cookie(InternalValue.cookieName, "invalid.token.string")
.when()
.get("/test-gateway")
.then()
.statusCode(401)
.body(containsString("InvalidToken"));
}
@Test
@DisplayName(
"Nên đóng gói X-Internal headers từ service thành JWT gửi về client"
)
public void testResponseFilterConvertsHeadersToJwt() throws Exception {
String token = createTestToken("user123");
Response response = given()
.cookie(InternalValue.cookieName, token)
.when()
.post("/test-gateway/update")
.then()
.statusCode(200)
.cookie(InternalValue.cookieName) // Kiểm tra có Set-Cookie trả về không
.header("X-Internal-newStatus", nullValue()) // Header nội bộ phải bị xóa khỏi response
.extract()
.response();
String setCookie = response.getHeader("Set-Cookie");
assertTrue(setCookie.contains("HttpOnly"));
assertTrue(setCookie.contains("SameSite=Strict"));
}
}
@@ -0,0 +1,28 @@
package com.drinkool.utils;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.Response;
@Path("/test-gateway")
public class TestResource {
@GET
public Response getHeaders(@Context HttpHeaders headers) {
// Trả về header nội bộ để chúng ta kiểm tra xem Filter có gắn đúng không
String userId = headers.getHeaderString("X-Internal-userId");
return Response.ok().entity(userId != null ? userId : "not-found").build();
}
@POST
@Path("/update")
public Response updateInternal(
@HeaderParam("X-Internal-userId") String userId
) {
// Giả lập service trả về header nội bộ để Gateway đóng gói vào JWT
return Response.ok("updated")
.header("X-Internal-newStatus", "active")
.build();
}
}
@@ -0,0 +1,5 @@
# Quarkus
quarkus.http.host=0.0.0.0
# JWT
gateway.jwt.secret=1uZk07Hqu1316z9YqrcSGPTTJDhMWU1ZhFSLBrDQvmU=