Многие пользователи по-прежнему самостоятельно управляют своими учетными данными при настройке нового устройства Android. Это может быть сложно и часто приводит к неудобству для пользователей. Block Store API – это библиотека, работающая на основе сервисов Google Play. Она позволяет приложениям сохранять учетные данные пользователей без сложностей и рисков для безопасности, связанных с хранением паролей.
API Block Store позволяет приложению хранить данные, которые можно будет использовать для повторной аутентификации пользователей на новом устройстве. Это позволяет пользователю сразу начать работу с приложением на новом устройстве, не вводя учетные данные.
Вот преимущества использования Block Store:
- Решение для хранения зашифрованных учетных данных для разработчиков. Учетные данные по возможности защищаются сквозным шифрованием.
- Сохраняйте токены вместо имен пользователей и паролей.
- Упростите процесс входа в аккаунт.
- Избавить пользователей от необходимости управлять сложными паролями.
- Google проверяет личность пользователя.
Подготовка
Чтобы подготовить приложение, выполните нужные действия в разделах, указанных ниже.
Настройка приложения
В файле build.gradle на уровне проекта добавьте репозиторий Google Maven в разделы 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 и получить данные.
- Убедитесь, что полученные байты совпадают с теми, которые были сохранены до удаления.
Между устройствами
В большинстве случаев для этого потребуется сбросить настройки целевого устройства до заводских. Затем вы можете воспользоваться беспроводным восстановлением Android или восстановлением с помощью кабеля Google (для поддерживаемых устройств).
Восстановление из облака
- Интегрируйте Block Store API в тестовое приложение и отправьте его в Google Play.
- На исходном устройстве используйте тестовое приложение, чтобы вызвать Block Store API и сохранить данные, задав для параметра
shouldBackUpToCloudзначениеtrue. - На устройствах с Android O и более поздних версий можно вручную запустить резервное копирование Block Store в облако. Для этого выберите Настройки > Google > Резервное копирование и нажмите кнопку "Создать резервную копию".
- Чтобы проверить, успешно ли выполнено копирование в облако, можно:
- После завершения резервного копирования найдите строки журнала с тегом "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) или более поздней версии.