Membatasi akses API dengan kunci API

Anda dapat menggunakan kunci API untuk membatasi akses ke metode API tertentu atau semua metode dalam API. Halaman ini menjelaskan cara membatasi akses API ke klien yang memiliki kunci API dan juga menunjukkan cara membuat kunci API.

The Extensible Service Proxy (ESP) menggunakan Service ControlAPI untuk memvalidasi kunci API dan kaitannya dengan API yang diaktifkan project. Jika Anda menetapkan persyaratan kunci API di API, permintaan ke metode, class, atau API yang dilindungi akan ditolak kecuali jika memiliki kunci yang dibuat di project Anda atau dalam project lain milik developer yang telah Anda beri akses untuk mengaktifkan API Anda. Project tempat kunci API dibuat tidak dicatat dan tidak ditambahkan ke header permintaan. Namun, Anda dapat melihat Google Cloud project yang terkait dengan klien di Endpoints > Service, seperti yang dijelaskan dalam Memfilter project konsumen tertentu.

Untuk mengetahui informasi tentang Google Cloud project tempat kunci API harus dibuat, lihat Berbagi API yang dilindungi oleh kunci API.

Secara default di layanan gRPC, semua metode API memerlukan kunci API untuk mengaksesnya. Anda dapat menonaktifkan persyaratan kunci API untuk seluruh API atau untuk metode tertentu. Anda dapat melakukannya dengan menambahkan bagian penggunaan ke konfigurasi layanan dan mengonfigurasi aturan dan pemilih, seperti yang dijelaskan dalam prosedur berikut.

Membatasi atau memberikan akses ke semua metode API

Untuk menentukan bahwa kunci API tidak diperlukan untuk mengakses API Anda:

  1. Buka file konfigurasi layanan gRPC project Anda di editor teks dan temukan atau tambahkan bagian usage.

  2. Di bagian usage, tentukan aturan allow_unregistered_calls sebagai berikut. Karakter pengganti "*" di selector berarti aturan tersebut berlaku untuk semua metode di API.

    usage:
      rules:
      # All methods can be called without an API Key.
      - selector: "*"
        allow_unregistered_calls: true
    

Menghapus pembatasan kunci API untuk metode

Untuk menonaktifkan validasi kunci API untuk metode tertentu meskipun Anda telah membatasi akses API untuk API:

  1. Buka file konfigurasi layanan gRPC project Anda di editor teks dan temukan atau tambahkan bagian usage:

  2. Di bagian usage, tentukan aturan allow_unregistered_calls sebagai berikut. selector berarti aturan tersebut hanya berlaku untuk metode yang ditentukan, dalam hal ini, ListShelves.

    usage:
      rules:
      # ListShelves method can be called without an API Key.
      - selector: endpoints.examples.bookstore.Bookstore.ListShelves
        allow_unregistered_calls: true
    

Memanggil API menggunakan kunci API

Cara memanggil API bervariasi, bergantung pada apakah Anda memanggil dari klien gRPC atau klien HTTP.

Klien gRPC

Jika metode memerlukan kunci API, klien gRPC harus meneruskan nilai kunci sebagai x-api-key metadata dengan panggilan metodenya.

Python

def run(
    host, port, api_key, auth_token, timeout, use_tls, servername_override, ca_path

Java

private static final class Interceptor implements ClientInterceptor {
  private final String apiKey;
  private final String authToken;

  private static Logger LOGGER = Logger.getLogger("InfoLogging");

  private static Metadata.Key<String> API_KEY_HEADER =
      Metadata.Key.of("x-api-key", Metadata.ASCII_STRING_MARSHALLER);
  private static Metadata.Key<String> AUTHORIZATION_HEADER =
      Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER);

  public Interceptor(String apiKey, String authToken) {
    this.apiKey = apiKey;
    this.authToken = authToken;
  }

  @Override
  public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
      MethodDescriptor<ReqT,RespT> method, CallOptions callOptions, Channel next) {
    LOGGER.info("Intercepted " + method.getFullMethodName());
    ClientCall<ReqT, RespT> call = next.newCall(method, callOptions);

    call = new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(call) {
      @Override
      public void start(Listener<RespT> responseListener, Metadata headers) {
        if (apiKey != null && !apiKey.isEmpty()) {
          LOGGER.info("Attaching API Key: " + apiKey);
          headers.put(API_KEY_HEADER, apiKey);
        }
        if (authToken != null && !authToken.isEmpty()) {
          System.out.println("Attaching auth token");
          headers.put(AUTHORIZATION_HEADER, "Bearer " + authToken);
        }
        super.start(responseListener, headers);
      }
    };
    return call;
  }
}

Go

func main() {
	flag.Parse()

	// Set up a connection to the server.
	conn, err := grpc.Dial(*addr, grpc.WithInsecure())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	c := pb.NewGreeterClient(conn)

	if *keyfile != "" {
		log.Printf("Authenticating using Google service account key in %s", *keyfile)
		keyBytes, err := ioutil.ReadFile(*keyfile)
		if err != nil {
			log.Fatalf("Unable to read service account key file %s: %v", *keyfile, err)
		}

		tokenSource, err := google.JWTAccessTokenSourceFromJSON(keyBytes, *audience)
		if err != nil {
			log.Fatalf("Error building JWT access token source: %v", err)
		}
		jwt, err := tokenSource.Token()
		if err != nil {
			log.Fatalf("Unable to generate JWT token: %v", err)
		}
		*token = jwt.AccessToken
		// NOTE: the generated JWT token has a 1h TTL.
		// Make sure to refresh the token before it expires by calling TokenSource.Token() for each outgoing requests.
		// Calls to this particular implementation of TokenSource.Token() are cheap.
	}

	ctx := context.Background()
	if *key != "" {
		log.Printf("Using API key: %s", *key)
		ctx = metadata.AppendToOutgoingContext(ctx, "x-api-key", *key)
	}
	if *token != "" {
		log.Printf("Using authentication token: %s", *token)
		ctx = metadata.AppendToOutgoingContext(ctx, "Authorization", fmt.Sprintf("Bearer %s", *token))
	}

	// Contact the server and print out its response.
	name := defaultName
	if len(flag.Args()) > 0 {
		name = flag.Arg(0)
	}
	r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
	if err != nil {
		log.Fatalf("could not greet: %v", err)
	}
	log.Printf("Greeting: %s", r.Message)
}

Node.js

const makeGrpcRequest = (JWT_AUTH_TOKEN, API_KEY, HOST, GREETEE) => {
  // Uncomment these lines to set their values
  // const JWT_AUTH_TOKEN = 'YOUR_JWT_AUTH_TOKEN';
  // const API_KEY = 'YOUR_API_KEY';
  // const HOST = 'localhost:50051'; // The IP address of your endpoints host
  // const GREETEE = 'world';

  // Import required libraries
  const grpc = require('@grpc/grpc-js');
  const protoLoader = require('@grpc/proto-loader');
  const path = require('path');

  // Load protobuf spec for an example API
  const PROTO_PATH = path.join(__dirname, '/protos/helloworld.proto');

  const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
    keepCase: true,
    longs: String,
    enums: String,
    defaults: true,
    oneofs: true,
  });

  const protoObj = grpc.loadPackageDefinition(packageDefinition).helloworld;

  // Create a client for the protobuf spec
  const client = new protoObj.Greeter(HOST, grpc.credentials.createInsecure());

  // Build gRPC request
  const metadata = new grpc.Metadata();
  if (API_KEY) {
    metadata.add('x-api-key', API_KEY);
  } else if (JWT_AUTH_TOKEN) {
    metadata.add('authorization', `Bearer ${JWT_AUTH_TOKEN}`);
  }

  // Execute gRPC request
  client.sayHello({name: GREETEE}, metadata, (err, response) => {
    if (err) {
      console.error(err);
    }

    if (response) {
      console.log(response.message);
    }
  });
};

Klien HTTP

Jika Anda menggunakan fitur transkode HTTP Cloud Endpoints untuk gRPC, klien HTTP dapat mengirim kunci sebagai parameter kueri dengan cara yang sama seperti yang mereka lakukan untuk layanan OpenAPI.

Berbagi API yang dilindungi oleh kunci API

Kunci API dikaitkan dengan Google Cloud project tempat kunci API dibuat. Jika Anda memutuskan untuk mewajibkan kunci API untuk API Anda, Google Cloud project tempat kunci API dibuat bergantung pada jawaban atas pertanyaan berikut:

  • Apakah Anda perlu membedakan antara pemanggil API Anda sehingga Anda dapat menggunakan fitur Endpoints seperti kuota?
  • Apakah semua pemanggil API Anda memiliki Google Cloud project mereka sendiri?
  • Apakah Anda perlu menyiapkan pembatasan kunci API yang berbeda?

Anda dapat menggunakan diagram alur pengambilan keputusan berikut sebagai panduan untuk memutuskan Google Cloud project tempat kunci API akan dibuat.

Pohon keputusan kunci API

Memberikan izin untuk mengaktifkan API

Jika Anda perlu membedakan antara pemanggil API Anda, dan setiap pemanggil memilikiproject mereka sendiri Google Cloud , Anda dapat memberikan izin utama untuk mengaktifkan API di project mereka sendiri Google Cloud . Dengan cara ini, pengguna API Anda dapat membuat kunci API mereka sendiri untuk digunakan dengan API Anda.

Misalnya, tim Anda telah membuat API untuk penggunaan internal oleh berbagai program klien di perusahaan Anda, dan setiap program klien memiliki Google Cloud project-nya sendiri. Untuk membedakan antara pemanggil API Anda, kunci API untuk setiap pemanggil harus dibuat diproject yang berbeda Google Cloud . Anda dapat memberikan izin kepada rekan kerja untuk mengaktifkan API di Google Cloud project yang terkait dengan program klien.

Untuk mengizinkan pengguna membuat kunci API mereka sendiri:

  1. Di Google Cloud project tempat API Anda dikonfigurasi, berikan izin kepada setiap pengguna untuk mengaktifkan API Anda.
  2. Hubungi pengguna, dan beri tahu mereka bahwa mereka dapat mengaktifkan API Anda di project mereka sendiri Google Cloud dan membuat kunci API.

Membuatproject terpisah Google Cloud untuk setiap pemanggil

Jika Anda perlu membedakan antara pemanggil API Anda, dan tidak semua pemanggil memiliki Google Cloud project, Anda dapat membuat Google Cloud project dan kunci API terpisah untuk setiap pemanggil. Sebelum membuat project, pertimbangkan nama project agar Anda dapat dengan mudah mengidentifikasi pemanggil yang terkait dengan project.

Misalnya, Anda memiliki pelanggan eksternal API Anda, dan Anda tidak tahu bagaimana program klien yang memanggil API Anda dibuat. Mungkin beberapa klien menggunakan Google Cloud layanan dan memilikiproject, dan mungkin beberapa tidak. Google Cloud Untuk membedakan antara pemanggil, Anda harus membuat project dan kunci API terpisah untuk setiap pemanggil. Google Cloud

Untuk membuat Google Cloud project dan kunci API terpisah untuk setiap pemanggil:

  1. Buat project terpisah untuk setiap pemanggil.
  2. Di setiap project, aktifkan API Anda dan buat kunci API.
  3. Berikan kunci API kepada setiap pemanggil.

Membuat kunci API untuk setiap pemanggil

Jika Anda tidak perlu membedakan antara pemanggil API Anda, tetapi Anda ingin menambahkan pembatasan kunci API, Anda dapat membuat kunci API terpisah untuk setiap pemanggil di project yang sama.

Untuk membuat kunci API untuk setiap pemanggil di project yang sama:

  1. Di project tempat API Anda dikonfigurasi, atau project tempat API Anda diaktifkan, buat kunci API untuk setiap pelanggan yang memiliki pembatasan kunci API yang Anda butuhkan.
  2. Berikan kunci API kepada setiap pemanggil.

Membuat satu kunci API untuk semua pemanggil

Jika Anda tidak perlu membedakan antara pemanggil API Anda, dan Anda tidak perlu menambahkan pembatasan API, tetapi Anda tetap ingin mewajibkan kunci API (misalnya, untuk mencegah akses anonim), Anda dapat membuat satu kunci API untuk digunakan oleh semua pemanggil.

Untuk membuat satu kunci API untuk semua pemanggil:
  1. Di project tempat API Anda dikonfigurasi, atau project tempat API Anda diaktifkan, buat kunci API untuk semua pemanggil yang memiliki pembatasan kunci API yang Anda butuhkan.
  2. Berikan kunci API yang sama kepada setiap pemanggil.

Pembatasan aplikasi

Pembatasan aplikasi menentukan situs, alamat IP, atau aplikasi mana yang dapat menggunakan kunci API Anda. Untuk mengetahui informasi selengkapnya, lihat Menambahkan pembatasan aplikasi.

Catatan: Jika Anda menggunakan perujuk HTTP (situs) sebagai pembatasan aplikasi, Anda harus menyertakan skema (misalnya, https://) saat menambahkan pembatasan situs. Misalnya, https://example.com/* adalah pembatasan yang valid, tetapi example.com/* tidak valid.

Langkah berikutnya