Sklep blokowy

Wielu użytkowników w dalszym ciągu zarządza własnymi danymi logowania podczas konfigurowania nowego urządzenia z Androidem. Ręczny proces może być trudny i często źle wpływa na wrażenia użytkowników. Block Store API to biblioteka korzystająca z Usług Google Play, aby rozwiązać ten problem, umożliwiając aplikacjom zapisywanie danych logowania użytkowników bez złożoności i bezpieczeństwa związanych z zapisywaniem haseł użytkowników.

Interfejs Block Store API umożliwia aplikacji przechowywanie danych, które mogą później pobierać w celu ponownego uwierzytelnienia użytkowników na nowym urządzeniu. Zwiększa to wygodę użytkowników, ponieważ nie muszą widzieć ekranu logowania przy pierwszym uruchomieniu aplikacji na nowym urządzeniu.

Zalety korzystania ze sklepu Block Store to między innymi:

  • Rozwiązanie do przechowywania zaszyfrowanych danych logowania dla programistów. Gdy to możliwe, dane uwierzytelniające są w pełni szyfrowane.
  • Zapisuj tokeny zamiast nazw użytkowników i haseł.
  • Wyeliminuj problemy podczas logowania.
  • uniknąć zarządzania złożonymi hasłami przez użytkowników;
  • Google weryfikuje tożsamość użytkownika.

Zanim zaczniesz

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

Konfiguracja aplikacji

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

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.2.0'
}

Jak to działa

Magazyn bloków umożliwia programistom zapisywanie i przywracanie tablic o wielkości do 16 bajtów. Dzięki temu możesz zapisywać ważne informacje o bieżącej sesji użytkownika. Możesz je też dowolnie zapisywać. Dane te mogą być w pełni szyfrowane, a infrastruktura obsługująca Block Store jest oparta na infrastrukturze do tworzenia i przywracania kopii zapasowych.

W tym przewodniku omawiamy przypadki użycia tokena użytkownika w blokadzie Store. Oto jak będzie działać aplikacja korzystająca ze sklepu Block Store:

  1. Podczas procesu uwierzytelniania aplikacji lub w dowolnym momencie po jej zakończeniu możesz przechowywać token uwierzytelniania użytkownika w Block Store, aby móc go później pobrać.
  2. Token będzie przechowywany lokalnie, a w miarę możliwości można go również utworzyć w chmurze z pełnym szyfrowaniem.
  3. Dane są przenoszone, gdy użytkownik rozpocznie proces przywracania na nowym urządzeniu.
  4. Jeśli użytkownik przywróci aplikację w trakcie procesu przywracania, będzie ona mogła odzyskać zapisany token z Block Store na nowym urządzeniu.

Zapisuję token

Gdy użytkownik loguje się w Twojej aplikacji, możesz zapisać w aplikacji Block Store token uwierzytelniania wygenerowany dla tego użytkownika. Możesz przechowywać ten token z użyciem unikalnej wartości pary kluczy o maksymalnej długości 4 KB na wpis. Aby zapisać token, wywołaj setBytes() i setKey() w instancji StoreBytesData.Builder w celu zapisania danych logowania użytkownika na urządzeniu źródłowym. Po zapisaniu tokena w magazynie blokowania jest on szyfrowany i przechowywany 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 zapisywane z użyciem 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 zweryfikują go, a potem pobierają dane sklepu Block Store. Użytkownik zgodził się już na przywrócenie danych aplikacji w ramach procesu przywracania, więc nie jest wymagana dodatkowa zgoda. Gdy użytkownik otworzy Twoją aplikację, możesz poprosić o token z Block Store, wywołując metodę retrieveBytes(). Pobrany token może być użyty, aby użytkownik był zalogowany 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, jak pobrać wszystkie tokeny zapisane 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)

Usuwanie tokenów

Usunięcie tokenów z BlockStore może być wymagane z tych powodów:

  • Użytkownik przechodzi przez proces wylogowania.
  • Token został unieważniony lub jest nieprawidłowy.

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

Poniżej pokazujemy na przykład, jak usunąć niektóre 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

Przykład poniżej 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, na urządzeniu musi być zainstalowany Android 9 lub nowszy, a użytkownik musi ustawić blokadę ekranu (kod PIN, wzór lub hasło). Aby sprawdzić, czy szyfrowanie będzie dostępne na urządzeniu, możesz zadzwonić pod numer isEndToEndEncryptionAvailable().

Ten przykład pokazuje, jak sprawdzić, czy szyfrowanie będzie 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ć tworzenie kopii zapasowej w chmurze, dodaj metodę setShouldBackupToCloud() do obiektu StoreBytesData. Jeśli setShouldBackupToCloud() ma wartość prawda, Magazyn bloków będzie okresowo tworzyć kopię zapasową w chmurze w postaci zapisanych bajtów.

Z przykładu poniżej dowiesz się, 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.")
          }
        }

Jak to sprawdzić

Aby przetestować procesy przywracania, podczas programowania korzystaj z tych metod.

Odinstalowanie/ponowne zainstalowanie tego samego urządzenia

Jeśli użytkownik włączy usługi tworzenia kopii zapasowych (możesz to sprawdzić w sekcji Ustawienia > Google > Kopia zapasowa), dane z blokady sklepu będą przechowywane przez cały czas odinstalowania lub ponownego instalowania aplikacji.

Aby przeprowadzić test, wykonaj te czynności:

  1. Zintegruj interfejs BlockStore API z aplikacją testową.
  2. Użyj aplikacji testowej, aby wywołać interfejs BlockStore API, aby zapisać dane.
  3. Odinstaluj aplikację testową, a następnie ponownie zainstaluj ją na tym samym urządzeniu.
  4. Użyj aplikacji testowej, aby wywołać interfejs BlockStore API, aby pobrać dane.
  5. Sprawdź, czy pobrane bajty są takie same jak przechowywane przed odinstalowaniem.

między urządzeniami

W większości przypadków będzie to wymagać przywrócenia urządzenia docelowego do ustawień fabrycznych. Następnie możesz przejść do procesu przywracania bezprzewodowego na Androidzie lub przywracania danych z użyciem kabla Google (w przypadku obsługiwanych urządzeń).

Przywracanie do Cloud

  1. Zintegruj interfejs Blockstore API z aplikacją testową. Aplikację testową należy przesłać do Sklepu Play.
  2. Na urządzeniu źródłowym użyj aplikacji testowej, aby wywołać interfejs Blockstore API w celu przechowywania danych. Powinien on mieć wartość true (prawda).
  3. W przypadku urządzeń w wersji O lub nowszej możesz ręcznie aktywować kopię zapasową w chmurze Block Store: otwórz Ustawienia > Google > Kopia zapasowa i kliknij przycisk „Utwórz kopię zapasową teraz”.
    1. Aby sprawdzić, czy kopia zapasowa Block Store została utworzona w chmurze, możesz:
      1. Gdy kopia zapasowa zostanie utworzona, wyszukaj wiersze logu z tagiem „CloudSyncBpTkSvc”.
      2. Powinny pojawić się następujące wiersze: „......, CloudSyncBpTkSvc: wynik synchronizacji: SUCCESS, ..., rozmiar przesłanego pliku: XXX bajtów ...”
    2. Po utworzeniu kopii zapasowej w chmurze Block Store rozpoczyna się 5-minutowy okres „schładzania”. W ciągu tych 5 minut kliknięcie przycisku „Utwórz kopię zapasową teraz” nie spowoduje uruchomienia kolejnej kopii zapasowej w chmurze Block Store.
  4. Przywróć urządzenie docelowe do ustawień fabrycznych i wykonaj proces przywracania w chmurze. Wybierz tę opcję, aby przywrócić aplikację testową podczas procesu przywracania. Więcej informacji o procesach przywracania do chmury znajdziesz w artykule Obsługiwane procesy przywracania do chmury.
  5. Na urządzeniu docelowym użyj aplikacji testowej, aby wywołać interfejs Blockstore API, aby pobrać dane.
  6. Sprawdź, czy pobrane bajty są takie same jak te zapisane na urządzeniu źródłowym.

Wymagania dotyczące urządzenia

Pełne szyfrowanie

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

Proces przywracania danych z urządzenia na urządzenie

Aby przywrócić dane z urządzenia na urządzenie, musisz mieć urządzenie źródłowe i docelowe. Będą to 2 urządzenia, które będą przesyłać dane.

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

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

Więcej informacji na temat procesu przywracania urządzenia na urządzenie znajdziesz tutaj.

Proces tworzenia i przywracania kopii zapasowej 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, urządzenia źródłowe muszą mieć zainstalowany Android 6 (API 23) lub nowszy.

Urządzenia docelowe są obsługiwane w zależności od dostawców. Urządzenia Pixel mogą korzystać z tej funkcji w Androidzie 9 (API 29), a na wszystkich pozostałych urządzeniach musi być zainstalowany Android 12 (API 31) lub nowszy.