API-Zugriff mit API-Schlüsseln einschränken

Sie können mit API-Schlüsseln den Zugriff auf bestimmte oder alle Methoden in einer API einschränken. Auf dieser Seite wird beschrieben, wie Sie den API-Zugriff auf Clients mit einem API-Schlüssel einschränken und API-Schlüssel erstellen.

Der Extensible Service Proxy (ESP) verwendet Service ControlAPI, um einen API-Schlüssel und seine Verknüpfung mit der aktivierten API eines Projekts zu prüfen. Wenn Sie eine API-Schlüsselanforderung in Ihrer API festlegen, werden Anfragen an die geschützte Methode, Klasse oder API abgelehnt, es sei denn, sie haben einen Schlüssel, der in Ihrem Projekt oder in anderen Projekten von Entwicklern generiert wurde, denen Zugriff zum Aktivieren Ihrer API gewährt wurde. Das Projekt, in dem der API-Schlüssel erstellt wurde, wird nicht protokolliert und nicht in den Anfrage-Header eingefügt. Sie können jedoch das Google Cloud-Projekt, mit dem ein Client verknüpft ist, auf der Seite Endpoints > Dienste aufrufen. Eine Beschreibung hierzu finden Sie unter Nach einem bestimmten Nutzerprojekt filtern.

Informationen darüber, in welchem Google Cloud-Projekt ein API-Schlüssel erstellt werden sollte, finden Sie unter Durch einen API-Schlüssel geschützte APIs freigeben.

Standardmäßig ist in gRPC-Diensten für den Zugriff auf alle API-Methoden ein API-Schlüssel erforderlich. Sie können die API-Schlüsselanforderung für die gesamte API oder für bestimmte Methoden deaktivieren. Dazu fügen Sie Ihrer Dienstkonfiguration einen Nutzungsabschnitt hinzu und konfigurieren Regeln und Selektoren, wie in den folgenden Verfahren beschrieben.

Zugriff auf alle API-Methoden einschränken oder gewähren

So legen Sie fest, dass für den Zugriff auf Ihre API kein API-Schlüssel erforderlich ist:

  1. Öffnen Sie die gRPC-Dienstkonfigurationsdatei Ihres Projekts in einem Texteditor und rufen Sie den Abschnitt usage auf oder fügen Sie ihn hinzu.

  2. Legen Sie im Abschnitt usage die Regel allow_unregistered_calls fest. Der Platzhalter "*" in selector gibt an, dass die Regel für alle Methoden in der API gilt.

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

API-Schlüsseleinschränkung für eine Methode entfernen

So deaktivieren Sie die API-Schlüsselvalidierung für eine bestimmte Methode trotz eingeschränktem Zugriff auf die API:

  1. Öffnen Sie die gRPC-Dienstkonfigurationsdatei Ihres Projekts in einem Texteditor und rufen Sie den Abschnitt usage auf oder fügen Sie ihn hinzu.

  2. Legen Sie im Abschnitt usage die Regel allow_unregistered_calls fest. Der Eintrag selector gibt an, dass die Regel nur für die angegebene Methode gilt – in diesem Fall ListShelves.

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

API mithilfe eines API-Schlüssels aufrufen

Wie APIs aufgerufen werden, hängt davon ab, ob der Aufruf von einem gRPC- oder HTTP-Client aus erfolgt.

gRPC-Clients

Wenn eine Methode einen API-Schlüssel erfordert, müssen gRPC-Clients den Schlüsselwert als x-api-key Metadaten mit dem Methodenaufruf übergeben.

Python

def run(host, port, api_key, auth_token, timeout, use_tls):
    """Makes a basic ListShelves call against a gRPC Bookstore server."""

    if use_tls:
        with open('../roots.pem', 'rb') as f:
            creds = grpc.ssl_channel_credentials(f.read())
        channel = grpc.secure_channel('{}:{}'.format(host, port), creds)
    else:
        channel = grpc.insecure_channel('{}:{}'.format(host, port))

    stub = bookstore_pb2_grpc.BookstoreStub(channel)
    metadata = []
    if api_key:
        metadata.append(('x-api-key', api_key))
    if auth_token:
        metadata.append(('authorization', 'Bearer ' + auth_token))
    shelves = stub.ListShelves(empty_pb2.Empty(), timeout, metadata=metadata)
    print('ListShelves: {}'.format(shelves))

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');
  const path = require('path');

  // Load protobuf spec for an example API
  const PROTO_PATH = path.join(__dirname, '/protos/helloworld.proto');
  const protoObj = grpc.load(PROTO_PATH).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);
    }
  });
};

HTTP-Clients

Wenn Sie Cloud Endpoints für die HTTP-Transcodierung von gRPC verwenden, können HTTP-Clients den Schlüssel wie für OpenAPI-Dienste als Abfrageparameter senden.

Durch einen API-Schlüssel geschützte APIs freigeben

API-Schlüssel sind dem Google Cloud-Projekt zugeordnet, in dem sie erstellt wurden. Wenn Sie für Ihre API das Anfordern eines API-Schlüssels festgelegt haben, hängt das Google Cloud-Projekt, in dem der API-Schlüssel erstellt wird, von den Antworten auf die folgenden Fragen ab:

  • Müssen Sie zwischen den Aufrufern Ihrer API unterscheiden, damit Sie Endpoints-Features wie Kontingente verwenden können?
  • Haben alle Aufrufer Ihrer API eigene Google Cloud-Projekte?
  • Müssen Sie verschiedene API-Schlüsseleinschränkungen einrichten?

Sie können sich am folgenden Entscheidungsbaum orientieren, um festzulegen, in welchem Google Cloud-Projekt der API-Schlüssel erstellt werden soll.

API-Schlüssel-Entscheidungsbaum

Berechtigung zum Aktivieren der API gewähren

Wenn Sie zwischen den Aufrufern Ihrer API unterscheiden müssen und jeder Aufrufer über ein eigenes Google Cloud-Projekt verfügt, können Sie Nutzern, die zu den aufrufenden Projekten gehören, die Berechtigung zum Aktivieren der API in ihrem eigenen Google Cloud-Projekt erteilen. Dadurch können Nutzer einen eigenen API-Schlüssel für die Verwendung mit Ihrer API erstellen.

Nehmen Sie als Beispiel ein Team, das eine API zur internen Verwendung durch verschiedene Clientprogramme in einem Unternehmen erstellt hat. Dabei gibt es für jedes Clientprogramm ein eigenes Google Cloud-Projekt. Zur Unterscheidung zwischen den Aufrufern der API muss der API-Schlüssel dann für jeden Aufrufer in einem anderen Google Cloud-Projekt erstellt werden. Den Mitarbeitern kann die Berechtigung zum Aktivieren der API in dem Google Cloud-Projekt, mit dem das Clientprogramm verknüpft ist, erteilt werden.

So ermöglichen Sie Nutzern, einen eigenen API-Schlüssel zu erstellen:

  1. Gewähren Sie jedem Nutzer im Google Cloud-Projekt, in dem die API konfiguriert ist, die Berechtigung zum Aktivieren Ihrer API.
  2. Teilen Sie den Nutzern mit, dass sie Ihre API im eigenen Google Cloud-Projekt aktivieren und einen API-Schlüssel erstellen können.

Separates Google Cloud-Projekt für jeden Aufrufer erstellen

Wenn Sie zwischen Aufrufern Ihrer API unterscheiden müssen und nicht alle Aufrufer über ein Google Cloud-Projekt verfügen, können Sie für jeden Aufrufer ein separates Google Cloud-Projekt und einen separaten API-Schlüssel erstellen. Überlegen Sie sich sinnvolle Projektnamen, bevor Sie die Projekte erstellen, damit Sie den Aufrufer, der mit dem jeweiligen Projekt verknüpft ist, einfach identifizieren können.

Beispiel: Ihre API wird von externen Kunden aufgerufen und Sie wissen nicht, wie die Clientprogramme, die Ihre API aufrufen, erstellt wurden. Einige Clients verwenden vielleicht Google Cloud-Dienste und haben ein Google Cloud-Projekt, andere aber nicht. Um zwischen den Aufrufern zu unterscheiden, müssen Sie für jeden Aufrufer ein separates Google Cloud-Projekt und einen separaten API-Schlüssel erstellen.

So erstellen Sie ein separates Google Cloud-Projekt und einen API-Schlüssel für jeden Aufrufer:

  1. Erstellen Sie ein separates Projekt für jeden Aufrufer.
  2. Aktivieren Sie die API in jedem Projekt und erstellen Sie einen API-Schlüssel.
  3. Geben Sie jedem Aufrufer den jeweiligen API-Schlüssel.

API-Schlüssel für jeden Aufrufer erstellen

Wenn Sie nicht zwischen Aufrufern Ihrer API unterscheiden müssen, aber Einschränkungen für API-Schlüssel einführen möchten, können Sie für jeden Aufrufer in demselben Projekt einen separaten API-Schlüssel erstellen.

So erstellen Sie einen API-Schlüssel für jeden Aufrufer im selben Projekt:

  1. Erstellen Sie in dem Projekt, in dem die API konfiguriert ist, oder in einem Projekt, in dem die API aktiviert ist, für jeden Kunden einen API-Schlüssel mit den gewünschten API-Schlüsseleinschränkungen.
  2. Geben Sie jedem Aufrufer den jeweiligen API-Schlüssel.

Einen einzigen API-Schlüssel für alle Aufrufer erstellen

Wenn Sie nicht zwischen den Aufrufern Ihrer API unterscheiden und keine API-Einschränkungen hinzufügen müssen, aber trotzdem einen API-Schlüssel voraussetzen möchten (um beispielsweise den anonymen Zugriff zu verhindern), können Sie einen API-Schlüssel erstellen, der von allen Aufrufern verwendet wird.

So erstellen Sie einen einzigen API-Schlüssel für alle Aufrufer:
  1. Erstellen Sie entweder in dem Projekt, in dem die API konfiguriert ist, oder in einem Projekt, in dem die API aktiviert ist, einen API-Schlüssel für alle Aufrufer.
  2. Geben Sie jedem Aufrufer denselben API-Schlüssel.

Weitere Informationen