Многие пользователи по-прежнему управляют своими учетными данными при настройке нового устройства Android. Это может быть неудобно для пользователей. Block Store API – это библиотека на основе сервисов Google Play, которая позволяет приложениям сохранять учетные данные пользователей без сложностей и рисков, связанных с хранением паролей.
Block Store API позволяет приложению сохранять данные, которые можно будет использовать для повторной аутентификации пользователей на новом устройстве. Это помогает обеспечить более удобный пользовательский интерфейс, поскольку пользователям не нужно видеть экран входа при первом запуске вашего приложения на новом устройстве.
Вот преимущества Block Store:
- Решение для разработчиков, позволяющее хранить зашифрованные учетные данные. По возможности для учетных данных используется сквозное шифрование.
- Сохраняйте токены вместо имен пользователей и паролей.
- Упростите процесс входа.
- Пользователям не нужно будет запоминать сложные пароли.
- Google подтверждает личность пользователя.
Что вам потребуется
Чтобы подготовить приложение, выполните нужные действия в разделах, указанных ниже.
Настройка приложения
В файле build.gradle на уровне проекта добавьте хранилище Maven от Google в разделы buildscript и allprojects:
buildscript {
repositories {
google()
mavenCentral()
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
Добавьте зависимость сервисов Google Play для Block Store API в файл build.gradle модуля – обычно это app/build.gradle:
dependencies {
implementation 'com.google.android.gms:play-services-auth-blockstore:16.4.0'
}
Принцип работы
Block Store позволяет разработчикам сохранять и восстанавливать до 16 массивов байтов. Это позволяет сохранять важную информацию о текущем сеансе пользователя и предлагает гибкость для сохранения этой информации любым удобным для вас способом. Для этих данных можно использовать сквозное шифрование. Инфраструктура Block Store основана на инфраструктуре резервного копирования и восстановления.
В этом руководстве мы рассмотрим, как сохранить токен пользователя в Block Store. Ниже описано, как работает приложение, использующее Block Store.
- Во время аутентификации в приложении или в любой момент после нее вы можете сохранить токен аутентификации пользователя в Block Store, чтобы использовать его позже.
- Токен будет храниться локально. Также можно создать его резервную копию в облаке. По возможности будет использоваться сквозное шифрование.
- Данные переносятся, когда пользователь запускает процесс восстановления на новом устройстве.
- Если пользователь восстановит ваше приложение, оно сможет получить сохраненный токен из Block Store на новом устройстве.
Сохранение токена
Когда пользователь входит в приложение, вы можете сохранить сгенерированный для него токен аутентификации в Block Store. Вы можете сохранить этот токен, используя уникальную пару "ключ-значение" (не более 4 КБ на запись). Чтобы сохранить токен, вызовите setBytes() и setKey() в экземпляре StoreBytesData.Builder. Это позволит сохранить учетные данные пользователя на исходном устройстве. После сохранения токена в Block Store он шифруется и хранится локально на устройстве.
В примере ниже показано, как сохранить токен аутентификации на локальном устройстве.
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) }
Использовать токен по умолчанию
Данные, сохраненные с помощью StoreBytes без ключа, используют ключ по умолчанию
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) }
Получение токена
Когда пользователь запускает процесс восстановления на новом устройстве, сервисы Google Play сначала проверяют его личность, а затем извлекают данные из Block Store. Пользователь уже согласился на восстановление данных приложения в процессе восстановления, поэтому дополнительных согласий не требуется. Когда пользователь открывает приложение, вы можете запросить токен из Block Store, вызвав retrieveBytes(). Полученный токен можно использовать, чтобы пользователь оставался в аккаунте на новом устройстве.
В примере ниже показано, как получить несколько токенов на основе определенных ключей.
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 ListrequestedKeys = 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) }
Получение всех токенов.
Ниже приведен пример того, как получить все токены, сохраненные в 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) }
Ниже приведен пример того, как получить ключ по умолчанию.
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)
Удаление токенов
Удаление токенов из BlockStore может потребоваться по следующим причинам:
- Пользователь выходит из аккаунта.
- Токен отозван или недействителен.
Как и при получении токенов, вы можете указать, какие токены нужно удалить, задав массив ключей, которые требуют удаления.
В примере ниже показано, как удалить определенные ключи.
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 ListrequestedKeys = 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)
Удалить все токены
В следующем примере показано, как удалить все токены, сохраненные в 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 keyBlockstoreClient.DEFAULT_BYTES_DATA_KEYcan be used in theRetrieveBytesRequestinstance 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")
}
Включить копирование в облако
Чтобы включить резервное копирование в облако, добавьте метод setShouldBackupToCloud() в объект StoreBytesData. Если для параметра setShouldBackupToCloud() задано значение true, Block Store будет периодически создавать резервную копию сохраненных байтов в облаке.
В примере ниже показано, как включить резервное копирование в облако, только если оно защищено сквозным шифрованием.
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.")
}
}
Как протестировать
Чтобы протестировать процесс восстановления, используйте следующие методы.
Удаление и повторная установка на одном устройстве
Если пользователь включит службы резервного копирования (это можно проверить в разделе Настройки > Google > Резервное копирование), данные Block Store будут сохраняться при удалении и повторной установке приложения.
Чтобы выполнить проверку, сделайте следующее:
- Интегрируйте Block Store API в тестовое приложение.
- Используйте тестовое приложение, чтобы вызвать Block Store API для сохранения данных.
- Удалите тестовое приложение и установите его заново на том же устройстве.
- Используйте тестовое приложение, чтобы вызвать Block Store API и получить данные.
- Убедитесь, что полученные байты совпадают с теми, которые были сохранены до удаления.
С устройства на устройство
В большинстве случаев для этого потребуется сбросить настройки целевого устройства. Затем вы можете восстановить данные по беспроводной сети или с помощью кабеля (на поддерживаемых устройствах).
Восстановление из облака
- Интегрируйте Block Store API в тестовое приложение. Его нужно опубликовать в Google Play.
- На исходном устройстве используйте тестовое приложение, чтобы вызвать Block Store API для сохранения данных, установив для
shouldBackUpToCloudзначениеtrue. - На устройствах с Android O и более поздних версий можно вручную запустить резервное копирование в облако Block Store. Для этого перейдите в Настройки > Google > Резервное копирование и нажмите кнопку "Начать копирование".
- Чтобы убедиться, что резервное копирование в облако с помощью Block Store прошло успешно, вы можете:
- После завершения резервного копирования найдите строки журнала с тегом CloudSyncBpTkSvc.
- Вы должны увидеть строки, подобные этой: "......, CloudSyncBpTkSvc: sync result: SUCCESS, ..., uploaded size: XXX bytes ..."
- После резервного копирования в облако Block Store наступает пятиминутный период ожидания. В течение этого времени нажатие кнопки "Создать резервную копию" не приведет к созданию ещё одной копии в облаке.
- Чтобы убедиться, что резервное копирование в облако с помощью Block Store прошло успешно, вы можете:
- Сбросьте настройки целевого устройства и восстановите данные из облака. Выберите его, чтобы восстановить тестовое приложение. Подробнее о поддерживаемых процессах восстановления из облака…
- На целевом устройстве используйте тестовое приложение, чтобы вызвать Block Store API и получить данные.
- Убедитесь, что извлеченные байты совпадают с теми, которые хранились на исходном устройстве.
Требования к устройствам
Сквозное шифрование
- Сквозное шифрование поддерживается на устройствах с Android 9 (API 29) и более поздними версиями.
- Чтобы сквозное шифрование работало корректно, на устройстве должна быть настроена блокировка экрана с помощью PIN-кода, графического ключа или пароля.
Процесс восстановления данных с одного устройства на другое
Для переноса данных с одного устройства на другое вам понадобятся оба устройства. Это будут два устройства, между которыми передаются данные.
Исходные устройства должны работать под управлением Android 6 (API 23) или более поздней версии.
Целевые устройства с Android 9 (API 29) и более поздними версиями, на которых можно восстанавливать данные.
Подробнее о переносе данных с одного устройства на другое…
Процесс резервного копирования в облако и восстановления данных
Для резервного копирования и восстановления данных в облаке потребуется исходное и целевое устройство.
Исходные устройства должны работать под управлением Android 6 (API 23) или более поздней версии.
Поддержка целевых устройств зависит от их производителей. На устройствах Pixel эта функция доступна начиная с Android 9 (API 29), а на всех остальных – с Android 12 (API 31).