Descripción general de la API de App Identity para Java

La API de App Identity permite que una aplicación descubra su ID de aplicación (también llamada ID del proyecto). A través del ID, una aplicación de App Engine puede confirmar su identidad a otras aplicaciones de App Engine, a las API de Google, y a las aplicaciones y servicios de terceros. El ID de aplicación también se puede usar con el fin de generar una URL o dirección de correo electrónico, o para tomar una decisión en tiempo de ejecución.

Cómo obtener el ID de aplicación

El ID de aplicación se puede encontrar con el método ApiProxy.getCurrentEnvironment().getAppId().

Cómo obtener el nombre de host de la aplicación

De forma predeterminada, las aplicaciones de App Engine se entregan desde las URL en el formato http://<your_app_id>.appspot.com, cuando el ID de aplicación es parte del nombre de host. Si una aplicación se entrega desde un dominio personalizado, tal vez sea necesario recuperar todo el componente del nombre de host. Puedes hacerlo con el atributo com.google.appengine.runtime.default_version_hostname de 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"));
}

Cómo confirmar la identidad a otras aplicaciones de App Engine

Las aplicaciones que se ejecutan en el entorno de ejecución Java 8 no usan el servicio de recuperación de URL de forma predeterminada. Para habilitar la recuperación de URL, sigue las instrucciones a continuación:

Si deseas determinar la identidad de la aplicación de App Engine que está realizando una solicitud a tu aplicación, puedes usar el encabezado de solicitud X-Appengine-Inbound-Appid. El servicio de recuperación de URL agrega este encabezado a la solicitud y el usuario no puede modificarlo, por lo que indica de manera segura el ID de aplicación solicitante, si está presente.

Para que este encabezado se agregue a la solicitud, la aplicación que realiza la solicitud debe indicar al servicio de recuperación de URL que no siga los redireccionamientos. Es decir, tu aplicación debe especificar un doNotFollowRedirect si usa la clase URLFetchService. Si tu aplicación usa java.net, entonces esta debe establecer la conexión de la siguiente forma: connection.setInstanceFollowRedirects(false);; luego, App Engine agregará el encabezado a la respuesta HTTP automáticamente.

En el controlador de la aplicación, puedes verificar el ID entrante leyendo el encabezado X-Appengine-Inbound-Appid y comparándolo con una lista de ID permitidas para realizar solicitudes.

Cómo confirmar la identidad a las API de Google

Las API de Google usan el protocolo OAuth 2.0 para la autenticación y la autorización La API de App Identity de la aplicación puede crear tokens OAuth que pueden usarse para confirmar que el origen de una solicitud es la aplicación en sí. El método getAccessToken() muestra un token de acceso para un alcance o lista de alcances. Este token se puede configurar en los encabezados HTTP de una llamada para identificar la aplicación que realiza la llamada.

El siguiente ejemplo muestra cómo usar la API de App Identity para realizar una llamada REST a la API del Reductor de URL de Google.

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

Ten en cuenta que la identidad de la aplicación está representada por el nombre de la cuenta de servicio, que suele ser applicationid@appspot.gserviceaccount.com. Puedes obtener el valor exacto con el método getServiceAccountName(). Para los servicios que ofrecen LCA, puedes otorgar acceso a la aplicación brindando acceso a esta cuenta.

Cómo confirmar la identidad a servicios de terceros

El token que genera getAccessToken() solo funciona en los servicios de Google. Sin embargo, puedes usar la tecnología de firma subyacente para confirmar la identidad de tu aplicación a otros servicios. El método signForApp() firmará bytes a través de una clave privada única para tu aplicación, y el método getPublicCertificatesForApp() mostrará los certificados que se pueden usar para validar la firma.

A continuación, puedes ver un ejemplo que muestra cómo firmar un BLOB y validar su firma:

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

Cómo obtener el nombre predeterminado del depósito de Cloud Storage

Cada aplicación puede tener un depósito de Cloud Storage predeterminado, que incluye 5 GB de almacenamiento gratuito y una cuota gratuita para operaciones de E/S. El almacenamiento máximo de este depósito es de 5 GB, el cual puedes aumentar cuando habilites la facturación para tu aplicación. Esto dará como resultado un depósito pago.

Para obtener el nombre del depósito predeterminado, puedes usar la API de App Identity. Llama a AppIdentityService.getDefaultGcsBucketName.

¿Te ha resultado útil esta página? Enviar comentarios:

Enviar comentarios sobre...

Entorno estándar de App Engine para Java 8