Restringir el acceso a las APIs con claves de API

Puedes usar claves de API para restringir el acceso a métodos de API específicos o a todos los métodos de 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 cómo crear una clave de API.

El proxy de servicios extensible (ESP) usa la API Service Control para validar una clave de API y su asociación con la API habilitada de un proyecto. Si estableces un requisito de clave de API en tu API, las solicitudes al método, la clase o la API protegidos se rechazarán a menos que tengan una clave generada en tu proyecto o en otros proyectos pertenecientes a desarrolladores a los que hayas concedido acceso para habilitar tu API. El proyecto en el que se creó la clave de API no se registra y no se añade al encabezado de la solicitud. Sin embargo, puede ver el Google Cloud proyecto al que está asociado un cliente en Puntos finales > Servicio, tal como se describe en Filtrar por un proyecto de consumidor específico.

Para obtener información sobre en qué Google Cloud proyecto se debe crear una clave de API, consulta Compartir APIs protegidas por claves de API.

De forma predeterminada, en los servicios gRPC, todos los métodos de la API requieren una clave de API para acceder a ellos. Puedes inhabilitar el requisito de clave de API para toda la API o para métodos específicos. Para ello, añade una sección usage a la configuración del servicio y configura reglas y selectores, tal como se describe en los siguientes procedimientos.

Restringir o conceder acceso a todos los métodos de la API

Para 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 gRPC de tu proyecto en un editor de texto y busca o añade una sección usage.

  2. En la sección usage, especifica una regla allow_unregistered_calls de la siguiente manera. El comodín "*" de 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
    

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

Para desactivar la validación de la clave de API de un método concreto, aunque hayas restringido el acceso a la API:

  1. Abre el archivo de configuración del servicio gRPC de tu proyecto en un editor de texto y busca o añade una sección usage:

  2. En la sección usage, especifica una regla allow_unregistered_calls de la siguiente manera. El símbolo 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
    

Llamar a una API con una clave de API

La forma de llamar a una API varía en función de si lo haces desde un cliente gRPC o un cliente HTTP.

Clientes gRPC

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

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

Clientes HTTP

Si usas la función de transcodificación HTTP de Cloud Endpoints para gRPC, los clientes HTTP pueden enviar la clave como un parámetro de consulta del mismo modo que lo hacen con los servicios OpenAPI.

Compartir APIs protegidas con una clave de API

Las claves de API están asociadas al Google Cloud proyecto en el que se han creado. Si has decidido que tu API requiera una clave de API, el Google Cloud proyecto en el que se cree la clave de API dependerá de las respuestas a las siguientes preguntas:

  • ¿Necesitas distinguir entre los llamantes de tu API para poder usar funciones de Endpoints, como las cuotas?
  • ¿Todos los usuarios que llaman a tu API tienen sus propios Google Cloud proyectos?
  • ¿Necesitas configurar diferentes restricciones de clave de API?

Puedes usar el siguiente diagrama de flujo para decidir en qué Google Cloud proyecto crear la clave de API.

Árbol de decisión de la clave de API

Concede permiso para habilitar la API

Si necesitas distinguir entre los llamantes de tu API y cada llamante tiene su propio proyecto, puedes conceder a las principales permiso para habilitar la API en su propio proyecto. Google Cloud Google Cloud De esta forma, los usuarios de tu API pueden crear su propia clave de API para usarla con tu API.

Por ejemplo, supongamos que tu equipo ha creado una API para uso interno de varios programas cliente de tu empresa y que cada programa cliente tiene su propio Google Cloud proyecto. Para distinguir entre los llamantes de tu API, la clave de API de cada llamante debe crearse en un proyecto Google Cloud diferente. Puedes conceder permiso a tus compañeros para que habiliten la API en el Google Cloud proyecto al que esté asociado el programa cliente.

Para permitir que los usuarios creen su propia clave de API, sigue estos pasos:

  1. En el Google Cloud proyecto en el que esté configurada tu API, concede a cada usuario el permiso para habilitar tu API.
  2. Ponte en contacto con los usuarios e infórmales de que pueden habilitar tu API en su propio proyecto Google Cloud y crear una clave de API.

Crea un Google Cloud proyecto independiente para cada persona que llame

Si necesitas distinguir entre los llamantes de tu API y no todos los llamantes tienen proyectos de Google Cloud , puedes crear un proyecto de Google Cloud y una clave de API independientes para cada llamante. Antes de crear los proyectos, piensa en los nombres que les vas a dar para poder identificar fácilmente la persona que llama asociada a cada proyecto.

Por ejemplo, supongamos que tienes clientes externos de tu API y no sabes cómo se crearon los programas de cliente que llaman a tu API. Puede que algunos clientes usen Google Cloud servicios y tengan un Google Cloud proyecto, y puede que otros no. Para distinguir entre los llamantes, debes crear un Google Cloud proyecto y una clave de API independientes para cada uno.

Para crear un proyecto y una clave de API independientes para cada llamante, sigue estos pasos: Google Cloud

  1. Crea un proyecto independiente para cada persona que llame.
  2. En cada proyecto, habilita la API y crea una clave de API.
  3. Proporciona la clave de API a cada persona que llame.

Crear una clave de API para cada llamante

Si no necesitas distinguir entre los llamantes de tu API, pero quieres añadir restricciones a la clave de API, puedes crear una clave de API independiente para cada llamante en el mismo proyecto.

Para crear una clave de API para cada llamante del mismo proyecto, sigue estos pasos:

  1. En el proyecto en el que esté configurada tu API o en un proyecto en el que 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 persona que llame.

Crear una clave de API para todos los llamantes

Si no necesitas distinguir entre los llamantes de tu API ni añadir restricciones de API, pero quieres requerir una clave de API (por ejemplo, para evitar el acceso anónimo), puedes crear una clave de API para que la usen todos los llamantes.

Para crear una clave de API para todos los llamantes, sigue estos pasos:
  1. En el proyecto en el que esté configurada tu API o en un proyecto en el que esté habilitada, crea una clave de API para todos los llamantes que tenga las restricciones de clave de API que necesites.
  2. Proporcionar la misma clave de API a todos los llamantes.

Siguientes pasos