The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

Implementando um serviço gRPC

As implementações de serviço gRPC expostas como beans CDI são automaticamente registradas e servidas pelo quarkus-grpc.

A implementação de um serviço gRPC requer que as classes gRPC sejam geradas. Coloque seus arquivos 'proto' em 'src/main/proto' e execute 'mvn compile'.

Código Gerado

O Quarkus gera algumas classes de implementação para serviços declarados no arquivo 'proto':

  1. Um service interface usando a API Mutiny

    • o nome da classe é '${JAVA_PACKAGE}.${NAME_OF_THE_SERVICE}'

  2. Uma classe base de implementação que utiliza a API gRPC

    • o nome da classe está estruturado da seguinte forma: '${JAVA_PACKAGE}.${NAME_OF_THE_SERVICE}Grpc.${NAME_OF_THE_SERVICE}ImplBase'

Por exemplo, se você usar o seguinte trecho de arquivo 'proto':

option java_package = "hello"; (1)

service Greeter { (2)
    rpc SayHello (HelloRequest) returns (HelloReply) {}
}
1 hello é o pacote java para as classes geradas.
2 Greeter é o nome do serviço.

Em seguida, a interface de serviço é 'Olá. Greeter' e a base de implementação é a classe aninhada estática abstrata: 'hello. GreeterGrpc.GreeterImplBase'.

Você precisará implementar o service interface ou estender o base class com seu bean de implementação de serviço, conforme descrito nas seções a seguir.

Implementando um serviço com a API Mutiny

Para implementar um serviço gRPC usando a API Mutiny , crie uma classe que implemente a interface de serviço. Em seguida, implemente os métodos definidos na interface de serviço. Se você não quiser implementar um método de serviço, basta lançar um 'java.lang.UnsupportedOperationException' do corpo do método (a exceção será convertida automaticamente para a exceção gRPC apropriada). Finalmente, implemente o serviço e adicione a anotação '@GrpcService':

import io.quarkus.grpc.GrpcService;
import hello.Greeter;

@GrpcService (1)
public class HelloService implements Greeter { (2)

    @Override
    public Uni<HelloReply> sayHello(HelloRequest request) {
        return Uni.createFrom().item(() ->
                HelloReply.newBuilder().setMessage("Hello " + request.getName()).build()
        );
    }
}
1 Um bean de implementação de serviço gRPC deve ser anotado com a anotação '@GrpcService' e não deve declarar nenhum outro qualificador CDI. Todos os serviços gRPC têm o escopo 'jakarta.inject.Singleton'. Além disso, o contexto de solicitação está sempre ativo durante uma chamada de serviço.
2 hello.Greeter é a interface de serviço gerada.
O bean de implementação de serviço também pode estender a base de implementação do Mutiny , onde o nome da classe é estruturado da seguinte maneira: 'Mutiny${NAME_OF_THE_SERVICE}Grpc.${NAME_OF_THE_SERVICE}ImplBase'.

Implementando um serviço com a API gRPC padrão

Para implementar um serviço gRPC usando a API gRPC padrão, crie uma classe que estenda a base de implementação padrão. Em seguida, substitua os métodos definidos na interface de serviço. Finalmente, implemente o serviço e adicione a anotação '@GrpcService':

import io.quarkus.grpc.GrpcService;

@GrpcService
public class HelloService extends GreeterGrpc.GreeterImplBase {

    @Override
    public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
        String name = request.getName();
        String message = "Hello " + name;
        responseObserver.onNext(HelloReply.newBuilder().setMessage(message).build());
        responseObserver.onCompleted();
    }
}

Implementação do serviço de bloqueio

Por padrão, todos os métodos de um serviço gRPC são executados no loop de eventos. Como consequência, você deve não bloquear. Se a lógica do serviço precisar bloquear, anote o método com 'io.smallrye.common.annotation.Blocking':

@Override
@Blocking
public Uni<HelloReply> sayHelloBlocking(HelloRequest request) {
    // Do something blocking before returning the Uni
}

Manipulação de fluxos

gRPC permite receber e retornar fluxos:

service Streaming {
    rpc Source(Empty) returns (stream Item) {} // Returns a stream
    rpc Sink(stream Item) returns (Empty) {}   // Reads a stream
    rpc Pipe(stream Item) returns (stream Item) {}  // Reads a streams and return a streams
}

Usando o Mutiny, você pode implementá-los da seguinte maneira:

import io.quarkus.grpc.GrpcService;

@GrpcService
public class StreamingService implements Streaming {

    @Override
    public Multi<Item> source(Empty request) {
        // Just returns a stream emitting an item every 2ms and stopping after 10 items.
        return Multi.createFrom().ticks().every(Duration.ofMillis(2))
                .select().first(10)
                .map(l -> Item.newBuilder().setValue(Long.toString(l)).build());
    }

    @Override
    public Uni<Empty> sink(Multi<Item> request) {
        // Reads the incoming streams, consume all the items.
        return request
                .map(Item::getValue)
                .map(Long::parseLong)
                .collect().last()
                .map(l -> Empty.newBuilder().build());
    }

    @Override
    public Multi<Item> pipe(Multi<Item> request) {
        // Reads the incoming stream, compute a sum and return the cumulative results
        // in the outbound stream.
        return request
                .map(Item::getValue)
                .map(Long::parseLong)
                .onItem().scan(() -> 0L, Long::sum)
                .onItem().transform(l -> Item.newBuilder().setValue(Long.toString(l)).build());
    }
}

Health Check

Para os serviços implementados, o Quarkus gRPC expõe informações de integridade no seguinte formato:

syntax = "proto3";

package grpc.health.v1;

message HealthCheckRequest {
  string service = 1;
}

message HealthCheckResponse {
  enum ServingStatus {
    UNKNOWN = 0;
    SERVING = 1;
    NOT_SERVING = 2;
  }
  ServingStatus status = 1;
}

service Health {
  rpc Check(HealthCheckRequest) returns (HealthCheckResponse);

  rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse);
}

Os clientes podem especificar o nome de serviço totalmente qualificado para obter o status de integridade de um serviço específico ou ignore a especificação do nome do serviço para obter o status geral do servidor gRPC.

Para mais detalhes, confira o documentação do gRPC

Além disso, se o Quarkus SmallRye Health for adicionado ao aplicativo, uma verificação de prontidão para o estado dos serviços gRPC será adicionado à resposta de ponto de extremidade do MicroProfile Health, ou seja, '/q/health'.

Reflection Service

O Quarkus gRPC Server implementa o reflection service. Este serviço permite que ferramentas como grpcurl ou grpcox interajam com seus serviços.

O reflection service é habilitado por padrão no modo dev. No modo de teste ou produção, você precisa habilitá-lo explicitamente definindo 'quarkus.grpc.server.enable-reflection-service' como 'true'.

O Quarkus expõe o reflection service v1 e v1alpha.

Dimensionamento

Por padrão, o quarkus-grpc inicia um único servidor gRPC em execução em um único loop de eventos.

Se você deseja dimensionar seu servidor, você pode definir o número de instâncias do servidor definindo 'quarkus.grpc.server.instances'.

Configuração do Servidor

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property

Tipo

Padrão

Do we use separate HTTP server to serve gRPC requests. Set this to false if you want to use new Vert.x gRPC support, which uses existing Vert.x HTTP server.

Environment variable: QUARKUS_GRPC_SERVER_USE_SEPARATE_SERVER

Show more

boolean

true

Configure XDS usage, if enabled

Tipo

Padrão

Explicitly enable use of XDS.

Environment variable: QUARKUS_GRPC_SERVER_XDS_ENABLED

Show more

boolean

false

Use secure credentials.

Environment variable: QUARKUS_GRPC_SERVER_XDS_SECURE

Show more

boolean

false

Explicitly enable use of in-process.

Environment variable: QUARKUS_GRPC_SERVER_IN_PROCESS_ENABLED

Show more

boolean

false

Set in-process name.

Environment variable: QUARKUS_GRPC_SERVER_IN_PROCESS_NAME

Show more

string

quarkus-grpc

The gRPC Server port.

Environment variable: QUARKUS_GRPC_SERVER_PORT

Show more

int

9000

The gRPC Server port used for tests.

Environment variable: QUARKUS_GRPC_SERVER_TEST_PORT

Show more

int

9001

The gRPC server host.

Environment variable: QUARKUS_GRPC_SERVER_HOST

Show more

string

0.0.0.0

The gRPC handshake timeout.

Environment variable: QUARKUS_GRPC_SERVER_HANDSHAKE_TIMEOUT

Show more

Duration 

The max inbound message size in bytes.

When using a single server (using quarkus.grpc.server.use-separate-server=false), the default value is 256KB. When using a separate server (using quarkus.grpc.server.use-separate-server=true), the default value is 4MB.

Environment variable: QUARKUS_GRPC_SERVER_MAX_INBOUND_MESSAGE_SIZE

Show more

int

The max inbound metadata size in bytes

Environment variable: QUARKUS_GRPC_SERVER_MAX_INBOUND_METADATA_SIZE

Show more

int

The classpath path or file path to a server certificate or certificate chain in PEM format.

Environment variable: QUARKUS_GRPC_SERVER_SSL_CERTIFICATE

Show more

path

The classpath path or file path to the corresponding certificate private key file in PEM format.

Environment variable: QUARKUS_GRPC_SERVER_SSL_KEY

Show more

path

An optional keystore that holds the certificate information instead of specifying separate files. The keystore can be either on classpath or an external file.

Environment variable: QUARKUS_GRPC_SERVER_SSL_KEY_STORE

Show more

path

An optional parameter to specify the type of the keystore file. If not given, the type is automatically detected based on the file name.

Environment variable: QUARKUS_GRPC_SERVER_SSL_KEY_STORE_TYPE

Show more

string

A parameter to specify the password of the keystore file.

Environment variable: QUARKUS_GRPC_SERVER_SSL_KEY_STORE_PASSWORD

Show more

string

A parameter to specify the alias of the keystore file.

Environment variable: QUARKUS_GRPC_SERVER_SSL_KEY_STORE_ALIAS

Show more

string

A parameter to specify the alias password of the keystore file.

Environment variable: QUARKUS_GRPC_SERVER_SSL_KEY_STORE_ALIAS_PASSWORD

Show more

string

An optional trust store which holds the certificate information of the certificates to trust

The trust store can be either on classpath or an external file.

Environment variable: QUARKUS_GRPC_SERVER_SSL_TRUST_STORE

Show more

path

An optional parameter to specify type of the trust store file. If not given, the type is automatically detected based on the file name.

Environment variable: QUARKUS_GRPC_SERVER_SSL_TRUST_STORE_TYPE

Show more

string

A parameter to specify the password of the trust store file.

Environment variable: QUARKUS_GRPC_SERVER_SSL_TRUST_STORE_PASSWORD

Show more

string

The cipher suites to use. If none is given, a reasonable default is selected.

Environment variable: QUARKUS_GRPC_SERVER_SSL_CIPHER_SUITES

Show more

list of string

Sets the ordered list of enabled SSL/TLS protocols.

If not set, it defaults to "TLSv1.3, TLSv1.2". The following list of protocols are supported: TLSv1, TLSv1.1, TLSv1.2, TLSv1.3. To only enable TLSv1.3, set the value to to "TLSv1.3".

Note that setting an empty list, and enabling SSL/TLS is invalid. You must at least have one protocol.

Environment variable: QUARKUS_GRPC_SERVER_SSL_PROTOCOLS

Show more

list of string

TLSv1.3,TLSv1.2

Configures the engine to require/request client authentication. NONE, REQUEST, REQUIRED

Environment variable: QUARKUS_GRPC_SERVER_SSL_CLIENT_AUTH

Show more

none, request, required

none

Disables SSL, and uses plain text instead. If disabled, configure the ssl configuration.

Environment variable: QUARKUS_GRPC_SERVER_PLAIN_TEXT

Show more

boolean

true

Whether ALPN should be used.

Environment variable: QUARKUS_GRPC_SERVER_ALPN

Show more

boolean

true

The path to the certificate file.

Environment variable: QUARKUS_GRPC_SERVER_TRANSPORT_SECURITY_CERTIFICATE

Show more

string

The path to the private key file.

Environment variable: QUARKUS_GRPC_SERVER_TRANSPORT_SECURITY_KEY

Show more

string

Enables the gRPC Reflection Service. By default, the reflection service is only exposed in dev mode. This setting allows overriding this choice and enable the reflection service every time.

Environment variable: QUARKUS_GRPC_SERVER_ENABLE_REFLECTION_SERVICE

Show more

boolean

false

Number of gRPC server verticle instances. This is useful for scaling easily across multiple cores. The number should not exceed the amount of event loops.

Environment variable: QUARKUS_GRPC_SERVER_INSTANCES

Show more

int

1

Sets a custom keep-alive duration. This configures the time before sending a keepalive ping when there is no read activity.

Environment variable: QUARKUS_GRPC_SERVER_NETTY_KEEP_ALIVE_TIME

Show more

Duration 

Sets a custom permit-keep-alive duration. This configures the most aggressive keep-alive time clients are permitted to configure. The server will try to detect clients exceeding this rate and when detected will forcefully close the connection.

Environment variable: QUARKUS_GRPC_SERVER_NETTY_PERMIT_KEEP_ALIVE_TIME

Show more

Duration 

Sets whether to allow clients to send keep-alive HTTP/2 PINGs even if there are no outstanding RPCs on the connection.

Environment variable: QUARKUS_GRPC_SERVER_NETTY_PERMIT_KEEP_ALIVE_WITHOUT_CALLS

Show more

boolean

gRPC compression, e.g. "gzip"

Environment variable: QUARKUS_GRPC_SERVER_COMPRESSION

Show more

string

About the Duration format

To write duration values, use the standard java.time.Duration format. See the Duration#parse() Java API documentation for more information.

Você também pode usar um formato simplificado, começando com um número:

  • Se o valor for apenas um número, ele representará o tempo em segundos.

  • Se o valor for um número seguido de 'ms', ele representa o tempo em milissegundos.

Em outros casos, o formato simplificado é traduzido para o formato 'java.time.Duration' para análise:

  • Se o valor for um número seguido de 'h', 'm' ou 's', ele é prefixado com 'PT'.

  • Se o valor for um número seguido de 'd', ele é prefixado com 'P'.

Quando você desabilita 'quarkus.grpc.server.use-separate-server', você está usando a nova implementação do servidor gRPC Vert.x que usa o servidor HTTP existente. O que significa que a porta do servidor agora é '8080' (ou a porta configurada com 'quarkus.http.port'). Além disso, a maioria das outras propriedades de configuração não são mais aplicadas, pois é o servidor HTTP que já deve estar configurado corretamente.
Quando você habilita 'quarkus.grpc.server.xds.enabled', é o xDS que deve lidar com a maior parte da configuração acima.

Exemplo de configuração

Habilitando o TLS

Para habilitar o TLS, use a configuração a seguir.

Observe que todos os caminhos na configuração podem especificar um recurso no classpath (normalmente de 'src/main/resources' ou sua subpasta) ou um arquivo externo.

quarkus.grpc.server.ssl.certificate=tls/server.pem
quarkus.grpc.server.ssl.key=tls/server.key
Quando SSL/TLS é configurado, 'texto sem formatação' é automaticamente desabilitado.

TLS com autenticação mútua

Para usar o TLS com autenticação mútua, use a seguinte configuração:

quarkus.grpc.server.ssl.certificate=tls/server.pem
quarkus.grpc.server.ssl.key=tls/server.key
quarkus.grpc.server.ssl.trust-store=tls/ca.jks
quarkus.grpc.server.ssl.trust-store-password=*****
quarkus.grpc.server.ssl.client-auth=REQUIRED

Interceptores de servidor

Os interceptadores de servidor gRPC permitem que você execute lógica, como autenticação, antes que seu serviço seja chamado.

Você pode implementar um interceptor de servidor gRPC criando um bean '@ApplicationScoped' implementando 'io.grpc.ServerInterceptor':

@ApplicationScoped
// add @GlobalInterceptor for interceptors meant to be invoked for every service
public class MyInterceptor implements ServerInterceptor {

    @Override
    public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> serverCall,
            Metadata metadata, ServerCallHandler<ReqT, RespT> serverCallHandler) {
        // ...
    }
}

Também é possível anotar um método de produtor como um interceptador global:

import io.quarkus.grpc.GlobalInterceptor;

import jakarta.enterprise.inject.Produces;

public class MyProducer {
    @GlobalInterceptor
    @Produces
    public MyInterceptor myInterceptor() {
        return new MyInterceptor();
    }
}
Verifique o ServerInterceptor JavaDoc para implementar corretamente seu interceptador.

Para aplicar um interceptador a todos os serviços expostos, anote-o com '@io.quarkus.grpc.GlobalInterceptor'. Para aplicar um interceptador a um único serviço, registre-o no serviço com '@io.quarkus.grpc.RegisterInterceptor':

import io.quarkus.grpc.GrpcService;
import io.quarkus.grpc.RegisterInterceptor;

@GrpcService
@RegisterInterceptor(MyInterceptor.class)
public class StreamingService implements Streaming {
    // ...
}

Quando você tem vários interceptadores de servidor, você pode encomendá-los implementando a interface 'jakarta.enterprise.inject.spi.Priorizd'. Observe que todos os interceptores globais são invocados antes do serviço específico Interceptadores.

@ApplicationScoped
public class MyInterceptor implements ServerInterceptor, Prioritized {

    @Override
    public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> serverCall,
            Metadata metadata, ServerCallHandler<ReqT, RespT> serverCallHandler) {
        // ...
    }

    @Override
    public int getPriority() {
        return 10;
    }
}

Os interceptores com a maior prioridade são chamados primeiro. A prioridade padrão, usada se o interceptador não implementar a interface 'Priorizada', é '0'.

Testando seus serviços

A maneira mais fácil de testar um serviço gRPC é usar um cliente gRPC conforme descrito em Consumindo um serviço gRPC.

Observe que, no caso de usar um cliente para testar um serviço exposto que não usa TLS, Não há necessidade de fornecer nenhuma configuração. Por exemplo, para testar o 'HelloService' definido acima, pode-se criar o seguinte teste:

public class HelloServiceTest implements Greeter {

    @GrpcClient
    Greeter client;

    @Test
    void shouldReturnHello() {
        CompletableFuture<String> message = new CompletableFuture<>();
        client.sayHello(HelloRequest.newBuilder().setName("Quarkus").build())
                .subscribe().with(reply -> message.complete(reply.getMessage()));
        assertThat(message.get(5, TimeUnit.SECONDS)).isEqualTo("Hello Quarkus");
    }
}

Experimentando seus serviços manualmente

In the dev mode, you can try out your gRPC services in the Quarkus Dev UI. Just go to http://localhost:8080/q/dev-ui and click on Services under the gRPC tile.

Observe que seu aplicativo precisa expor a porta HTTP "normal" para que a interface do usuário de desenvolvimento seja acessível. Se seu aplicativo não expor nenhum ponto de extremidade HTTP, você poderá criar um perfil dedicado com uma dependência em 'quarkus-vertx-http':

    <profiles>
        <profile>
            <id>development</id>
            <dependencies>
                <dependency>
                    <groupId>io.quarkus</groupId>
                    <artifactId>quarkus-vertx-http</artifactId>
                </dependency>
            </dependencies>
        </profile>
    </profiles>

Tendo isso, você pode executar o modo dev com: 'mvn quarkus:dev -Pdevelopment'.

Se você usar o Gradle, você pode simplesmente adicionar uma dependência para a tarefa 'quarkusDev':

dependencies {
    quarkusDev 'io.quarkus:quarkus-vertx-http'
}

Métricas do gRPC Server

Habilitando a coleta de métricas

As métricas do servidor gRPC são ativadas automaticamente quando o aplicativo também usa a extensão 'quarkus-micrometer'. O Micrometer coleta as métricas de todos os serviços gRPC implementados pelo aplicativo.

Por exemplo, se você exportar as métricas para o Prometheus, você terá:

# HELP grpc_server_responses_sent_messages_total The total number of responses sent
# TYPE grpc_server_responses_sent_messages_total counter
grpc_server_responses_sent_messages_total{method="SayHello",methodType="UNARY",service="helloworld.Greeter",} 6.0
# HELP grpc_server_processing_duration_seconds The total time taken for the server to complete the call
# TYPE grpc_server_processing_duration_seconds summary
grpc_server_processing_duration_seconds_count{method="SayHello",methodType="UNARY",service="helloworld.Greeter",statusCode="OK",} 6.0
grpc_server_processing_duration_seconds_sum{method="SayHello",methodType="UNARY",service="helloworld.Greeter",statusCode="OK",} 0.016216771
# HELP grpc_server_processing_duration_seconds_max The total time taken for the server to complete the call
# TYPE grpc_server_processing_duration_seconds_max gauge
grpc_server_processing_duration_seconds_max{method="SayHello",methodType="UNARY",service="helloworld.Greeter",statusCode="OK",} 0.007985236
# HELP grpc_server_requests_received_messages_total The total number of requests received
# TYPE grpc_server_requests_received_messages_total counter
grpc_server_requests_received_messages_total{method="SayHello",methodType="UNARY",service="helloworld.Greeter",} 6.0

O nome do serviço, o método e o tipo podem ser encontrados em tags.

Desabilitando a coleta de métricas

Para desabilitar as métricas do servidor gRPC quando 'quarkus-micrometer' for usado, adicione a seguinte propriedade à configuração do aplicativo:

quarkus.micrometer.binder.grpc-server.enabled=false

Usar threads virtuais

Para usar threads virtuais na implementação do serviço gRPC, verifique o guia dedicado.

gRPC Server authorization

Quarkus includes built-in security to allow authorization using annotations when the Vert.x gRPC support, which uses existing Vert.x HTTP server, is enabled.

Add the Quarkus Security extension

Security capabilities are provided by the Quarkus Security extension, therefore make sure your pom.xml file contains following dependency:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-security</artifactId>
</dependency>

To add the Quarkus Security extension to an existing Maven project, run the following command from your project base directory:

CLI
quarkus extension add security
Maven
./mvnw quarkus:add-extension -Dextensions='security'
Gradle
./gradlew addExtension --extensions='security'

Overview of supported authentication mechanisms

Some supported authentication mechanisms are built into Quarkus, while others require you to add an extension. The following table maps specific authentication requirements to a supported mechanism that you can use in Quarkus:

Table 1. Authentication requirements and mechanisms
Authentication requirement Authentication mechanism

Username and password

Autenticação básica

Client certificate

Mutual TLS authentication

Custom requirements

Custom authentication

Bearer access token

OIDC Bearer token authentication, JWT, OAuth2

Do not forget to install at least one extension that provides an IdentityProvider based on selected authentication requirements. Please refer to the Basic authentication guide for example how to provide the IdentityProvider based on username and password.

If you use separate HTTP server to serve gRPC requests, Custom authentication is your only option. Set the quarkus.grpc.server.use-separate-server configuration property to false so that you can use other mechanisms.

Secure gRPC service

The gRPC services can be secured with the standard security annotations like in the example below:

package org.acme.grpc.auth;

import hello.Greeter;
import io.quarkus.grpc.GrpcService;
import jakarta.annotation.security.RolesAllowed;

@GrpcService
public class HelloService implements Greeter {

    @RolesAllowed("admin")
    @Override
    public Uni<HelloReply> sayHello(HelloRequest request) {
        return Uni.createFrom().item(() ->
                HelloReply.newBuilder().setMessage("Hello " + request.getName()).build()
        );
    }
}

Most of the examples of the supported mechanisms sends authentication headers, please refer to the gRPC Headers section of the Consuming a gRPC Service guide for more information about the gRPC headers.

Autenticação básica

Quarkus Security provides built-in authentication support for the Basic authentication.

quarkus.grpc.server.use-separate-server=false
quarkus.http.auth.basic=true (1)
1 Enable the Basic authentication.
package org.acme.grpc.auth;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

import org.acme.proto.Greeter;
import org.acme.proto.HelloRequest;
import io.grpc.Metadata;
import io.quarkus.grpc.GrpcClient;
import io.quarkus.grpc.GrpcClientUtils;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;

@QuarkusTest
public class GreeterServiceTest {

    private static final Metadata.Key<String> AUTHORIZATION = Metadata.Key.of("Authorization", Metadata.ASCII_STRING_MARSHALLER);

    @GrpcClient
    Greeter greeterClient;

    @Test
    void shouldReturnHello() throws ExecutionException, InterruptedException, TimeoutException {
        Metadata headers = new Metadata();

        // Set the headers - Basic auth for testing
        headers.put(AUTHORIZATION, "Basic YWxpY2U6YWxpY2U="); // alice:alice with "admin" role
        var client = GrpcClientUtils.attachHeaders(greeterClient, headers);

        // Call the client
        CompletableFuture<String> message = new CompletableFuture<>();
        client.sayHello(HelloRequest.newBuilder().setName("Quarkus").build())
                .subscribe().with(reply -> message.complete(reply.getMessage()));

        // Get the values
        String theValue = message.get(5, TimeUnit.SECONDS);

        // Assert
        assertThat(theValue, is("Hello Quarkus"));
    }
}

Mutual TLS authentication

Quarkus provides mutual TLS (mTLS) authentication so that you can authenticate users based on their X.509 certificates. The simplest way to enforce authentication for all your gRPC services is described in the TLS com autenticação mútua section of this guide. However, the Quarkus Security supports role mapping that you can use to perform even more fine-grained access control.

quarkus.grpc.server.use-separate-server=false
quarkus.http.insecure-requests=disabled
quarkus.http.ssl.certificate.files=tls/server.pem
quarkus.http.ssl.certificate.key-files=tls/server.key
quarkus.http.ssl.certificate.trust-store-file=tls/ca.jks
quarkus.http.ssl.certificate.trust-store-password=**********
quarkus.http.ssl.client-auth=required
quarkus.http.auth.certificate-role-properties=role-mappings.txt     (1)
quarkus.native.additional-build-args=-H:IncludeResources=.*\\.txt
1 Adds certificate role mapping.
Example of the role mapping file
testclient=admin (1)
1 Map the testclient certificate CN (Common Name) to the SecurityIdentity role admin.

Custom authentication

You can always implement one or more GrpcSecurityMechanism bean if above-mentioned mechanisms provided by Quarkus do no meet your needs.

Example of custom GrpcSecurityMechanism
package org.acme.grpc.auth;

import jakarta.inject.Singleton;

import io.grpc.Metadata;
import io.quarkus.security.credential.PasswordCredential;
import io.quarkus.security.identity.request.AuthenticationRequest;
import io.quarkus.security.identity.request.UsernamePasswordAuthenticationRequest;

@Singleton
public class CustomGrpcSecurityMechanism implements GrpcSecurityMechanism {

    private static final Metadata.Key<String> AUTHORIZATION = Metadata.Key.of("Authorization", Metadata.ASCII_STRING_MARSHALLER);

    @Override
    public boolean handles(Metadata metadata) {
        String authString = metadata.get(AUTHORIZATION);
        return authString != null && authString.startsWith("Custom ");
    }

    @Override
    public AuthenticationRequest createAuthenticationRequest(Metadata metadata) {
        final String authString = metadata.get(AUTHORIZATION);
        final String userName;
        final String password;
        // here comes your application logic that transforms 'authString' to user name and password
        return new UsernamePasswordAuthenticationRequest(userName, new PasswordCredential(password));
    }
}

Conteúdo Relacionado