Visão geral da API App Identity para Java

A API App Identity permite que um aplicativo descubra o próprio código do aplicativo, também chamado de código do projeto. Com esse ID, um aplicativo do App Engine pode confirmar a própria identidade para outros aplicativos do App Engine e APIs do Google, assim como para aplicativos e serviços de terceiros. Ele também pode ser usado para gerar um URL ou endereço de e-mail ou para tomar uma decisão de tempo de execução.

Como conseguir o ID do aplicativo

O ID do aplicativo pode ser encontrado com o método ApiProxy.getCurrentEnvironment().getAppId().

Como conseguir o nome do host do aplicativo

Por padrão, os apps do App Engine são disponibilizados a partir de URLs no formato http://<your_app_id>.appspot.com, e o ID faz parte do nome do host. Se um aplicativo é suprido a partir de um domínio personalizado, talvez seja necessário recuperar todo o componente do nome do host. Para isso, use o atributo com.google.appengine.runtime.default_version_hostname do CurrentEnvironment.

Java 8

@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
  resp.setContentType("text/plain");
  ApiProxy.Environment env = ApiProxy.getCurrentEnvironment();
  resp.getWriter().print("default_version_hostname: ");
  resp.getWriter()
      .println(env.getAttributes().get("com.google.appengine.runtime.default_version_hostname"));
}

Java 7

@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
  resp.setContentType("text/plain");
  ApiProxy.Environment env = ApiProxy.getCurrentEnvironment();
  resp.getWriter().print("default_version_hostname: ");
  resp.getWriter()
      .println(env.getAttributes().get("com.google.appengine.runtime.default_version_hostname"));
}

Como confirmar a identidade para outros aplicativos do App Engine

Os aplicativos em execução no ambiente de execução do Java 8 não usam o serviço URLFetch por padrão. Para ativar o URLFetch, siga estas instruções.

Para determinar a identidade do aplicativo do App Engine que faz uma solicitação ao aplicativo do App Engine, use o cabeçalho de solicitação X-Appengine-Inbound-Appid. Esse cabeçalho é adicionado à solicitação pelo serviço URLFetch e não pode ser modificado pelo usuário. Por isso, indica com segurança o ID do aplicativo solicitante, se presente.

Para que esse cabeçalho seja adicionado à solicitação, o aplicativo que faz essa solicitação precisa informar ao serviço URLFetch para não seguir os redirecionamentos. Ou seja, o aplicativo precisa especificar doNotFollowRedirect ao usar a classe URLFetchService. Se o aplicativo usar java.net, ele precisará definir a conexão da seguinte maneira: connection.setInstanceFollowRedirects(false); O App Engine adicionará automaticamente o cabeçalho à resposta HTTP.

No gerenciador de aplicativos, verifique o código de entrada lendo o cabeçalho X-Appengine-Inbound-Appid e o comparando a uma lista de códigos com permissão para fazer solicitações.

Como confirmar a identidade das APIs do Google

As APIs do Google usam o protocolo OAuth 2.0 para autenticação e autorização. A App Identity API pode criar tokens OAuth usados para confirmar que a origem de uma solicitação é o próprio aplicativo. O método getAccessToken() retorna um token de acesso para um escopo ou lista de escopos. Esse token pode, então, ser configurado nos cabeçalhos HTTP de uma chamada para identificar o aplicativo de chamada.

No exemplo a seguir, veja como usar a API App Identity para fazer uma chamada REST para a API Google URL Shortener.

Java 8

/**
 * Returns a shortened URL by calling the Google URL Shortener API.
 *
 * <p>Note: Error handling elided for simplicity.
 */
public String createShortUrl(String longUrl) throws Exception {
  ArrayList<String> scopes = new ArrayList<String>();
  scopes.add("https://www.googleapis.com/auth/urlshortener");
  final AppIdentityService appIdentity = AppIdentityServiceFactory.getAppIdentityService();
  final AppIdentityService.GetAccessTokenResult accessToken = appIdentity.getAccessToken(scopes);
  // The token asserts the identity reported by appIdentity.getServiceAccountName()
  JSONObject request = new JSONObject();
  request.put("longUrl", longUrl);

  URL url = new URL("https://www.googleapis.com/urlshortener/v1/url?pp=1");
  HttpURLConnection connection = (HttpURLConnection) url.openConnection();
  connection.setDoOutput(true);
  connection.setRequestMethod("POST");
  connection.addRequestProperty("Content-Type", "application/json");
  connection.addRequestProperty("Authorization", "Bearer " + accessToken.getAccessToken());

  OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
  request.write(writer);
  writer.close();

  if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
    // Note: Should check the content-encoding.
    //       Any JSON parser can be used; this one is used for illustrative purposes.
    JSONTokener responseTokens = new JSONTokener(connection.getInputStream());
    JSONObject response = new JSONObject(responseTokens);
    return (String) response.get("id");
  } else {
    try (InputStream s = connection.getErrorStream();
        InputStreamReader r = new InputStreamReader(s, StandardCharsets.UTF_8)) {
      throw new RuntimeException(
          String.format(
              "got error (%d) response %s from %s",
              connection.getResponseCode(), CharStreams.toString(r), connection.toString()));
    }
  }
}

Java 7

/**
 * Returns a shortened URL by calling the Google URL Shortener API.
 *
 * <p>Note: Error handling elided for simplicity.
 */
public String createShortUrl(String longUrl) throws Exception {
  ArrayList<String> scopes = new ArrayList<String>();
  scopes.add("https://www.googleapis.com/auth/urlshortener");
  final AppIdentityService appIdentity = AppIdentityServiceFactory.getAppIdentityService();
  final AppIdentityService.GetAccessTokenResult accessToken = appIdentity.getAccessToken(scopes);
  // The token asserts the identity reported by appIdentity.getServiceAccountName()
  JSONObject request = new JSONObject();
  request.put("longUrl", longUrl);

  URL url = new URL("https://www.googleapis.com/urlshortener/v1/url?pp=1");
  HttpURLConnection connection = (HttpURLConnection) url.openConnection();
  connection.setDoOutput(true);
  connection.setRequestMethod("POST");
  connection.addRequestProperty("Content-Type", "application/json");
  connection.addRequestProperty("Authorization", "Bearer " + accessToken.getAccessToken());

  OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
  request.write(writer);
  writer.close();

  if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
    // Note: Should check the content-encoding.
    //       Any JSON parser can be used; this one is used for illustrative purposes.
    JSONTokener responseTokens = new JSONTokener(connection.getInputStream());
    JSONObject response = new JSONObject(responseTokens);
    return (String) response.get("id");
  } else {
    try (InputStream s = connection.getErrorStream();
        InputStreamReader r = new InputStreamReader(s, StandardCharsets.UTF_8)) {
      throw new RuntimeException(String.format(
          "got error (%d) response %s from %s",
          connection.getResponseCode(),
          CharStreams.toString(r),
          connection.toString()));
    }
  }
}

Observe que a identidade do aplicativo é representada pelo nome da conta do serviço, que normalmente é applicationid@appspot.gserviceaccount.com. Para conseguir o valor exato, use o método getServiceAccountName(). Para serviços que oferecem ACLs, você pode conceder o acesso ao aplicativo concedendo este acesso à conta.

Como afirmar a identidade para serviços de terceiros

O token gerado por getAccessToken() só funciona nos serviços do Google. No entanto, você pode usar a tecnologia de assinatura subjacente para afirmar a identidade do seu aplicativo para outros serviços. O método signForApp() assinará bytes usando uma chave particular exclusiva para seu aplicativo e o método getPublicCertificatesForApp() retornará certificados que podem ser usados para validar a assinatura.

Veja um exemplo que mostra como assinar um blob e validar sua assinatura:

Java 8

// Note that the algorithm used by AppIdentity.signForApp() and
// getPublicCertificatesForApp() is "SHA256withRSA"

private byte[] signBlob(byte[] blob) {
  AppIdentityService.SigningResult result = appIdentity.signForApp(blob);
  return result.getSignature();
}

private byte[] getPublicCertificate() throws UnsupportedEncodingException {
  Collection<PublicCertificate> certs = appIdentity.getPublicCertificatesForApp();
  PublicCertificate publicCert = certs.iterator().next();
  return publicCert.getX509CertificateInPemFormat().getBytes("UTF-8");
}

private Certificate parsePublicCertificate(byte[] publicCert)
    throws CertificateException, NoSuchAlgorithmException {
  InputStream stream = new ByteArrayInputStream(publicCert);
  CertificateFactory cf = CertificateFactory.getInstance("X.509");
  return cf.generateCertificate(stream);
}

private boolean verifySignature(byte[] blob, byte[] blobSignature, PublicKey pk)
    throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
  Signature signature = Signature.getInstance("SHA256withRSA");
  signature.initVerify(pk);
  signature.update(blob);
  return signature.verify(blobSignature);
}

private String simulateIdentityAssertion()
    throws CertificateException, UnsupportedEncodingException, NoSuchAlgorithmException,
    InvalidKeyException, SignatureException {
  // Simulate the sending app.
  String message = "abcdefg " + Calendar.getInstance().getTime().toString();
  byte[] blob = message.getBytes();
  byte[] blobSignature = signBlob(blob);
  byte[] publicCert = getPublicCertificate();

  // Simulate the receiving app, which gets the certificate, blob, and signature.
  Certificate cert = parsePublicCertificate(publicCert);
  PublicKey pk = cert.getPublicKey();
  boolean isValid = verifySignature(blob, blobSignature, pk);

  return String.format(
      "isValid=%b for message: %s\n\tsignature: %s\n\tpublic cert: %s",
      isValid, message, Arrays.toString(blobSignature), Arrays.toString(publicCert));
}

Java 7

// Note that the algorithm used by AppIdentity.signForApp() and
// getPublicCertificatesForApp() is "SHA256withRSA"

private byte[] signBlob(byte[] blob) {
  AppIdentityService.SigningResult result = appIdentity.signForApp(blob);
  return result.getSignature();
}

private byte[] getPublicCertificate() throws UnsupportedEncodingException {
  Collection<PublicCertificate> certs = appIdentity.getPublicCertificatesForApp();
  PublicCertificate publicCert = certs.iterator().next();
  return publicCert.getX509CertificateInPemFormat().getBytes("UTF-8");
}

private Certificate parsePublicCertificate(byte[] publicCert)
    throws CertificateException, NoSuchAlgorithmException {
  InputStream stream = new ByteArrayInputStream(publicCert);
  CertificateFactory cf = CertificateFactory.getInstance("X.509");
  return cf.generateCertificate(stream);
}

private boolean verifySignature(byte[] blob, byte[] blobSignature, PublicKey pk)
    throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
  Signature signature = Signature.getInstance("SHA256withRSA");
  signature.initVerify(pk);
  signature.update(blob);
  return signature.verify(blobSignature);
}

private String simulateIdentityAssertion()
    throws CertificateException, UnsupportedEncodingException, NoSuchAlgorithmException,
           InvalidKeyException, SignatureException {
  // Simulate the sending app.
  String message = "abcdefg";
  byte[] blob = message.getBytes();
  byte[] blobSignature = signBlob(blob);
  byte[] publicCert = getPublicCertificate();

  // Simulate the receiving app, which gets the certificate, blob, and signature.
  Certificate cert = parsePublicCertificate(publicCert);
  PublicKey pk = cert.getPublicKey();
  boolean isValid = verifySignature(blob, blobSignature, pk);

  return String.format(
      "isValid=%b for message: %s\n\tsignature: %s\n\tpublic cert: %s",
      isValid,
      message,
      Arrays.toString(blobSignature),
      Arrays.toString(publicCert));
}

Como encontrar o nome padrão do intervalo do Cloud Storage

Cada aplicativo pode ter um intervalo padrão do Cloud Storage, que inclui 5 GB de armazenamento gratuito e uma cota gratuita para operações de E/S. O armazenamento máximo desse intervalo é de 5 GB. Aumente-o ativando o faturamento do seu aplicativo e pagando pelo armazenamento extra, tornando este um intervalo pago.

Para conseguir o nome do intervalo padrão, use a API App Identity. Chame AppIdentityService.getDefaultGcsBucketName.

Esta página foi útil? Conte sua opinião sobre:

Enviar comentários sobre…

Ambiente padrão do App Engine para Java 8