Sklep blokowy

Wielu użytkowników nadal zarządza własnymi danymi logowania podczas konfigurowania nowego urządzenia z Androidem. Ręczne wykonywanie tego typu zadań bywa nie lada wyzwaniem, a wrażenia użytkowników często są niewygodne. Block Store API, który obsługuje Usługi Google Play, chce rozwiązać ten problem, umożliwiając aplikacjom zapisywanie danych logowania użytkowników bez naruszania ich bezpieczeństwa i ryzyka związanego z ich zapisywaniem.

Interfejs Block Store API umożliwia aplikacji zapisywanie danych, które będzie można później pobrać na nowe urządzenie. Zwiększa to wygodę użytkownika, ponieważ przy pierwszym uruchomieniu aplikacji na nowym urządzeniu nie widzi ekranu logowania.

Zalety korzystania z klocków:

  • Rozwiązanie dla programistów do szyfrowania danych logowania. Dane logowania są w pełni szyfrowane, jeśli to możliwe.
  • Zapisz tokeny zamiast nazw użytkowników i haseł.
  • Wyeliminuj problemy z logowaniem.
  • Unikniesz zarządzania złożonymi hasłami.
  • Google weryfikuje tożsamość użytkownika.

Zanim zaczniesz

Aby przygotować aplikację, wykonaj czynności opisane w poniższych sekcjach.

Konfiguracja aplikacji

W pliku build.gradle na poziomie projektu umieść repozytorium Google Maven w sekcjach buildscript i allprojects:

buildscript {
  repositories {
    google()
    mavenCentral()
  }
}

allprojects {
  repositories {
    google()
    mavenCentral()
  }
}

Dodaj zależność interfejsu Google Play Services od interfejsu Block Store API do pliku kompilacji Gradle modułu, który zazwyczaj stanowi app/build.gradle:

dependencies {
  implementation 'com.google.android.gms:play-services-auth-blockstore:16.2.0'
}

Jak to działa

Block Store umożliwia programistom zapisywanie i przywracanie tablic o rozmiarze do 16 bajtów. Dzięki temu można zapisać ważne informacje dotyczące bieżącej sesji użytkownika i w dowolny sposób zapisać te informacje. Te dane mogą być w pełni szyfrowane, a infrastruktura obsługująca Block Store jest oparta na infrastrukturze tworzenia i przywracania kopii zapasowych.

Z tego przewodnika dowiesz się, jak zapisać token użytkownika w magazynie zablokowanych. Poniżej pokazujemy, jak działa aplikacja, która korzysta z blokady:

  1. Podczas procesu uwierzytelniania w aplikacji lub w dowolnym momencie później możesz zapisać token uwierzytelniania użytkownika w celu zablokowania sklepu do późniejszego pobrania.
  2. Token będzie przechowywany lokalnie i można w miarę możliwości tworzyć jego pełne kopie zapasowe w chmurze.
  3. Dane są przenoszone, gdy użytkownik rozpocznie proces przywracania na nowym urządzeniu.
  4. Jeśli w trakcie procesu przywracania użytkownik przywróci Twoją aplikację, będzie ona mogła pobrać zapisany token z Block Store na nowym urządzeniu.

Zapisywanie tokena

Gdy użytkownik loguje się w Twojej aplikacji, możesz zapisać token uwierzytelniania wygenerowany dla tego użytkownika w celu zablokowania sklepu. Możesz zapisać ten token, korzystając z unikalnej wartości pary kluczy o maksymalnym rozmiarze 4 KB na wpis. Aby zapisać token, wywołaj metody setBytes() i setKey() w instancji StoreBytesData.Builder w celu zapisania danych logowania użytkownika na urządzeniu źródłowym. Gdy zapiszesz token w usłudze Block Store, zostanie on zaszyfrowany i zapisany lokalnie na urządzeniu.

Poniższy przykład pokazuje, jak zapisać token uwierzytelniania na urządzeniu lokalnym:

Java

  BlockstoreClient client = Blockstore.getClient(this);
  byte[] bytes1 = new byte[] { 1, 2, 3, 4 };  // Store one data block.
  String key1 = "com.example.app.key1";
  StoreBytesData storeRequest1 = StoreBytesData.Builder()
          .setBytes(bytes1)
          // Call this method to set the key value pair the data should be associated with.
          .setKeys(Arrays.asList(key1))
          .build();
  client.storeBytes(storeRequest1)
    .addOnSuccessListener(result -> Log.d(TAG, "stored " + result + " bytes"))
    .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));

Kotlin

  val client = Blockstore.getClient(this)

  val bytes1 = byteArrayOf(1, 2, 3, 4) // Store one data block.
  val key1 = "com.example.app.key1"
  val storeRequest1 = StoreBytesData.Builder()
    .setBytes(bytes1) // Call this method to set the key value with which the data should be associated with.
    .setKeys(Arrays.asList(key1))
    .build()
  client.storeBytes(storeRequest1)
    .addOnSuccessListener { result: Int ->
      Log.d(TAG,
            "Stored $result bytes")
    }
    .addOnFailureListener { e ->
      Log.e(TAG, "Failed to store bytes", e)
    }

Użyj tokena domyślnego

Dane zapisane przy użyciu StoreBytes bez klucza używają domyślnego klucza BlockstoreClient.DEFAULT_BYTES_DATA_KEY.

Java

  BlockstoreClient client = Blockstore.getClient(this);
  // The default key BlockstoreClient.DEFAULT_BYTES_DATA_KEY.
  byte[] bytes = new byte[] { 9, 10 };
  StoreBytesData storeRequest = StoreBytesData.Builder()
          .setBytes(bytes)
          .build();
  client.storeBytes(storeRequest)
    .addOnSuccessListener(result -> Log.d(TAG, "stored " + result + " bytes"))
    .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));

Kotlin

  val client = Blockstore.getClient(this);
  // the default key BlockstoreClient.DEFAULT_BYTES_DATA_KEY.
  val bytes = byteArrayOf(1, 2, 3, 4)
  val storeRequest = StoreBytesData.Builder()
    .setBytes(bytes)
    .build();
  client.storeBytes(storeRequest)
    .addOnSuccessListener { result: Int ->
      Log.d(TAG,
            "stored $result bytes")
    }
    .addOnFailureListener { e ->
      Log.e(TAG, "Failed to store bytes", e)
    }

Pobieram token

Później, gdy użytkownik przeprowadzi proces przywracania na nowym urządzeniu, Usługi Google Play najpierw je zweryfikują, a potem pobiorą dane z blokady. W ramach procesu przywracania użytkownik zgodził się już na przywrócenie danych aplikacji, więc nie są wymagane żadne dodatkowe zgody. Gdy użytkownik otworzy Twoją aplikację, możesz poprosić o token z Block Store, wywołując metodę retrieveBytes(). Pobrany token będzie mógł następnie służyć do utrzymywania aktywności użytkownika na nowym urządzeniu.

Poniższy przykład pokazuje, jak pobrać wiele tokenów na podstawie konkretnych kluczy.

Java

BlockstoreClient client = Blockstore.getClient(this);

// Retrieve data associated with certain keys.
String key1 = "com.example.app.key1";
String key2 = "com.example.app.key2";
String key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY; // Used to retrieve data stored without a key

List requestedKeys = Arrays.asList(key1, key2, key3); // Add keys to array
RetrieveBytesRequest retrieveRequest = new RetrieveBytesRequest.Builder()
    .setKeys(requestedKeys)
    .build();

client.retrieveBytes(retrieveRequest)
    .addOnSuccessListener(
        result -> {
          Map blockstoreDataMap = result.getBlockstoreDataMap();
          for (Map.Entry entry : blockstoreDataMap.entrySet()) {
            Log.d(TAG, String.format(
                "Retrieved bytes %s associated with key %s.",
                new String(entry.getValue().getBytes()), entry.getKey()));
          }
        })
    .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));

Kotlin

val client = Blockstore.getClient(this)

// Retrieve data associated with certain keys.
val key1 = "com.example.app.key1"
val key2 = "com.example.app.key2"
val key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY // Used to retrieve data stored without a key

val requestedKeys = Arrays.asList(key1, key2, key3) // Add keys to array

val retrieveRequest = RetrieveBytesRequest.Builder()
  .setKeys(requestedKeys)
  .build()

client.retrieveBytes(retrieveRequest)
  .addOnSuccessListener { result: RetrieveBytesResponse ->
    val blockstoreDataMap =
      result.blockstoreDataMap
    for ((key, value) in blockstoreDataMap) {
      Log.d(ContentValues.TAG, String.format(
        "Retrieved bytes %s associated with key %s.",
        String(value.bytes), key))
    }
  }
  .addOnFailureListener { e: Exception? ->
    Log.e(ContentValues.TAG,
          "Failed to store bytes",
          e)
  }

Pobieram wszystkie tokeny.

Poniżej znajdziesz przykład pobrania wszystkich tokenów zapisanych w BlockStore.

Java

BlockstoreClient client = Blockstore.getClient(this)

// Retrieve all data.
RetrieveBytesRequest retrieveRequest = new RetrieveBytesRequest.Builder()
    .setRetrieveAll(true)
    .build();

client.retrieveBytes(retrieveRequest)
    .addOnSuccessListener(
        result -> {
          Map blockstoreDataMap = result.getBlockstoreDataMap();
          for (Map.Entry entry : blockstoreDataMap.entrySet()) {
            Log.d(TAG, String.format(
                "Retrieved bytes %s associated with key %s.",
                new String(entry.getValue().getBytes()), entry.getKey()));
          }
        })
    .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));

Kotlin

val client = Blockstore.getClient(this)

val retrieveRequest = RetrieveBytesRequest.Builder()
  .setRetrieveAll(true)
  .build()

client.retrieveBytes(retrieveRequest)
  .addOnSuccessListener { result: RetrieveBytesResponse ->
    val blockstoreDataMap =
      result.blockstoreDataMap
    for ((key, value) in blockstoreDataMap) {
      Log.d(ContentValues.TAG, String.format(
        "Retrieved bytes %s associated with key %s.",
        String(value.bytes), key))
    }
  }
  .addOnFailureListener { e: Exception? ->
    Log.e(ContentValues.TAG,
          "Failed to store bytes",
          e)
  }

Poniżej znajdziesz przykład pobierania klucza domyślnego.

Java

BlockStoreClient client = Blockstore.getClient(this);
RetrieveBytesRequest retrieveRequest = new RetrieveBytesRequest.Builder()
    .setKeys(Arrays.asList(BlockstoreClient.DEFAULT_BYTES_DATA_KEY))
    .build();
client.retrieveBytes(retrieveRequest);

Kotlin

val client = Blockstore.getClient(this)

val retrieveRequest = RetrieveBytesRequest.Builder()
  .setKeys(Arrays.asList(BlockstoreClient.DEFAULT_BYTES_DATA_KEY))
  .build()
client.retrieveBytes(retrieveRequest)

Usuwam tokeny

Usuwanie tokenów z StoreStore może być wymagane z tych powodów:

  • Proces wylogowywania użytkownika
  • Token został unieważniony lub nieprawidłowy.

Podobnie jak w przypadku pobierania tokenów, możesz wskazać, które z nich wymagają usunięcia, ustawiając tablicę kluczy do usunięcia.

Poniżej znajdziesz przykład usuwania niektórych kluczy.

Java

BlockstoreClient client = Blockstore.getClient(this);

// Delete data associated with certain keys.
String key1 = "com.example.app.key1";
String key2 = "com.example.app.key2";
String key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY; // Used to delete data stored without key

List requestedKeys = Arrays.asList(key1, key2, key3) // Add keys to array
DeleteBytesRequest deleteRequest = new DeleteBytesRequest.Builder()
      .setKeys(requestedKeys)
      .build();
client.deleteBytes(deleteRequest)

Kotlin

val client = Blockstore.getClient(this)

// Retrieve data associated with certain keys.
val key1 = "com.example.app.key1"
val key2 = "com.example.app.key2"
val key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY // Used to retrieve data stored without a key

val requestedKeys = Arrays.asList(key1, key2, key3) // Add keys to array

val retrieveRequest = DeleteBytesRequest.Builder()
      .setKeys(requestedKeys)
      .build()

client.deleteBytes(retrieveRequest)

Usuń wszystkie tokeny

Ten przykład usuwa wszystkie tokeny zapisane obecnie w BlockStore:

Java

// Delete all data.
DeleteBytesRequest deleteAllRequest = new DeleteBytesRequest.Builder()
      .setDeleteAll(true)
      .build();
client.deleteBytes(deleteAllRequest)
.addOnSuccessListener(result -> Log.d(TAG, "Any data found and deleted? " + result));

Kotlin

  val deleteAllRequest = DeleteBytesRequest.Builder()
  .setDeleteAll(true)
  .build()
client.deleteBytes(deleteAllRequest)
  .addOnSuccessListener { result: Boolean ->
    Log.d(TAG,
          "Any data found and deleted? $result")
  }

Pełne szyfrowanie

Aby można było korzystać z pełnego szyfrowania, urządzenie musi mieć Androida w wersji 9 lub nowszej, a użytkownik musi ustawić na urządzeniu blokadę ekranu (kod PIN, wzór lub hasło). Aby sprawdzić, czy szyfrowanie jest dostępne na urządzeniu, wywołaj metodę isEndToEndEncryptionAvailable().

Poniższy przykład pokazuje, jak sprawdzić, czy szyfrowanie jest dostępne podczas tworzenia kopii zapasowej w chmurze:

client.isEndToEndEncryptionAvailable()
        .addOnSuccessListener { result ->
          Log.d(TAG, "Will Block Store cloud backup be end-to-end encrypted? $result")
        }

Włącz kopię zapasową w chmurze

Aby włączyć kopię zapasową w chmurze, dodaj metodę setShouldBackupToCloud() do obiektu StoreBytesData. Block będzie okresowo tworzyć kopie zapasowe w chmurze, aby przechowywać w bajtach zapisane bajty, gdy zasada setShouldBackupToCloud() ma wartość Prawda.

Poniższy przykład pokazuje, jak włączyć kopię zapasową w chmurze tylko wtedy, gdy kopia zapasowa w chmurze jest w pełni szyfrowana:

val client = Blockstore.getClient(this)
val storeBytesDataBuilder = StoreBytesData.Builder()
        .setBytes(/* BYTE_ARRAY */)

client.isEndToEndEncryptionAvailable()
        .addOnSuccessListener { isE2EEAvailable ->
          if (isE2EEAvailable) {
            storeBytesDataBuilder.setShouldBackupToCloud(true)
            Log.d(TAG, "E2EE is available, enable backing up bytes to the cloud.")

            client.storeBytes(storeBytesDataBuilder.build())
                .addOnSuccessListener { result ->
                  Log.d(TAG, "stored: ${result.getBytesStored()}")
                }.addOnFailureListener { e ->
                  Log.e(TAG, “Failed to store bytes”, e)
                }
          } else {
            Log.d(TAG, "E2EE is not available, only store bytes for D2D restore.")
          }
        }

Testowanie

Aby przetestować proces przywracania, podczas programowania użyj tych metod.

Odinstalowanie/ponowne zainstalowanie tego samego urządzenia

Jeśli użytkownik włączy usługi kopii zapasowej (można to sprawdzić w sekcji Ustawienia > Google > Kopia zapasowa), dane blokowania w sklepie będą przechowywane przez odinstalowanie/ponowne zainstalowanie aplikacji.

Aby to sprawdzić, wykonaj te czynności:

  1. Zintegruj z aplikacją testową interfejs API StoreStore.
  2. Użyj aplikacji testowej do wywołania interfejsu BlockStore API w celu przechowywania danych.
  3. Odinstaluj aplikację testową, a potem zainstaluj ją ponownie na tym samym urządzeniu.
  4. Aby pobrać swoje dane, użyj aplikacji testowej do wywołania interfejsu BlockStore API.
  5. Sprawdź, czy pobrane bajty są takie same jak te zapisane przed odinstalowaniem.

Z urządzenia na urządzenie

W większości przypadków musisz przywrócić na urządzeniu docelowym ustawienia fabryczne. Następnie możesz wprowadzić proces przywracania Androida bezprzewodowo lub przywrócenie kabla Google (w przypadku obsługiwanych urządzeń).

Przywracanie chmury

  1. Zintegruj aplikację Blockstore API z aplikacją testową. Aplikacja testowa musi zostać przesłana do Sklepu Play.
  2. Na urządzeniu źródłowym użyj aplikacji testowej, aby wywołać interfejs Blockstore API w celu przechowywania danych. Ustawienie parametru BackBackUpToCloud musi mieć wartość „true” (prawda).
  3. W przypadku urządzeń z Androidem OS i nowszych możesz ręcznie włączyć tworzenie kopii zapasowej w chmurze ze Sklepu Store. Aby to zrobić, kliknij Ustawienia > Google > Kopia zapasowa i kliknij przycisk „Utwórz kopię zapasową”.
    1. Aby sprawdzić, czy udało się utworzyć kopię zapasową w chmurze, możesz:
      1. Po utworzeniu kopii zapasowej wyszukaj wiersze logu z tagiem „CloudSyncBpTkSvc”.
      2. Powinno pojawić się takie wiersze: „......, CloudSyncBpTkSvc: sync result: SUCCESS, ..., przesłany rozmiar: XXX bajtów ...”
    2. Po utworzeniu kopii zapasowej danych z blokady w Store Store trwa 5 minut. W ciągu 5 minut kliknięcie przycisku „Utwórz kopię zapasową teraz” nie spowoduje uruchomienia kolejnej kopii zapasowej w Cloud Store.
  4. Przywróć ustawienia fabryczne na urządzeniu docelowym i przejdź przez proces przywracania w chmurze. Wybierz, aby przywrócić aplikację testową podczas procesu przywracania. Więcej informacji o procesach przywracania w chmurze znajdziesz w tym artykule.
  5. Na urządzeniu docelowym użyj aplikacji testowej do wywołania interfejsu Blockstore API, aby pobrać swoje dane.
  6. Sprawdź, czy pobrane bajty są takie same jak te zapisane na urządzeniu źródłowym.

Wymagania sprzętowe

Pełne szyfrowanie

  • Pełne szyfrowanie jest obsługiwane na urządzeniach z Androidem 9 (API 29) i nowszym.
  • Aby można było włączyć pełne szyfrowanie i prawidłowo zaszyfrować dane użytkownika, urządzenie musi mieć ustawioną blokadę ekranu z kodem PIN, wzorem lub hasłem.

Proces przywracania urządzeń

Przywrócenie urządzenia do urządzenia wymaga urządzenia źródłowego i docelowego. Będą to 2 urządzenia, które będą przenosić dane.

Aby można było tworzyć kopie zapasowe, na urządzeniach źródłowych musi być zainstalowany Android 6 (API 23) lub nowszy.

Możliwość kierowania na urządzenia z Androidem 9 (API 29) lub nowszym.

Więcej informacji na temat procedury przywracania danych między urządzeniami znajdziesz tutaj.

Proces tworzenia i przywracania kopii zapasowych w chmurze

Tworzenie i przywracanie kopii zapasowej w chmurze będzie wymagało urządzenia źródłowego i docelowego.

Aby można było tworzyć kopie zapasowe, na urządzeniach źródłowych musi być zainstalowany Android 6 (API 23) lub nowszy.

Urządzenia docelowe są obsługiwane przez różnych dostawców. Urządzenia Pixel mogą korzystać z tej funkcji na urządzeniach z Androidem 9 (API 29) oraz na wszystkich innych urządzeniach z Androidem 12 (API 31) lub nowszym.