Sklep blokowy

Wielu użytkowników nadal zarządza własnymi danymi logowania podczas konfigurowania nowego urządzenia z Androidem. Ten ręczny proces może być trudny i często powoduje u użytkowników dyskomfort. Interfejs Block Store API, który jest biblioteką obsługiwaną przez usługi Google Play, ma rozwiązać ten problem, zapewniając aplikacjom możliwość zapisywania danych logowania użytkownika bez konieczności skomplikowania lub ryzyka związanego z bezpieczeństwem, które występuje przy zapisywaniu haseł użytkowników.

Interfejs Block Store API umożliwia aplikacji przechowywanie danych, które można później pobrać, aby ponownie uwierzytelnić użytkowników na nowym urządzeniu. Dzięki temu użytkownik nie musi widzieć ekranu logowania, gdy uruchamia aplikację po raz pierwszy na nowym urządzeniu.

Zalety korzystania z Block Store:

  • Zaszyfrowane rozwiązanie do przechowywania danych logowania dla deweloperów. Dane logowania są w miarę możliwości w pełni szyfrowane.
  • Zapisywanie tokenów zamiast nazw użytkowników i haseł.
  • Usuń problemy z logowaniem.
  • Uwolnij użytkowników od konieczności zarządzania skomplikowanymi hasłami.
  • Google weryfikuje tożsamość użytkownika.

Zanim zaczniesz

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

Konfigurowanie aplikacji

W pliku build.gradle na poziomie projektu dodaj repozytorium Maven firmy Google w sekcjach buildscriptallprojects:

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

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

Dodaj zależność Usług Google Play do interfejsu Block Store API do pliku build.gradle modułu. Jest to zwykle app/build.gradle:

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

Jak to działa

Block Store umożliwia deweloperom zapisywanie i przywracanie tablic o długości do 16 bajtów. Dzięki temu możesz zapisywać ważne informacje dotyczące bieżącej sesji użytkownika i robić to w dowolny sposób. Dane te mogą być w pełni szyfrowane, a infrastruktura obsługująca Block Store jest budowana na podstawie infrastruktury kopii zapasowej i przywracania.

W tym przewodniku omówimy zapisywanie tokena użytkownika w Block Store. Poniższe kroki opisują działanie aplikacji korzystającej z Block Store:

  1. Podczas procesu uwierzytelniania w aplikacji lub w dowolnym momencie później możesz zapisać token uwierzytelniający użytkownika w Block Store, aby można go było później odzyskać.
  2. Token jest przechowywany lokalnie, ale można też utworzyć jego kopię zapasową w chmurze, szyfrując ją w całości, o ile to możliwe.
  3. Dane są przenoszone, gdy użytkownik rozpocznie proces przywracania na nowym urządzeniu.
  4. Jeśli użytkownik przywróci Twoją aplikację podczas procesu przywracania, aplikacja może pobrać zapisany token z Block Store na nowym urządzeniu.

Zapisywanie tokena

Gdy użytkownik zaloguje się w aplikacji, możesz zapisać wygenerowany dla niego token uwierzytelniania w Block Store. Możesz przechowywać ten token za pomocą unikalnej pary klucz-wartość, która ma maksymalnie 4 KB na wpis. Aby zapisać token, wywołaj metody setBytes() i setKey() w instancji StoreBytesData.Builder, aby zapisać dane logowania użytkownika na urządzeniu źródłowym. Po zapisaniu tokena za pomocą Block Store jest on szyfrowany i zapisywany lokalnie na urządzeniu.

Ten 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żywanie domyślnego tokena

Dane zapisane za pomocą funkcji StoreBytes bez klucza używają klucza domyślnegoBlockstoreClient.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)
    }

Pobieranie tokena

Gdy użytkownik będzie przechodzić proces przywracania na nowym urządzeniu, usługi Google Play najpierw zweryfikują użytkownika, a potem pobiorą dane z Block Store. Użytkownik wyraził już zgodę na przywrócenie danych aplikacji w ramach procesu przywracania, więc nie są wymagane żadne dodatkowe zgody. Gdy użytkownik otworzy aplikację, możesz poprosić o token z Block Store, wywołując funkcję retrieveBytes(). Odzyskany token może być następnie używany do utrzymywania zalogowanego użytkownika na nowym urządzeniu.

Poniższy przykład pokazuje, jak pobrać wiele tokenów na podstawie określonych 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<String, BlockstoreData> blockstoreDataMap = result.getBlockstoreDataMap();
          for (Map.Entry<String, BlockstoreData> 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)
  }

Pobieranie wszystkich tokenów.

Poniżej znajdziesz przykładowy sposób pobierania 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<String, BlockstoreData> blockstoreDataMap = result.getBlockstoreDataMap();
          for (Map.Entry<String, BlockstoreData> 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, jak pobrać klucz domyślny.

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)

Usuwanie tokenów

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

  • Użytkownik przechodzi proces wylogowywania.
  • Token został cofnięty lub jest nieprawidłowy.

Podobnie jak w przypadku pobierania tokenów, możesz określić, które tokeny mają zostać usunięte, ustawiając tablicę kluczy, które mają zostać usunięte.

Ten przykład pokazuje, jak usunąć określone klucze:

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 pokazuje, jak usunąć wszystkie tokeny zapisane 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()
retrieve bytes, the key BlockstoreClient.DEFAULT_BYTES_DATA_KEY can be used
in the RetrieveBytesRequest instance in order to get your saved data

The following example shows how to retrieve the default key.

Java

End-to-end encryption

In order for end-to-end encryption to be made available, the device must be running Android 9 or higher, and the user must have set a screen lock (PIN, pattern, or password) for their device. You can verify if encryption will be available on the device by calling isEndToEndEncryptionAvailable().

The following sample shows how to verify if encryption will be available during cloud backup:

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

Włączanie kopii zapasowej w chmurze

Aby włączyć tworzenie kopii zapasowej w chmurze, dodaj metodę setShouldBackupToCloud() do obiektu StoreBytesData. Block Store będzie okresowo tworzyć kopie zapasowe przechowywanych bajtów w chmurze, gdy parametr setShouldBackupToCloud() ma wartość Prawda.

Ten przykład pokazuje, jak włączyć tworzenie kopii zapasowej w chmurze tylko wtedy, gdy jest ono szyfrowane end-to-end:

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.")
          }
        }

Jak przeprowadzić test

Podczas tworzenia aplikacji używaj tych metod do testowania procesów przywracania.

Odinstalowanie/ponowne zainstalowanie na tym samym urządzeniu

Jeśli użytkownik włączy usługi tworzenia kopii zapasowych (można to sprawdzić w sekcji Ustawienia > Google > Kopia zapasowa), dane Block Store są zachowywane po odinstalowaniu i ponownie zainstalowaniu aplikacji.

Aby przetestować tę funkcję:

  1. Zintegruj interfejs Block Store API z aplikacją testową.
  2. Użyj aplikacji testowej, aby wywołać interfejs Block Store API i przechowywać dane.
  3. Odinstaluj aplikację testową, a następnie zainstaluj ją ponownie na tym samym urządzeniu.
  4. Użyj aplikacji testowej, aby wywołać interfejs Block Store API i pobrać dane.
  5. Sprawdź, czy pobrane bajty są takie same jak te, które zostały zapisane przed odinstalowaniem.

Urządzenie na urządzenie

W większości przypadków wymaga to przywrócenia urządzenia do ustawień fabrycznych. Następnie możesz przejść przez proces przywracania bezprzewodowego na urządzeniu z Androidem lub proces przywracania za pomocą kabla Google (w przypadku obsługiwanych urządzeń).

Przywracanie z poziomu chmury

  1. Zintegruj interfejs Block Store API z aplikacją testową. Aplikację testową należy przesłać do Sklepu Play.
  2. Na urządzeniu źródłowym użyj aplikacji testowej do wywołania interfejsu Block Store API, aby zapisać dane. Ustaw parametr shouldBackUpToCloud na true.
  3. Na urządzeniach z systemem Android O i nowszymi można ręcznie utworzyć kopię zapasową w chmurze Block Store: otwórz Ustawienia > Google > Kopia zapasowa i kliknij przycisk „Utwórz kopię zapasową teraz”.
    1. Aby sprawdzić, czy tworzenie kopii zapasowej w Block Store w chmurze zakończyło się sukcesem, możesz:
      1. Po zakończeniu tworzenia kopii zapasowej wyszukaj wiersze dziennika z tagiem „CloudSyncBpTkSvc”.
      2. Powinny się wyświetlić takie wiersze: „......, CloudSyncBpTkSvc: sync result: SUCCESS, ..., uploaded size: XXX bytes ...”
    2. Po utworzeniu kopii zapasowej w chmurze w Block Store następuje 5-minutowy okres „chłodzenia”. W ciągu tych 5 minut kliknięcie przycisku „Utwórz kopię zapasową teraz” nie spowoduje utworzenia kolejnej kopii zapasowej w Block Store.
  4. Przywróć urządzenie do ustawień fabrycznych i wykonaj proces przywracania z chmury. Wybierz tę opcję, aby przywrócić testową aplikację podczas procesu przywracania. Więcej informacji o przesyłaniu danych do chmury znajdziesz w artykule Obsługiwane procesy przywracania danych z chmury.
  5. Na urządzeniu docelowym użyj testowej aplikacji do wywołania interfejsu Block Store API w celu pobrania danych.
  6. Sprawdź, czy pobrane bajty są takie same jak te zapisane na urządzeniu źródłowym.

Wymagania dotyczące urządzeń

Pełne szyfrowanie

  • Pełne szyfrowanie jest obsługiwane na urządzeniach z Androidem 9 (interfejs API 29) lub nowszym.
  • Aby włączyć szyfrowanie end-to-end i prawidłowo szyfrować dane użytkownika, na urządzeniu musi być ustawiona blokada ekranu za pomocą kodu PIN, wzoru lub hasła.

Proces przywracania danych z urządzenia na urządzenie

Przywracanie danych z urządzenia na urządzenie wymaga posiadania urządzenia źródłowego i docelowego. To będą 2 urządzenia, które będą przesyłać dane.

Aby można było utworzyć kopię zapasową, na urządzeniach źródłowych musi być zainstalowany Android w wersji 6 (poziom interfejsu API 23) lub nowszej.

Kieruj na urządzenia z Androidem 9 (poziom interfejsu API 29) lub nowszym, aby mieć możliwość przywracania.

Więcej informacji o procesie przywracania danych z jednego urządzenia na drugie znajdziesz tutaj.

Tworzenie i przywracanie kopii zapasowej w chmurze

Aby wykonać kopię zapasową w chmurze i przywrócić dane, potrzebne są urządzenie źródłowe i docelowe.

Aby można było utworzyć kopię zapasową, na urządzeniach źródłowych musi być zainstalowany Android w wersji 6 (poziom interfejsu API 23) lub nowszej.

Urządzenia docelowe są obsługiwane na podstawie ich producentów. Urządzenia Pixel mogą korzystać z tej funkcji od Androida 9 (poziom API 29), a wszystkie inne urządzenia muszą mieć zainstalowanego Androida 12 (poziom API 31) lub nowszego.