Limitazione dell'accesso alle API con le chiavi API

Puoi utilizzare le chiavi API per limitare l'accesso a metodi API specifici o a tutti i metodi in un'API. In questa pagina viene descritto come limitare l'accesso alle API ai client con una chiave API e viene mostrato inoltre come creare una chiave API.

Extensible Service Proxy (ESP) utilizza l'API Service Control per convalidare una chiave API e la sua associazione con l'API abilitata di un progetto. Se imposti un requisito di chiave API nell'API, le richieste al metodo protetto, alla classe o all'API vengono rifiutate a meno che non abbiano una chiave generata nel progetto o all'interno di altri progetti appartenenti a sviluppatori con cui hai consentito l'accesso per abilitare l'API. Il progetto in cui è stata creata la chiave API non viene registrato e non viene aggiunto all'intestazione della richiesta. Tuttavia, puoi visualizzare il progetto Google Cloud a cui è associato un client in Endpoint > Servizio, come descritto in Filtro per un progetto consumer specifico.

Per informazioni sul progetto Google Cloud in cui deve essere creata una chiave API, consulta la pagina relativa alla condivisione delle API protette dalla chiave API.

Per impostazione predefinita nei servizi gRPC, tutti i metodi API richiedono una chiave API per accedervi. Puoi disabilitare il requisito della chiave API per l'intera API o per metodi specifici. A tale scopo, aggiungi una sezione utilizzo alla configurazione del servizio e configura regole e selettori, come descritto nelle seguenti procedure.

Limitazione o concessione dell'accesso a tutti i metodi API

Per specificare che non è necessaria una chiave API per accedere all'API:

  1. Apri il file di configurazione del servizio gRPC del tuo progetto in un editor di testo e trova o aggiungi una sezione usage.

  2. Nella sezione usage, specifica una regola allow_unregistered_calls come segue. Il carattere jolly "*" in selector indica che la regola si applica a tutti i metodi nell'API.

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

Rimozione della limitazione della chiave API per un metodo

Per disattivare la convalida della chiave API per un determinato metodo anche se hai limitato l'accesso all'API:

  1. Apri il file di configurazione del servizio gRPC del progetto in un editor di testo e trova o aggiungi una sezione usage:

  2. Nella sezione usage, specifica una regola allow_unregistered_calls come segue. selector significa che la regola si applica solo al metodo specificato, in questo caso ListShelves.

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

Chiamata di un'API utilizzando una chiave API

La chiamata a un'API varia a seconda che tu chiami da un client gRPC o da un client HTTP.

Client gRPC

Se un metodo richiede una chiave API, i client gRPC devono passare il valore della chiave come x-api-key metadati con la loro chiamata di metodo.

Python

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

    if use_tls:
        with open(ca_path, 'rb') as f:
            creds = grpc.ssl_channel_credentials(f.read())
        channel_opts = ()
        if servername_override:
            channel_opts += ((
                        'grpc.ssl_target_name_override', servername_override,),)
        channel = grpc.secure_channel(f'{host}:{port}', creds, channel_opts)
    else:
        channel = grpc.insecure_channel(f'{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(f'ListShelves: {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);
    }
  });
};

Client HTTP

Se utilizzi la funzionalità di transcodifica HTTP di Cloud Endpoints per gRPC, i client HTTP possono inviare la chiave come parametro di ricerca, come nel caso dei servizi OpenAPI.

Condivisione di API protette dalla chiave API

Le chiavi API sono associate al progetto Google Cloud in cui sono state create. Se hai deciso di richiedere una chiave API per la tua API, il progetto Google Cloud in cui viene creata la chiave API dipende dalle risposte alle seguenti domande:

  • Devi distinguere tra i chiamanti della tua API in modo da poter utilizzare le funzionalità endpoint come le quote?
  • Tutti i chiamanti dell'API hanno i propri progetti Google Cloud?
  • Hai bisogno di configurare diverse limitazioni delle chiavi API?

Puoi utilizzare la seguente struttura decisionale come guida per decidere in quale progetto Google Cloud creare la chiave API.

Struttura decisionale a livello di chiave API

Concedi l'autorizzazione per abilitare l'API

Quando devi distinguere tra i chiamanti della tua API e ogni chiamante ha un proprio progetto Google Cloud, puoi concedere alle entità l'autorizzazione ad abilitare l'API nel proprio progetto Google Cloud. In questo modo, gli utenti della tua API possono creare la propria chiave API da utilizzare con la tua API.

Ad esempio, supponiamo che il tuo team abbia creato un'API per uso interno da parte di vari programmi client nella tua azienda e che ogni programma client abbia il proprio progetto Google Cloud. Per distinguere i chiamanti dell'API, devi creare la chiave API per ciascun chiamante in un progetto Google Cloud diverso. Puoi concedere ai tuoi colleghi l'autorizzazione ad abilitare l'API nel progetto Google Cloud a cui è associato il programma client.

Per consentire agli utenti di creare la propria chiave API:

  1. Nel progetto Google Cloud in cui è configurata l'API, concedi a ogni utente l'autorizzazione per abilitare la tua API.
  2. Contatta gli utenti per informarli che possono abilitare la tua API nel loro progetto Google Cloud e creare una chiave API.

Creare un progetto Google Cloud separato per ogni chiamante

Quando devi distinguere tra i chiamanti della tua API e non tutti i chiamanti hanno progetti Google Cloud, puoi creare un progetto Google Cloud separato e una chiave API per ogni chiamante. Prima di creare i progetti, pensa ai nomi dei progetti in modo da poter identificare facilmente il chiamante associato al progetto.

Ad esempio, supponiamo che tu abbia clienti esterni della tua API e che non abbia idea di come sono stati creati i programmi client che chiamano l'API. Forse alcuni client utilizzano i servizi Google Cloud e hanno un progetto Google Cloud, e alcuni no. Per distinguere i chiamanti, devi creare un progetto Google Cloud e una chiave API separati per ciascun chiamante.

Per creare un progetto e una chiave API Google Cloud separati per ciascun chiamante:

  1. Creare un progetto separato per ogni chiamante.
  2. In ogni progetto, abilita l'API e crea una chiave API.
  3. Assegna la chiave API a ciascun chiamante.

Creare una chiave API per ciascun chiamante

Se non hai la necessità di distinguere i chiamanti dell'API, ma vuoi aggiungere restrizioni per le chiavi API, puoi creare una chiave API separata per ciascun chiamante nello stesso progetto.

Per creare una chiave API per ogni chiamante nello stesso progetto:

  1. Puoi creare una chiave API per ogni cliente con le limitazioni delle chiavi API di cui hai bisogno nel progetto in cui è configurata l'API o in quello in cui è attiva l'API.
  2. Assegna la chiave API a ciascun chiamante.

Crea una chiave API per tutti i chiamanti

Quando non hai bisogno di distinguere tra i chiamanti della tua API e non devi aggiungere restrizioni API, ma vuoi comunque richiedere una chiave API (per impedire l'accesso anonimo, ad esempio), puoi creare una chiave API per tutti i chiamanti da utilizzare.

Per creare una chiave API per tutti i chiamanti:
  1. Crea una chiave API per tutti i chiamanti, nel progetto in cui è configurata l'API o in un progetto in cui è abilitata l'API.
  2. Fornisci la stessa chiave API a ogni chiamante.

Passaggi successivi