Co-authored-by: TakahashiNg <83152264+TakahashiNguyen@users.noreply.github.com> Reviewed-on: #41
This commit was merged in pull request #41.
This commit is contained in:
@@ -48,6 +48,10 @@
|
||||
<artifactId>jose4j</artifactId>
|
||||
<version>0.9.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-smallrye-graphql-client</artifactId>
|
||||
|
||||
@@ -1,43 +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()) {
|
||||
List<String> errorMessages = response
|
||||
.getErrors()
|
||||
.stream()
|
||||
.map(GraphQLError::getMessage)
|
||||
.collect(Collectors.toList());
|
||||
httpHeaders
|
||||
.getRequestHeaders()
|
||||
.forEach((name, values) -> {
|
||||
String headerName = name.toLowerCase();
|
||||
|
||||
return Response.status(400).entity(errorMessages).build();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
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<String> 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<SecurityIdentity> authenticate(
|
||||
RoutingContext context,
|
||||
IdentityProviderManager identityProviderManager
|
||||
) {
|
||||
this.removeForeignHeader(context);
|
||||
|
||||
var cookie = context.request().getCookie(InternalValue.cookieName);
|
||||
|
||||
if (cookie == null) return Uni.createFrom().nullItem();
|
||||
|
||||
return Uni.createFrom().item(() -> {
|
||||
try {
|
||||
JwtConsumer jwtConsumer = new JwtConsumerBuilder()
|
||||
.setRequireExpirationTime()
|
||||
.setVerificationKey(new HmacKey(SECRET_KEY.getBytes()))
|
||||
.build();
|
||||
|
||||
JwtClaims claims = jwtConsumer.processToClaims(cookie.getValue());
|
||||
Map<String, Object> 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);
|
||||
|
||||
return QuarkusSecurityIdentity.builder()
|
||||
.setPrincipal(() -> userId)
|
||||
.addRole(role)
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
context.response()
|
||||
.setStatusCode(401)
|
||||
.end("InvalidToken");
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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<Response> handleRequestHeaders(
|
||||
ContainerRequestContext requestContext
|
||||
) {
|
||||
requestContext
|
||||
.getHeaders()
|
||||
.keySet()
|
||||
.removeIf(
|
||||
key ->
|
||||
key.equalsIgnoreCase(InternalValue.headerId) ||
|
||||
key.toLowerCase().startsWith(InternalValue.headerId.toLowerCase())
|
||||
);
|
||||
|
||||
Map<String, Cookie> 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<String, Object> 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();
|
||||
|
||||
@@ -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
|
||||
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
|
||||
Reference in New Issue
Block a user