Autenticação e autorização
Explore estratégias para autenticar clientes e autorizar o acesso aos métodos de serviços gRPC.
Autenticação e autorização é uma aula grátis de gRPC & High Performance APIs no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de gRPC & High Performance APIs, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de gRPC & High Performance APIs inclui 4 aulas no total.
Partes desta aula ainda não foram traduzidas e aparecem em inglês.
Secure Your gRPC Services
Welcome! In this lesson, we'll dive into Authentication and Authorization for gRPC services. These are crucial concepts for building secure and reliable distributed systems.
You'll learn how to verify who is accessing your services and what actions they are allowed to perform.
Authentication: Who Are You?
Authentication is the process of verifying a client's identity. Think of it like checking an ID at a club.
- It answers the question: "Are you who you say you are?"
- Common methods include API keys, JWTs (JSON Web Tokens), or OAuth tokens.
- In gRPC, these credentials are often passed as custom metadata with each request.
Authorization: What Can You Do?
Once a client is authenticated, Authorization determines what actions they are permitted to perform.
- It answers the question: "Are you allowed to do that?"
- For example, an "admin" user might be authorized to delete data, while a "guest" user can only view it.
- Authorization checks happen after successful authentication.
Why Auth & AuthZ Matter
Securing your gRPC services with proper authentication and authorization is vital:
- Prevent Unauthorized Access: Only trusted clients can interact with your services.
- Protect Sensitive Data: Ensure data is only accessed or modified by authorized entities.
- Compliance & Auditing: Meet regulatory requirements and maintain an audit trail of actions.
It's a foundational layer of security for any production system.
Clients Send Credentials
In gRPC, clients typically send authentication credentials as custom metadata in the request header.
This metadata is essentially a map of key-value pairs that travels with the RPC call. For instance, an API-Key or an Authorization header carrying a token.
The server then extracts and validates these credentials.
Server Verifies Identity
On the server side, your gRPC service needs logic to:
- Extract Credentials: Read the authentication token or API key from the incoming request's metadata.
- Validate Credentials: Check if the extracted credential is valid (e.g., compare an API key against a database, verify a JWT's signature and expiry).
- Identify Principal: If valid, identify the user or service making the request.
This process determines if the client is legitimate.
Server Checks Permissions
After a client is authenticated, the server proceeds to authorization.
This involves checking if the authenticated client (or "principal") has the necessary permissions to call the specific gRPC method requested.
- You might use roles (e.g.,
admin,user) or specific permissions associated with the client's identity. - This check often happens early in the method's execution or via an interceptor.
Attaching an API Key (Client)
Here's a simple Java client example that attaches an API-Key to a gRPC request using metadata. The Metadata class is used to build these headers.
Try running this example (you'll need the server from the next scene running first):
// auth_service.proto (simplified for context in code)
// syntax = "proto3";
// option java_multiple_files = true;
// option java_package = "com.coddykit.grpc.auth";
// option java_outer_classname = "AuthServiceProto";
// package auth;
// service AuthService {
// rpc SayHello (HelloRequest) returns (HelloResponse);
// rpc SayAdminHello (HelloRequest) returns (HelloResponse);
// }
// message HelloRequest { string name = 1; }
// message HelloResponse { string message = 1; }
package com.coddykit.grpc.auth;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;
import io.grpc.stub.StreamObserver;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class AuthClient {
private final ManagedChannel channel;
private final AuthServiceGrpc.AuthServiceStub asyncStub;
public AuthClient(String host, int port) {
channel = ManagedChannelBuilder.forAddress(host, port)
.usePlaintext() // For demonstration, use TLS in production
.build();
asyncStub = AuthServiceGrpc.newStub(channel);
}
public void shutdown() throws InterruptedException {
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
}
public void callServiceWithKey(String method, String name, String apiKey) throws InterruptedException {
System.out.println("--- Calling " + method + " with API Key: " + apiKey + " ---");
final CountDownLatch latch = new CountDownLatch(1);
Metadata headers = new Metadata();
Metadata.Key<String> apiKeyHeader = Metadata.Key.of("api-key", Metadata.ASCII_STRING_MARSHALLER);
headers.put(apiKeyHeader, apiKey);
AuthServiceGrpc.AuthServiceStub authenticatedStub = MetadataUtils.attachHeaders(asyncStub, headers);
HelloRequest request = HelloRequest.newBuilder().setName(name).build();
StreamObserver<HelloResponse> responseObserver = new StreamObserver<HelloResponse>() {
@Override
public void onNext(HelloResponse response) {
System.out.println("Response: " + response.getMessage());
}
@Override
public void onError(Throwable t) {
System.err.println("Error calling " + method + ": " + t.getMessage());
latch.countDown();
}
@Override
public void onCompleted() {
System.out.println("Call completed.");
latch.countDown();
}
};
if ("SayHello".equals(method)) {
authenticatedStub.sayHello(request, responseObserver);
} else if ("SayAdminHello".equals(method)) {
authenticatedStub.sayAdminHello(request, responseObserver);
} else {
System.err.println("Unknown method: " + method);
latch.countDown();
}
latch.await(1, TimeUnit.MINUTES);
}
public static void main(String[] args) throws Exception {
AuthClient client = new AuthClient("localhost", 50051);
try {
// These keys would be issued to different clients
String validApiKey = "my-secret-api-key-123";
String adminApiKey = "admin-secret-key-456";
String invalidApiKey = "wrong-key";
client.callServiceWithKey("SayHello", "Alice", validApiKey);
Thread.sleep(500);
client.callServiceWithKey("SayHello", "Bob", invalidApiKey);
Thread.sleep(500);
client.callServiceWithKey("SayAdminHello", "Charlie", validApiKey);
Thread.sleep(500);
client.callServiceWithKey("SayAdminHello", "AdminUser", adminApiKey);
Thread.sleep(500);
} finally {
client.shutdown();
}
}
}Validating API Key (Server)
This server example demonstrates how an interceptor extracts the API-Key from the request metadata and performs initial authentication. If valid, the key is attached to the Context.
Service methods then retrieve the key from the Context for fine-grained authorization checks. This is a common and robust pattern.
Try running this example (start this server, then the client from the previous scene):
// auth_service.proto (simplified for context in code)
// syntax = "proto3";
// option java_multiple_files = true;
// option java_package = "com.coddykit.grpc.auth";
// option java_outer_classname = "AuthServiceProto";
// package auth;
// service AuthService {
// rpc SayHello (HelloRequest) returns (HelloResponse);
// rpc SayAdminHello (HelloRequest) returns (HelloResponse);
// }
// message HelloRequest { string name = 1; }
// message HelloResponse { string message = 1; }
package com.coddykit.grpc.auth;
import io.grpc.Context;
import io.grpc.Metadata;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.ServerCall;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;
import io.grpc.Status;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
import java.util.logging.Logger;
public class AuthServer {
private static final Logger logger = Logger.getLogger(AuthServer.class.getName());
private Server server;
private void start() throws IOException {
int port = 50051;
server = ServerBuilder.forPort(port)
.addService(new AuthServiceImpl())
.intercept(new AuthInterceptor()) // Add our authentication interceptor
.build()
.start();
logger.info("Server started, listening on " + port);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.err.println("*** shutting down gRPC server since JVM is shutting down");
try {
AuthServer.this.stop();
} catch (InterruptedException e) {
e.printStackTrace(System.err);
}
System.err.println("*** server shut down");
}));
}
private void stop() throws InterruptedException {
if (server != null) {
server.shutdown().awaitTermination(30, java.util.concurrent.TimeUnit.SECONDS);
}
}
private void blockUntilShutdown() throws InterruptedException {
if (server != null) {
server.awaitTermination();
}
}
public static void main(String[] args) throws IOException, InterruptedException {
final AuthServer server = new AuthServer();
server.start();
server.blockUntilShutdown();
}
// Context key to store the authenticated API Key after interceptor processing
static final Context.Key<String> AUTH_API_KEY = Context.key("api-key");
// A simple, hardcoded valid API key for demonstration
private static final String VALID_API_KEY = "my-secret-api-key-123";
private static final String ADMIN_API_KEY = "admin-secret-key-456"; // For authorization example
static class AuthInterceptor implements ServerInterceptor {
static final Metadata.Key<String> API_KEY_METADATA_KEY =
Metadata.Key.of("api-key", Metadata.ASCII_STRING_MARSHALLER);
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> call,
Metadata headers,
ServerCallHandler<ReqT, RespT> next) {
String apiKey = headers.get(API_KEY_METADATA_KEY);
// Basic Authentication check in the interceptor
if (apiKey == null || (!apiKey.equals(VALID_API_KEY) && !apiKey.equals(ADMIN_API_KEY))) {
logger.warning("AuthInterceptor: Authentication failed - Invalid or missing API key.");
call.close(Status.UNAUTHENTICATED.withDescription("Missing or invalid API key"), headers);
return new ServerCall.Listener<ReqT>() {}; // No-op listener
}
// If authenticated, attach the API key to the Context for later use by service methods
Context context = Context.current().withValue(AUTH_API_KEY, apiKey);
return Context.current().call(() -> next.startCall(call, headers));
}
}
static class AuthServiceImpl extends AuthServiceGrpc.AuthServiceImplBase {
@Override
public void sayHello(HelloRequest request, StreamObserver<HelloResponse> responseObserver) {
String apiKey = AUTH_API_KEY.get(); // Get API key from Context (set by interceptor)
logger.info("AuthServiceImpl: sayHello called with authenticated API Key: " + apiKey);
// No further authorization needed for SayHello, as authentication was done by interceptor
String message = "Hello " + request.getName() + " from authenticated service!";
HelloResponse response = HelloResponse.newBuilder().setMessage(message).build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
@Override
public void sayAdminHello(HelloRequest request, StreamObserver<HelloResponse> responseObserver) {
String apiKey = AUTH_API_KEY.get(); // Get API key from Context (set by interceptor)
// Authorization check (only admin key can access this specific method)
if (!apiKey.equals(ADMIN_API_KEY)) {
logger.warning("AuthServiceImpl: Authorization failed - API key " + apiKey + " is not authorized for admin access.");
responseObserver.onError(
Status.PERMISSION_DENIED
.withDescription("Access denied: Requires admin privileges")
.asRuntimeException());
return;
}
logger.info("AuthServiceImpl: sayAdminHello called with authorized API Key: " + apiKey);
String message = "Hello Admin " + request.getName() + " from secure service!";
HelloResponse response = HelloResponse.newBuilder().setMessage(message).build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
}Check Your Understanding
Time for a quick check!
Recap: Auth & AuthZ
Great job! In this lesson, we explored:
- The difference between Authentication (who you are) and Authorization (what you can do).
- How clients send credentials via gRPC metadata.
- How servers can extract these credentials and apply both authentication and authorization logic, often with the help of interceptors.
Securing your gRPC services is a critical step towards building robust and reliable distributed applications!
Perguntas Frequentes
A aula “Autenticação e autorização” é grátis?
Sim — o texto completo de “Autenticação e autorização” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de gRPC & High Performance APIs, atualize para CoddyKit PRO. O curso de gRPC & High Performance APIs inclui 4 aulas no total.
O que vou aprender em “Autenticação e autorização”?
Explore estratégias para autenticar clientes e autorizar o acesso aos métodos de serviços gRPC. Você pratica gRPC & High Performance APIs com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar gRPC & High Performance APIs?
Nenhuma experiência prévia é necessária. gRPC & High Performance APIs no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.
Quanto tempo leva a aula “Autenticação e autorização”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de gRPC & High Performance APIs?
Sim. Cada aula de gRPC & High Performance APIs inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- TLS/SSL para gRPC
- Autenticação e autorização
- Interceptadores para segurança
- TLS mútuo (mTLS) para autenticação entre serviços