Como restringir o acesso à API com as chaves de API

Use as chaves da API para restringir o acesso a métodos específicos ou a todos os métodos em uma API. Nesta página, você aprende a restringir o acesso à API para os clientes com uma chave, bem como a criar uma chave de API. Nesta página, você aprende a restringir o acesso à API para os clientes com uma chave, bem como a criar uma chave de API.

O Extensible Service Proxy (ESP) usa a API Service Control para validar uma chave de API e a associação com a API ativada de um projeto. Se você definir um requisito de chave para uma API, as solicitações para métodos, classes ou APIs protegidos serão rejeitadas a menos que elas tenham uma chave gerada no seu projeto ou em outros projetos de desenvolvedores com acesso para ativar sua API. O projeto em que a chave de API foi criada não é registrado nem adicionado ao cabeçalho da solicitação. No entanto, você pode visualizar o Google Cloud projeto ao qual um cliente está associado em Endpoints > Serviço, conforme descrito na Filtrar por projetos de um consumidor específico.

Para mais informações sobre em qual Google Cloud projeto uma chave de API precisa ser criada, consulte Como compartilhar APIs protegidas por chaves.

Por padrão, nos serviços do gRPC, todos os métodos da API exigem uma chave de API para acessá-los. É possível desativar a exigência de chave de API para toda a API ou para métodos específicos. Para isso, adicione uma seção usage à configuração de serviço e defina regras e seletores, conforme descrito nos procedimentos a seguir.

Como restringir ou conceder acesso a todos os métodos da API

Para especificar que uma chave de API não é necessária para acessar a API:

  1. Abra o arquivo de configurações de serviço gRPC do projeto em um editor de texto e localize ou adicione a seção usage.

  2. Na seção usage, especifique uma regra allow_unregistered_calls, conforme mostrado a seguir. O caractere curinga "*" em selector significa que a regra aplica-se a todos os métodos na API.

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

Como remover a restrição de chave de API de um método

Para desativar a validação da chave de API de um método específico mesmo que você tenha restringido o acesso da API:

  1. Abra o arquivo de configurações de serviço do gRPC do projeto em um editor de texto e localize ou adicione a seção usage:

  2. Na seção usage, especifique uma regra allow_unregistered_calls, conforme mostrado a seguir. O selector significa que a regra aplica-se apenas ao método especificado, neste caso, ListShelves.

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

Como chamar uma API usando uma chave de API

A chamada a uma API varia, dependendo de a origem ser um cliente gRPC ou um cliente HTTP.

Clientes gRPC

Quando um método requer uma chave de API, é preciso que os clientes do gRPC transmitam o valor da chave como metadados x-api-key (em inglês) com a chamada de método deles.

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

Se você estiver usando o recurso de transcodificação de HTTP do Cloud Endpoints para gRPC, os clientes HTTP poderão enviar a chave como um parâmetro de consulta da mesma maneira que fazem para os serviços OpenAPI.

Como compartilhar APIs protegidas por chaves

As chaves de API são associadas ao Google Cloud projeto em que foram criadas. Se você decidiu exigir uma chave de API para sua API, o Google Cloud projeto em que a chave de API é criada depende de as respostas às seguintes perguntas:

  • Você precisa diferenciar os chamadores de sua API para poder usar recursos do Endpoints, como cotas?
  • Todos os autores das chamadas da API têm os próprios Google Cloud projetos?
  • Você precisa configurar diferentes restrições de chave de API?

Use a árvore de decisões abaixo como guia para decidir em qual Google Cloud projeto criar a chave de API.

Árvore de decisão de chave de API

Conceder permissão para ativar a API

Quando você precisa distinguir os autores das chamadas da sua API, e cada um tem o próprio Google Cloud projeto, é possível conceder permissão aos principais para ativar a API no próprio Google Cloud projeto. Dessa forma, os usuários da sua API podem criar a própria chave de API para uso com ela.

Por exemplo, digamos que sua equipe tenha criado uma API para uso interno por diversos programas cliente na empresa e cada um deles tenha o próprio Google Cloud projeto. Para diferenciar os autores das chamadas da API, a chave de cada um deles precisa ser criada em um Google Cloud projeto diferente. Você pode conceder permissão para que seus colegas ativem a API no Google Cloud projeto ao qual o programa cliente está associado.

Veja como permitir que os usuários criem a própria chave de API:

  1. Noprojeto em que a API está configurada, conceda a cada usuário a permissão para ativar a API. Google Cloud
  2. Entre em contato com os usuários e informe que eles podem ativar a API no próprio Google Cloud projeto e criar uma chave de API.

Criar um Google Cloud projeto separado para cada autor da chamada

Quando você precisar diferenciar os autores das chamadas da API, mas nem todos tiverem Google Cloud projetos, crie um Google Cloud projeto separado e uma chave de API para cada um. Antes de criar os projetos, pense nos nomes deles para facilitar a identificação do cliente associado a cada um.

Por exemplo, digamos que você tenha clientes externos da API e você não tenha ideia de como foram criados os programas clientes que chamam essa API. Talvez alguns dos clientes usem Google Cloud serviços e tenham um Google Cloud projeto, e talvez isso não aconteça com outros clientes. Para distinguir entre os autores das chamadas, crie um projeto separado Google Cloud e uma chave de API para cada um.

Para criar um Google Cloud projeto separado e uma chave de API para cada autor da chamada:

  1. Crie um projeto separado para cada autor da chamada.
  2. Em cada projeto, ative a API e crie uma chave de API.
  3. Forneça a chave de API para cada autor da chamada.

Criar uma chave de API para cada autor da chamada

Quando você não precisa distinguir entre os autores das chamadas de API, mas quer adicionar restrições de chave de API, é possível criar uma chave de API separada para cada autor da chamada no mesmo projeto.

Veja como criar uma chave de API para cada autor da chamada no mesmo projeto:

  1. No projeto em que a API estiver ativada ou configurada, crie uma chave de API para cada cliente com as restrições necessárias.
  2. Forneça a chave de API para cada autor da chamada.

Criar uma chave de API para todos os autores das chamadas

Quando você não precisa distinguir entre os autores das chamadas de API e não precisa adicionar restrições, mas ainda quer exigir uma chave de API (para impedir o acesso anônimo, por exemplo), é possível criar uma chave para todos os autores de chamadas usarem.

Veja como criar uma chave de API para todos os autores das chamadas:
  1. No projeto em que a API estiver ativada ou configurada, crie uma chave de API para todos os autores das chamadas com as restrições necessárias.
  2. Forneça a mesma chave de API para cada autor da chamada.

Restrições de aplicativo

As restrições de aplicativo especificam quais sites, endereços IP ou apps podem usar sua chave de API. Para mais informações, consulte Adicionar restrições de aplicativos.

Observação: se você usar referenciadores HTTP (sites) como uma restrição de aplicativo, inclua um esquema (por exemplo, https://) ao adicionar a restrição do site. Por exemplo, https://example.com/* é uma restrição válida, mas example.com/* não é.

A seguir