From a8ed99f134e9076ba6334024beaea831d4b5371d Mon Sep 17 00:00:00 2001 From: TakahashiNg <83152264+TakahashiNguyen@users.noreply.github.com> Date: Thu, 30 Apr 2026 15:56:03 +0000 Subject: [PATCH] chore: update --- eatery-service/pom.xml | 27 ++--- .../provider/ManualSecurityFilter.java | 32 ++++++ gateway-service/pom.xml | 26 ++--- .../drinkool/controller/EateryController.java | 60 +++++++---- .../drinkool/filters/HeaderRolesFilter.java | 99 +++++++++++++++++++ .../filters/InternalHeaderGatewayFilter.java | 58 ----------- .../src/main/resources/application.properties | 8 +- k8s/base/gateway.yaml | 4 - lib/pom.xml | 15 +-- .../lib/utils/BaseHeaderAuthentication.java | 43 ++++++++ .../lib/utils/HeaderIdentityProvider.java | 38 ------- 11 files changed, 257 insertions(+), 153 deletions(-) create mode 100644 eatery-service/src/main/java/com/drinkool/provider/ManualSecurityFilter.java create mode 100644 gateway-service/src/main/java/com/drinkool/filters/HeaderRolesFilter.java create mode 100644 lib/src/main/java/com/drinkool/lib/utils/BaseHeaderAuthentication.java delete mode 100644 lib/src/main/java/com/drinkool/lib/utils/HeaderIdentityProvider.java diff --git a/eatery-service/pom.xml b/eatery-service/pom.xml index dd704c2..1178c1c 100644 --- a/eatery-service/pom.xml +++ b/eatery-service/pom.xml @@ -1,8 +1,8 @@ - + 4.0.0 @@ -20,11 +20,9 @@ 3.15.0 25 UTF-8 - UTF-8 + UTF-8 quarkus-bom - io.quarkus.platform + io.quarkus.platform 3.32.4 true 3.5.4 @@ -48,6 +46,11 @@ lib ${project.version} + + io.quarkus + quarkus-security + provided + net.datafaker datafaker @@ -138,8 +141,7 @@ @{argLine} - org.jboss.logmanager.LogManager + org.jboss.logmanager.LogManager ${maven.home} 1 @@ -163,8 +165,7 @@ ${project.build.directory}/${project.build.finalName}-runner - org.jboss.logmanager.LogManager + org.jboss.logmanager.LogManager ${maven.home} @@ -187,4 +188,4 @@ - + \ No newline at end of file diff --git a/eatery-service/src/main/java/com/drinkool/provider/ManualSecurityFilter.java b/eatery-service/src/main/java/com/drinkool/provider/ManualSecurityFilter.java new file mode 100644 index 0000000..5a98c4b --- /dev/null +++ b/eatery-service/src/main/java/com/drinkool/provider/ManualSecurityFilter.java @@ -0,0 +1,32 @@ +package com.drinkool.provider; + +import io.quarkus.security.identity.IdentityProviderManager; +import io.quarkus.security.identity.SecurityIdentity; +import io.quarkus.vertx.http.runtime.security.ChallengeData; +import io.quarkus.vertx.http.runtime.security.HttpAuthenticationMechanism; +import io.smallrye.mutiny.Uni; +import io.vertx.ext.web.RoutingContext; +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class ManualSecurityFilter implements HttpAuthenticationMechanism { + + @Override + public Uni authenticate( + RoutingContext context, + IdentityProviderManager identityProviderManager + ) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException( + "Unimplemented method 'authenticate'" + ); + } + + @Override + public Uni getChallenge(RoutingContext context) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException( + "Unimplemented method 'getChallenge'" + ); + } +} diff --git a/gateway-service/pom.xml b/gateway-service/pom.xml index 72c5c83..4b49027 100644 --- a/gateway-service/pom.xml +++ b/gateway-service/pom.xml @@ -1,8 +1,8 @@ - + 4.0.0 @@ -20,11 +20,9 @@ 3.15.0 25 UTF-8 - UTF-8 + UTF-8 quarkus-bom - io.quarkus.platform + io.quarkus.platform 3.32.4 true 3.5.4 @@ -48,6 +46,10 @@ jose4j 0.9.6 + + io.quarkus + quarkus-security + io.quarkus quarkus-smallrye-graphql-client @@ -110,8 +112,7 @@ @{argLine} - org.jboss.logmanager.LogManager + org.jboss.logmanager.LogManager ${maven.home} 1 @@ -135,8 +136,7 @@ ${project.build.directory}/${project.build.finalName}-runner - org.jboss.logmanager.LogManager + org.jboss.logmanager.LogManager ${maven.home} @@ -158,4 +158,4 @@ - + \ No newline at end of file diff --git a/gateway-service/src/main/java/com/drinkool/controller/EateryController.java b/gateway-service/src/main/java/com/drinkool/controller/EateryController.java index b7de506..42830e7 100644 --- a/gateway-service/src/main/java/com/drinkool/controller/EateryController.java +++ b/gateway-service/src/main/java/com/drinkool/controller/EateryController.java @@ -1,39 +1,59 @@ package com.drinkool.controller; +import com.drinkool.InternalValue; import com.drinkool.dtos.GraphqlDto; -import io.smallrye.graphql.client.GraphQLClient; -import io.smallrye.graphql.client.GraphQLError; import io.smallrye.graphql.client.dynamic.api.DynamicGraphQLClient; +import io.smallrye.graphql.client.dynamic.api.DynamicGraphQLClientBuilder; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.Response; -import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.stream.Collectors; +import org.eclipse.microprofile.config.inject.ConfigProperty; @Path("/api/eatery") public class EateryController { - @GraphQLClient("eatery") - DynamicGraphQLClient dynamicClient; + @ConfigProperty(name = "quarkus.smallrye-graphql-client.eatery.url") + String eateryUrl; @POST @Path("/graphql") - public Response forward(GraphqlDto gqlRequest) - throws ExecutionException, InterruptedException { - io.smallrye.graphql.client.Response response = dynamicClient.executeSync( - gqlRequest.query, - gqlRequest.variables - ); + public Response forward( + GraphqlDto gqlRequest, + @Context HttpHeaders httpHeaders + ) { + DynamicGraphQLClientBuilder builder = + DynamicGraphQLClientBuilder.newBuilder().url(eateryUrl); - if (response.hasError()) { - return Response.status(400) - .entity(response.getErrors().toString()) + httpHeaders + .getRequestHeaders() + .forEach((name, values) -> { + String headerName = name.toLowerCase(); + + if (headerName.startsWith(InternalValue.headerId.toLowerCase())) { + if (!values.isEmpty()) { + builder.header(name, values.get(0)); + } + } + }); + + try (DynamicGraphQLClient tempClient = builder.build()) { + io.smallrye.graphql.client.Response response = tempClient.executeSync( + gqlRequest.query, + gqlRequest.variables + ); + + if (response.hasError()) { + return Response.status(400).entity(response.getErrors()).build(); + } + + return Response.ok(response.getData()).build(); + } catch (Exception e) { + e.printStackTrace(); + return Response.serverError() + .entity("Lỗi kết nối tới service Eatery") .build(); } - - return jakarta.ws.rs.core.Response.ok( - response.getData().toString() - ).build(); } } diff --git a/gateway-service/src/main/java/com/drinkool/filters/HeaderRolesFilter.java b/gateway-service/src/main/java/com/drinkool/filters/HeaderRolesFilter.java new file mode 100644 index 0000000..69a5b1d --- /dev/null +++ b/gateway-service/src/main/java/com/drinkool/filters/HeaderRolesFilter.java @@ -0,0 +1,99 @@ +package com.drinkool.filters; + +import com.drinkool.InternalValue; +import com.drinkool.lib.utils.BaseHeaderAuthentication; +import io.quarkus.security.identity.IdentityProviderManager; +import io.quarkus.security.identity.SecurityIdentity; +import io.quarkus.security.runtime.QuarkusSecurityIdentity; +import io.smallrye.mutiny.Uni; +import io.vertx.core.MultiMap; +import io.vertx.ext.web.RoutingContext; +import jakarta.enterprise.context.ApplicationScoped; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.jose4j.jwt.JwtClaims; +import org.jose4j.jwt.consumer.JwtConsumer; +import org.jose4j.jwt.consumer.JwtConsumerBuilder; +import org.jose4j.keys.HmacKey; + +@ApplicationScoped +public class HeaderRolesFilter extends BaseHeaderAuthentication { + + @ConfigProperty(name = "gateway.jwt.secret") + String SECRET_KEY; + + private boolean isSystemClaim(String key) { + return List.of("iss", "sub", "aud", "exp", "nbf", "iat", "jti").contains( + key + ); + } + + private void removeForeignHeader(RoutingContext context) { + MultiMap headers = context.request().headers(); + List keysToRemove = new ArrayList<>(); + + for (String key : headers.names()) { + if (key.toLowerCase().startsWith(InternalValue.headerId.toLowerCase())) { + keysToRemove.add(key); + } + } + + for (String key : keysToRemove) { + headers.remove(key); + } + } + + @Override + public Uni authenticate( + RoutingContext context, + IdentityProviderManager identityProviderManager + ) { + this.removeForeignHeader(context); + + var cookie = context.request().getCookie(InternalValue.cookieName); + + if (cookie == null) return Uni.createFrom().nullItem(); + + try { + JwtConsumer jwtConsumer = new JwtConsumerBuilder() + .setRequireExpirationTime() + .setVerificationKey(new HmacKey(SECRET_KEY.getBytes())) + .build(); + + JwtClaims claims = jwtConsumer.processToClaims(cookie.getValue()); + Map allClaims = claims.getClaimsMap(); + + allClaims.forEach((key, value) -> { + if (!isSystemClaim(key) && value != null) { + context + .request() + .headers() + .add(InternalValue.headerId + key, value.toString()); + } + }); + + String role = context.request().getHeader(InternalValue.role); + String userId = context.request().getHeader(InternalValue.userId); + + System.out.println( + "HeaderRolesFilter - Authenticated: " + + role + + " - " + + context.request().headers().toString() + ); + + return Uni.createFrom().item( + QuarkusSecurityIdentity.builder() + .setPrincipal(() -> userId) + .addRole(role) + .build() + ); + } catch (Exception e) { + System.out.println(e.getMessage()); + + return Uni.createFrom().nullItem(); + } + } +} diff --git a/gateway-service/src/main/java/com/drinkool/filters/InternalHeaderGatewayFilter.java b/gateway-service/src/main/java/com/drinkool/filters/InternalHeaderGatewayFilter.java index 99afa06..0872821 100644 --- a/gateway-service/src/main/java/com/drinkool/filters/InternalHeaderGatewayFilter.java +++ b/gateway-service/src/main/java/com/drinkool/filters/InternalHeaderGatewayFilter.java @@ -1,19 +1,15 @@ 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.*; import java.util.*; import org.eclipse.microprofile.config.inject.ConfigProperty; -import org.jboss.resteasy.reactive.server.ServerRequestFilter; import org.jboss.resteasy.reactive.server.ServerResponseFilter; import org.jose4j.jws.AlgorithmIdentifiers; import org.jose4j.jws.JsonWebSignature; import org.jose4j.jwt.JwtClaims; -import org.jose4j.jwt.consumer.JwtConsumer; -import org.jose4j.jwt.consumer.JwtConsumerBuilder; import org.jose4j.keys.HmacKey; public class InternalHeaderGatewayFilter { @@ -21,60 +17,6 @@ public class InternalHeaderGatewayFilter { @ConfigProperty(name = "gateway.jwt.secret") String SECRET_KEY; - private boolean isSystemClaim(String key) { - return List.of("iss", "sub", "aud", "exp", "nbf", "iat", "jti").contains( - key - ); - } - - @ServerRequestFilter(preMatching = true) - public Uni handleRequestHeaders( - ContainerRequestContext requestContext - ) { - requestContext - .getHeaders() - .keySet() - .removeIf( - key -> - key.equalsIgnoreCase(InternalValue.headerId) || - key.toLowerCase().startsWith(InternalValue.headerId.toLowerCase()) - ); - - Map cookies = requestContext.getCookies(); - Cookie authCookie = cookies.get(InternalValue.cookieName); - - if (authCookie == null) return Uni.createFrom().nullItem(); - - return Uni.createFrom().item(() -> { - try { - String token = authCookie.getValue(); - - JwtConsumer jwtConsumer = new JwtConsumerBuilder() - .setRequireExpirationTime() - .setVerificationKey(new HmacKey(SECRET_KEY.getBytes())) - .build(); - - JwtClaims claims = jwtConsumer.processToClaims(token); - - Map allClaims = claims.getClaimsMap(); - - allClaims.forEach((key, value) -> { - if (!isSystemClaim(key) && value != null) { - String headerName = InternalValue.headerId + (key); - - requestContext.getHeaders().add(headerName, value.toString()); - } - }); - } catch (Exception e) { - return Response.status(Response.Status.UNAUTHORIZED) - .entity("InvalidToken") - .build(); - } - - return null; - }); - } - @ServerResponseFilter(priority = Priorities.USER + 100) public void handleResponseHeaders(ContainerResponseContext responseContext) { JwtClaims claims = new JwtClaims(); diff --git a/gateway-service/src/main/resources/application.properties b/gateway-service/src/main/resources/application.properties index be0c229..ec3257a 100644 --- a/gateway-service/src/main/resources/application.properties +++ b/gateway-service/src/main/resources/application.properties @@ -10,4 +10,10 @@ quarkus.container-image.additional-tags=latest # Build quarkus.native.container-build=true quarkus.native.remote-container-build=true -quarkus.native.additional-build-args=--future-defaults=all \ No newline at end of file +quarkus.native.additional-build-args=--future-defaults=all + +# Rest Client +quarkus.rest-client.account.url=http://account-service + +# GraphQL Client +quarkus.smallrye-graphql-client.eatery.url=http://eatery-service/graphql \ No newline at end of file diff --git a/k8s/base/gateway.yaml b/k8s/base/gateway.yaml index 5eaf5ad..c0fb9c8 100644 --- a/k8s/base/gateway.yaml +++ b/k8s/base/gateway.yaml @@ -19,10 +19,6 @@ spec: ports: - containerPort: 8080 env: - - name: QUARKUS_REST_CLIENT_ACCOUNT_URL - value: "http://account-service" - - name: QUARKUS_SMALLRYE_GRAPHQL_CLIENT_EATERY_URL - value: "http://eatery-service/graphql" - name: GATEWAY_JWT_SECRET valueFrom: secretKeyRef: diff --git a/lib/pom.xml b/lib/pom.xml index e871486..ce92231 100644 --- a/lib/pom.xml +++ b/lib/pom.xml @@ -1,8 +1,8 @@ - + 4.0.0 @@ -24,7 +24,10 @@ io.quarkus quarkus-security - provided + + + io.quarkus + quarkus-vertx-http com.fasterxml.jackson.core @@ -60,4 +63,4 @@ test - + \ No newline at end of file diff --git a/lib/src/main/java/com/drinkool/lib/utils/BaseHeaderAuthentication.java b/lib/src/main/java/com/drinkool/lib/utils/BaseHeaderAuthentication.java new file mode 100644 index 0000000..25d3c41 --- /dev/null +++ b/lib/src/main/java/com/drinkool/lib/utils/BaseHeaderAuthentication.java @@ -0,0 +1,43 @@ +package com.drinkool.lib.utils; + +import com.drinkool.InternalValue; +import io.quarkus.security.identity.IdentityProviderManager; +import io.quarkus.security.identity.SecurityIdentity; +import io.quarkus.security.identity.request.TrustedAuthenticationRequest; +import io.quarkus.vertx.http.runtime.security.ChallengeData; +import io.quarkus.vertx.http.runtime.security.HttpAuthenticationMechanism; +import io.smallrye.mutiny.Uni; +import io.vertx.ext.web.RoutingContext; + +public abstract class BaseHeaderAuthentication + implements HttpAuthenticationMechanism +{ + + @Override + public Uni authenticate( + RoutingContext context, + IdentityProviderManager identityProviderManager + ) { + String role = context.request().getHeader(InternalValue.role); + + System.out.println( + "HeaderRolesFilter: " + + role + + " - " + + context.request().headers().toString() + ); + + if (role != null && !role.isEmpty()) { + return identityProviderManager.authenticate( + new TrustedAuthenticationRequest(role) + ); + } + + return Uni.createFrom().nullItem(); + } + + @Override + public Uni getChallenge(RoutingContext context) { + return Uni.createFrom().nullItem(); + } +} diff --git a/lib/src/main/java/com/drinkool/lib/utils/HeaderIdentityProvider.java b/lib/src/main/java/com/drinkool/lib/utils/HeaderIdentityProvider.java deleted file mode 100644 index aee7b63..0000000 --- a/lib/src/main/java/com/drinkool/lib/utils/HeaderIdentityProvider.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.drinkool.lib.utils; - -import com.drinkool.InternalValue; -import io.quarkus.security.identity.AuthenticationRequestContext; -import io.quarkus.security.identity.IdentityProvider; -import io.quarkus.security.identity.SecurityIdentity; -import io.quarkus.security.identity.request.TrustedAuthenticationRequest; -import io.quarkus.security.runtime.QuarkusSecurityIdentity; -import io.smallrye.mutiny.Uni; -import io.vertx.core.http.HttpServerRequest; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; - -@ApplicationScoped -public class HeaderIdentityProvider - implements IdentityProvider -{ - - @Inject - HttpServerRequest httpServerRequest; - - @Override - public Class getRequestType() { - return TrustedAuthenticationRequest.class; - } - - @Override - public Uni authenticate( - TrustedAuthenticationRequest request, - AuthenticationRequestContext context - ) { - String role = httpServerRequest.getHeader(InternalValue.role); - - return Uni.createFrom().item( - QuarkusSecurityIdentity.builder().addRole(role).build() - ); - } -}