Restringe el acceso a la API con claves de API

Puedes usar claves de API para restringir el acceso a métodos de la API específicos o a todos los métodos en una API. En esta página, se describe cómo restringir el acceso a la API a los clientes que tienen una clave de API y, también, cómo crear una clave de API.

El proxy de servicio extensible (ESP) usa la API de Control de servicios para validar una clave de API y su asociación con la API habilitada de un proyecto. Si configuras un requisito de clave en tu API, se rechazan las solicitudes a la API, la clase, o el método protegidos si no tienen una clave generada en tu proyecto o en otros proyectos que pertenezcan a desarrolladores a los que les hayas otorgado acceso para habilitar tu API. El proyecto en el que se creó la clave de API no se registra y no se agrega al encabezado de la solicitud. Sin embargo, puedes ver el proyecto de Google Cloud con el que está asociado un cliente en Endpoints > Servicio, como se describe en Filtro para un proyecto de consumidor específico.

Si deseas obtener información sobre en qué proyecto de Google Cloud debe crearse una clave de API, consulta la sección sobre cómo compartir API protegidas por una clave de API.

De forma predeterminada en los servicios de gRPC, todos los métodos de la API requieren una clave de API para poder acceder. Puedes inhabilitar el requisito de clave de API para toda la API o para métodos específicos. Para eso, agrega una sección de uso a la configuración del servicio, y configura reglas y selectores, como se describe en los procedimientos que aparecen a continuación.

Otorga o restringe el acceso a todos los métodos de la API

A fin de especificar que no se necesita una clave de API para acceder a tu API, sigue estos pasos:

  1. Abre el archivo de configuración del servicio de gRPC del proyecto en un editor de texto y encuentra o agrega una sección de usage.

  2. En tu sección de usage, especifica una regla allow_unregistered_calls de la siguiente manera. El comodín "*" en el selector significa que la regla se aplica a todos los métodos de la API.

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

Quita la restricción de clave de API de un método

A fin de desactivar la validación de la clave de API para un método en particular, incluso cuando restringiste el acceso a la API para la API:

  1. Abre el archivo de configuración del servicio de gRPC del proyecto en un editor de texto y encuentra o agrega una sección de usage:

  2. En tu sección de usage, especifica una regla allow_unregistered_calls de la siguiente manera. El selector significa que la regla se aplica solo al método especificado; en este caso, ListShelves.

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

Llama a una API mediante una clave de API

La llamada a una API varía, según si llamas desde un cliente gRPC o HTTP.

Clientes gRPC

Si un método requiere una clave de API, los clientes de gRPC deben pasar el valor de clave como metadatos de x-api-key con su llamada al método.

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);
    }
  });
};

Clientes HTTP

Si usas la característica de transcodificación de HTTP a gRPC de Cloud Endpoints, los clientes HTTP pueden enviar una clave como un parámetro de consulta de la misma manera que lo hacen para los servicios de OpenAPI.

Comparte API protegidas por claves de API

Las claves de API están asociadas al proyecto de Google Cloud en el que se crearon. Si decidiste solicitar una clave de API para tu API, el proyecto de Google Cloud en el que se crea la clave de API depende de las respuestas a las siguientes preguntas:

  • ¿Necesitas distinguir entre los emisores de tu API de modo que puedas usar características de Endpoints, como las cuotas?
  • ¿Todos los emisores de tu API tienen sus propios proyectos de Google Cloud?
  • ¿Necesitas configurar restricciones de clave de API diferentes?

Puedes usar el siguiente árbol de decisión como guía para decidir en qué proyecto de Google Cloud se creará la clave de API.

Árbol de decisión de clave de API

Otorga permisos para habilitar la API

Cuando necesites distinguir entre los emisores de tu API y cada emisor tiene su propio proyecto de Google Cloud, puedes otorgar a las principales permiso para habilitar la API en su propio proyecto de Google Cloud. De esta forma, los usuarios pueden crear su propia clave de API para usarla en tu API.

Por ejemplo, supongamos que tu equipo creó una API de uso interno para varios programas cliente de tu empresa y cada programa cliente tiene su propio proyecto de Google Cloud. Si necesitas distinguir entre los emisores de la API, se debe crear una clave de API para cada emisor en un proyecto de Google Cloud diferente. Puedes otorgar a tus compañeros de trabajo permiso para habilitar la API en el proyecto de Google Cloud con el que está asociado el programa cliente.

Para permitir que los usuarios creen sus propias claves de API, haz lo siguiente:

  1. En el proyecto de Google Cloud en el que está configurada tu API, otorga a cada usuario permiso para habilitar tu API.
  2. Comunícate con los usuarios y hazles saber que pueden habilitar la API en su propio proyecto de Google Cloud y crear una clave de API.

Crea un proyecto de Google Cloud por separado para cada emisor

Cuando necesites distinguir entre los emisores de tu API y no todos los emisores tengan proyectos de Google Cloud, puedes crear un proyecto de Google Cloud y una clave de API diferentes para cada emisor. Antes de crear los proyectos, piensa bien qué nombres de proyecto usarás, a fin de poder identificar con facilidad al emisor asociado con el proyecto.

Por ejemplo, supongamos que la API tiene clientes externos y no tienes idea de cómo se crearon los programas cliente que llaman a la API. Es posible que algunos de los clientes utilicen los servicios de Google Cloud y tengan un proyecto de Google Cloud, y quizás otros no. Para distinguir entre los emisores, debes crear un proyecto de Google Cloud separado y una clave de API para cada emisor.

Si deseas crear un proyecto de Google Cloud y una clave de API distintos para cada emisor, sigue estos pasos:

  1. Crea un proyecto por separado para cada emisor.
  2. En cada proyecto, habilita tu API y crea una clave de API.
  3. Proporciona la clave de API a cada emisor.

Crear una clave de API para cada emisor

Cuando no necesitas distinguir entre los emisores de tu API, pero quieres agregar restricciones de clave de API, puedes crear una clave de API separada para cada emisor en el mismo proyecto.

Para crear una clave de API para cada emisor en el mismo proyecto, haz lo siguiente:

  1. En el proyecto en el que está configurada la API o en un proyecto en el que la API esté habilitada, crea una clave de API para cada cliente que tenga las restricciones de clave de API que necesites.
  2. Proporciona la clave de API a cada emisor.

Crear una clave de API para todos los emisores

Cuando no necesitas distinguir entre los emisores de la API ni necesitas agregar restricciones de API, pero deseas requerir una clave de API de todos modos (por ejemplo, para impedir el acceso anónimo), puedes crear una sola clave de API que usarán todos los emisores.

Si deseas crear una clave de API para todos los emisores, haz lo siguiente:
  1. En el proyecto en el que está configurada tu API o en un proyecto en el que la API esté habilitada, crea una clave de API para todos los emisores.
  2. Proporciona la misma clave de API a todos los emisores.

¿Qué sigue?