Wielu użytkowników nadal samodzielnie zarządza swoimi danymi logowania podczas konfigurowania nowego urządzenia z Androidem. Ten ręczny proces może być trudny i często prowadzi do pogorszenia wrażeń użytkowników. Interfejs Block Store API, biblioteka obsługiwana przez usługi Google Play, rozwiązuje ten problem, umożliwiając aplikacjom zapisywanie danych logowania użytkownika bez złożoności i zagrożeń związanych z zapisywaniem haseł.
Interfejs Block Store API umożliwia aplikacji przechowywanie danych, które może później pobrać, aby ponownie uwierzytelnić użytkowników na nowym urządzeniu. Dzięki temu użytkownik nie musi widzieć ekranu logowania, gdy po raz pierwszy uruchamia aplikację na nowym urządzeniu.
Zalety korzystania z Block Store:
- Rozwiązanie do przechowywania zaszyfrowanych danych logowania dla deweloperów. Dane logowania są w miarę możliwości w pełni szyfrowane.
- Zapisuj tokeny zamiast nazw użytkowników i haseł.
- Uprość proces logowania.
- Użytkownicy nie muszą zarządzać złożonymi hasłami.
- Google zweryfikuje tożsamość użytkownika.
Zanim zaczniesz
Aby przygotować aplikację, wykonaj czynności opisane w poniższych sekcjach.
Konfiguracja aplikacji
W sekcjach buildscript i allprojects pliku build.gradle na poziomie projektu dodaj 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.4.0'
}
Jak to działa
Block Store umożliwia deweloperom zapisywanie i przywracanie maksymalnie 16 tablic bajtów. Umożliwia to zapisywanie ważnych informacji dotyczących bieżącej sesji użytkownika i zapewnia elastyczność zapisywania tych informacji w dowolny sposób. Te dane mogą być w pełni szyfrowane, a infrastruktura obsługująca Block Store jest oparta na infrastrukturze tworzenia i przywracania kopii zapasowych.
W tym przewodniku opisujemy przypadek użycia polegający na zapisywaniu tokena użytkownika w Block Store. Poniżej opisujemy, jak działa aplikacja korzystająca z Block Store:
- Podczas procesu uwierzytelniania aplikacji lub w dowolnym momencie po jego zakończeniu możesz zapisać token uwierzytelniania użytkownika w Block Store, aby móc go później pobrać.
- Token będzie przechowywany lokalnie i może być też zapisywany w kopii zapasowej w chmurze, w miarę możliwości z pełnym szyfrowaniem.
- Dane są przenoszone, gdy użytkownik inicjuje proces przywracania na nowym urządzeniu.
- Jeśli użytkownik przywróci aplikację podczas procesu przywracania, aplikacja może pobrać zapisany token z Block Store na nowym urządzeniu.
Zapisywanie tokena
Gdy użytkownik loguje się w aplikacji, możesz zapisać w Block Store token uwierzytelniania wygenerowany dla tego użytkownika. Możesz przechowywać ten token, używając unikalnej pary klucz-wartość, która może mieć maksymalnie 4 KB na wpis. Aby zapisać token, wywołaj metody setBytes() i setKey() w instancji StoreBytesData.Builder. Pozwoli to zapisać dane logowania użytkownika na urządzeniu źródłowym. Po zapisaniu tokena w Block Store 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 domyślnego tokena
Dane zapisane za pomocą metody StoreBytes bez klucza używają klucza domyślnego 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)
}
Pobieranie tokena
Później, gdy użytkownik przejdzie przez proces przywracania na nowym urządzeniu, Usługi Google Play najpierw zweryfikują użytkownika, a następnie 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 metodę retrieveBytes(). Pobrany token może być następnie używany do utrzymywania zalogowania 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<String> 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)
}
Pobieram wszystkie tokeny.
Poniższy przykład pokazuje, 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<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ższy przykład pokazuje, 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
Usunięcie tokenów z BlockStore może być konieczne z tych powodów:
- Użytkownik przechodzi przez wzorzec przeglądania wylogowywania.
- Token został unieważniony 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 wymagają usunięcia.
Poniższy 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<String> 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 Block Store:
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
// Delete all data.
val deleteAllRequest = DeleteBytesRequest.Builder()
.setDeleteAll(true)
.build()
client.deleteBytes(deleteAllRequest)
.addOnSuccessListener { result -> Log.d(TAG, "Any data found and deleted? $result") }
Pełne szyfrowanie
Aby pełne szyfrowanie było dostępne, na urządzeniu musi być zainstalowany Android 9 lub nowszy, a użytkownik musi ustawić blokadę ekranu (kod PIN, wzór lub hasło). Możesz sprawdzić, czy szyfrowanie będzie dostępne na urządzeniu, wywołując metodę isEndToEndEncryptionAvailable().
Poniższy 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. Gdy zasada setShouldBackupToCloud() ma wartość „true”, Block Store będzie okresowo tworzyć w chmurze kopie zapasowe przechowywanych bajtów.
Poniższy przykład pokazuje, jak włączyć tworzenie kopii zapasowej w chmurze tylko wtedy, gdy jest ona w pełni zaszyfrowana:
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
Aby przetestować procesy przywracania, podczas programowania użyj tych metod.
Odinstalowanie i ponowna instalacja na tym samym urządzeniu
Jeśli użytkownik włączy usługi tworzenia kopii zapasowej (można to sprawdzić w sekcji Ustawienia > Google > Kopia zapasowa), dane Block Store będą zachowywane po odinstalowaniu i ponownym zainstalowaniu aplikacji.
Aby przeprowadzić test, wykonaj te czynności:
- Zintegruj interfejs Block Store API z aplikacją testową.
- Użyj aplikacji testowej, aby wywołać interfejs Block Store API i zapisać dane.
- Odinstaluj aplikację testową, a potem ponownie zainstaluj aplikację na tym samym urządzeniu.
- Użyj aplikacji testowej, aby wywołać interfejs Block Store API i pobrać dane.
- Sprawdź, czy pobrane bajty są takie same jak te, które zostały zapisane przed odinstalowaniem.
Z urządzenia na urządzenie
W większości przypadków będzie to wymagało przywrócenia urządzenia docelowego do ustawień fabrycznych. Następnie możesz przejść do procesu przywracania bezprzewodowego na Androidzie lub przywracania za pomocą kabla Google (w przypadku obsługiwanych urządzeń).
Przywracanie z chmury
- Zintegruj interfejs Block Store API z aplikacją testową. Aplikację testową należy przesłać do Sklepu Play.
- Na urządzeniu źródłowym użyj aplikacji testowej, aby wywołać interfejs Block Store API i zapisać dane, ustawiając
shouldBackUpToCloudnatrue. - Na urządzeniach z Androidem 8.0 (poziom interfejsu API 26) lub nowszym możesz ręcznie uruchomić tworzenie kopii zapasowej Block Store w chmurze: otwórz Ustawienia > Google > Kopia zapasowa i kliknij przycisk „Utwórz kopię zapasową teraz”.
- Aby sprawdzić, czy tworzenie kopii zapasowej w chmurze w Block Store się powiodło, możesz:
- Po zakończeniu tworzenia kopii zapasowej wyszukaj wiersze dziennika z tagiem
[CloudSyncBackupTaskService]. - Powinny wyświetlić się takie wiersze:
..., [CloudSyncBackupTaskService] sync result: SUCCESS,..., uploaded size: [size] bytes ...
- Po zakończeniu tworzenia kopii zapasowej wyszukaj wiersze dziennika z tagiem
- Po utworzeniu kopii zapasowej w chmurze w Block Store następuje 5-minutowy okres „schładzania”. W ciągu tych 5 minut kliknięcie przycisku „Utwórz kopię zapasową teraz” nie spowoduje utworzenia kolejnej kopii zapasowej Block Store w chmurze.
- Aby sprawdzić, czy tworzenie kopii zapasowej w chmurze w Block Store się powiodło, możesz:
- Przywróć urządzenie docelowe do ustawień fabrycznych i przeprowadź proces przywracania z chmury. Wybierz, aby przywrócić aplikację testową podczas procesu przywracania. Więcej informacji o procesach przywracania z chmury znajdziesz w sekcji Obsługiwane procesy przywracania z chmury.
- Na urządzeniu docelowym użyj aplikacji testowej, aby wywołać interfejs Block Store API i pobrać dane.
- Sprawdź, czy pobrane bajty są takie same jak te zapisane na urządzeniu źródłowym.
Wymagania dotyczące urządzeń
Zapoznaj się z poniższymi wymaganiami dotyczącymi urządzeń, które mogą korzystać z operacji Block Store.
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 zaszyfrować dane użytkownika, urządzenie musi mieć ustawioną blokadę ekranu za pomocą kodu PIN, wzoru lub hasła.
Proces przywracania danych z urządzenia na urządzenie
Przywracanie z urządzenia na urządzenie wymaga urządzenia źródłowego i docelowego. Będą to 2 urządzenia, między którymi są przenoszone dane.
Aby można było utworzyć kopię zapasową, na urządzeniach źródłowych musi być zainstalowany Android 6 (API 23) lub nowszy.
Urządzenia docelowe z Androidem 9 (API 29) lub nowszym, aby umożliwić przywracanie.
Więcej informacji o procesie przywracania danych z urządzenia na urządzenie znajdziesz w sekcji Obsługiwane procesy przywracania danych z urządzenia na urządzenie.
Proces tworzenia i przywracania kopii zapasowej w chmurze
Do tworzenia i przywracania kopii zapasowej w chmurze potrzebne jest urządzenie źródłowe i urządzenie docelowe.
Urządzenia źródłowe muszą mieć Androida 6 (API 23) lub nowszego, aby można było utworzyć kopię zapasową.
Urządzenia docelowe są obsługiwane na podstawie ich dostawców. Urządzenia Pixel mogą korzystać z tej funkcji od Androida 9 (API 29), a wszystkie inne urządzenia muszą mieć Androida 12 (API 31) lub nowszego.