gRPC & High Performance APIs · Pelajaran

Autentikasi dan Otorisasi

Jelajahi strategi untuk mengautentikasi klien dan mengotorisasi akses ke metode layanan gRPC.

Pelajaran 2 dari 411 langkah

Autentikasi dan Otorisasi adalah pelajaran gRPC & High Performance APIs gratis di CoddyKit. Ini adalah pelajaran 2 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar gRPC & High Performance APIs, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus gRPC & High Performance APIs mencakup 4 pelajaran total.

Bagian dari pelajaran ini belum diterjemahkan dan ditampilkan dalam bahasa Inggris.

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:

  1. Extract Credentials: Read the authentication token or API key from the incoming request's metadata.
  2. 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).
  3. 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!

Gratis untuk memulai

Belajar gRPC & High Performance APIs dengan tutor AI — gratis

Tulis dan jalankan kode asli di browser kamu, dapatkan bantuan instan dari tutor AI 24/7, dan lanjutkan di mana kamu tinggalkan di web atau aplikasi.

Kursus
12
Pelajaran
48

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Autentikasi dan Otorisasi” gratis?

Ya — teks lengkap “Autentikasi dan Otorisasi” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus gRPC & High Performance APIs, upgrade ke CoddyKit PRO. Kursus gRPC & High Performance APIs mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Autentikasi dan Otorisasi”?

Jelajahi strategi untuk mengautentikasi klien dan mengotorisasi akses ke metode layanan gRPC. Kamu berlatih gRPC & High Performance APIs dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai gRPC & High Performance APIs?

Tidak diperlukan pengalaman sebelumnya. gRPC & High Performance APIs di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 2 dari 4.

Berapa lama pelajaran “Autentikasi dan Otorisasi” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran gRPC & High Performance APIs ini?

Ya. Setiap pelajaran gRPC & High Performance APIs menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. TLS/SSL untuk gRPC
  2. Autentikasi dan Otorisasi
  3. Interceptor untuk Keamanan
  4. TLS Saling Autentikasi (mTLS) untuk Autentikasi Layanan
← Kembali ke gRPC & High Performance APIs