فروشگاه بلوک

بسیاری از کاربران هنوز هنگام راه‌اندازی یک دستگاه جدید مبتنی بر اندروید، اطلاعات کاربری خود را مدیریت می‌کنند. این فرآیند دستی می‌تواند چالش‌برانگیز باشد و اغلب منجر به تجربه کاربری ضعیفی می‌شود. Block Store API، کتابخانه‌ای که توسط سرویس‌های Google Play پشتیبانی می‌شود، به دنبال حل این مشکل است و راهی را برای برنامه‌ها فراهم می‌کند تا اطلاعات کاربری کاربران را بدون پیچیدگی یا خطر امنیتی مرتبط با ذخیره رمزهای عبور کاربر، ذخیره کنند.

API فروشگاه بلوک به برنامه شما اجازه می‌دهد داده‌هایی را ذخیره کند که بعداً می‌تواند برای تأیید مجدد کاربران در دستگاه جدید بازیابی کند. این به ارائه یک تجربه یکپارچه‌تر برای کاربر کمک می‌کند، زیرا هنگام اجرای برنامه شما برای اولین بار در دستگاه جدید، نیازی به دیدن صفحه ورود به سیستم ندارند.

مزایای استفاده از فروشگاه بلوکی شامل موارد زیر است:

  • راهکار ذخیره‌سازی رمزگذاری‌شده‌ی اعتبارنامه‌ها برای توسعه‌دهندگان. اعتبارنامه‌ها در صورت امکان رمزگذاری سرتاسری می‌شوند.
  • به جای نام کاربری و رمز عبور، توکن‌ها را ذخیره کنید.
  • اصطکاک را از جریان‌های ورود به سیستم حذف کنید.
  • کاربران را از بار مدیریت رمزهای عبور پیچیده نجات دهید.
  • گوگل هویت کاربر را تأیید می‌کند.

قبل از اینکه شروع کنی

برای آماده‌سازی برنامه خود، مراحل بخش‌های زیر را تکمیل کنید.

برنامه خود را پیکربندی کنید

در فایل build.gradle در سطح پروژه، مخزن Maven گوگل را هم در بخش‌های buildscript و هم allprojects وارد کنید:

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

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

وابستگی سرویس‌های گوگل پلی برای API بلاک استور را به فایل ساخت Gradle ماژول خود که معمولاً app/build.gradle است، اضافه کنید:

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

چگونه کار می‌کند؟

Block Store به توسعه‌دهندگان اجازه می‌دهد تا آرایه‌های تا ۱۶ بایت را ذخیره و بازیابی کنند. این به شما امکان می‌دهد اطلاعات مهم مربوط به جلسه فعلی کاربر را ذخیره کنید و انعطاف‌پذیری لازم برای ذخیره این اطلاعات را به هر شکلی که دوست دارید، ارائه می‌دهد. این داده‌ها می‌توانند به صورت سرتاسری رمزگذاری شوند و زیرساختی که از Block Store پشتیبانی می‌کند، بر روی زیرساخت پشتیبان‌گیری و بازیابی ساخته شده است.

این راهنما موارد استفاده از ذخیره توکن کاربر در Block Store را پوشش می‌دهد. مراحل زیر نحوه عملکرد یک برنامه با استفاده از Block Store را شرح می‌دهد:

  1. در طول فرآیند احراز هویت برنامه یا هر زمان پس از آن، می‌توانید توکن احراز هویت کاربر را برای بازیابی بعدی در Block Store ذخیره کنید.
  2. این توکن به صورت محلی ذخیره می‌شود و همچنین می‌تواند در فضای ابری پشتیبان‌گیری شود و در صورت امکان به صورت سرتاسری رمزگذاری شود.
  3. داده‌ها زمانی منتقل می‌شوند که کاربر جریان بازیابی را در دستگاه جدید آغاز کند.
  4. اگر کاربر در طول فرآیند بازیابی، برنامه شما را بازیابی کند، برنامه شما می‌تواند توکن ذخیره شده را از Block Store در دستگاه جدید بازیابی کند.

ذخیره توکن

وقتی کاربری وارد برنامه شما می‌شود، می‌توانید توکن احراز هویتی که برای آن کاربر ایجاد می‌کنید را در Block Store ذخیره کنید. می‌توانید این توکن را با استفاده از یک جفت کلید منحصر به فرد که حداکثر ۴ کیلوبایت برای هر ورودی دارد، ذخیره کنید. برای ذخیره توکن، setBytes() و setKey() روی نمونه‌ای از StoreBytesData.Builder فراخوانی کنید تا اعتبارنامه‌های کاربر در دستگاه مبدا ذخیره شود. پس از ذخیره توکن با Block Store، توکن رمزگذاری شده و به صورت محلی در دستگاه ذخیره می‌شود.

نمونه زیر نحوه ذخیره توکن احراز هویت در دستگاه محلی را نشان می‌دهد:

جاوا

  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));

کاتلین

  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 استفاده می‌کنند.

جاوا

  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));

کاتلین

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

بازیابی توکن

بعداً، وقتی کاربری در دستگاه جدیدی وارد فرآیند بازیابی می‌شود، سرویس‌های گوگل پلی ابتدا کاربر را تأیید می‌کنند، سپس داده‌های Block Store شما را بازیابی می‌کنند. کاربر قبلاً موافقت کرده است که داده‌های برنامه شما را به عنوان بخشی از فرآیند بازیابی بازیابی کند، بنابراین هیچ رضایت اضافی لازم نیست. وقتی کاربر برنامه شما را باز می‌کند، می‌توانید با فراخوانی retrieveBytes() توکن خود را از Block Store درخواست کنید. توکن بازیابی شده سپس می‌تواند برای ورود کاربر به دستگاه جدید استفاده شود.

نمونه زیر نحوه بازیابی چندین توکن بر اساس کلیدهای خاص را نشان می‌دهد.

جاوا

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));

کاتلین

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 آورده شده است.

جاوا

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));

کاتلین

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

در زیر مثالی از نحوه بازیابی کلید پیش‌فرض آورده شده است.

جاوا

BlockStoreClient client = Blockstore.getClient(this);
RetrieveBytesRequest retrieveRequest = new RetrieveBytesRequest.Builder()
    .setKeys(Arrays.asList(BlockstoreClient.DEFAULT_BYTES_DATA_KEY))
    .build();
client.retrieveBytes(retrieveRequest);

کاتلین

val client = Blockstore.getClient(this)

val retrieveRequest = RetrieveBytesRequest.Builder()
  .setKeys(Arrays.asList(BlockstoreClient.DEFAULT_BYTES_DATA_KEY))
  .build()
client.retrieveBytes(retrieveRequest)

حذف توکن‌ها

حذف توکن‌ها از BlockStore ممکن است به دلایل زیر ضروری باشد:

  • کاربر مراحل خروج از سیستم (یا خروج از سیستم) را طی می‌کند.
  • توکن باطل شده یا نامعتبر است.

مشابه بازیابی توکن‌ها، می‌توانید با تنظیم آرایه‌ای از کلیدهایی که نیاز به حذف دارند، مشخص کنید کدام توکن‌ها نیاز به حذف دارند.

مثال زیر نحوه حذف کلیدهای خاص را نشان می‌دهد:

جاوا

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)

کاتلین

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 را نشان می‌دهد:

جاوا

// 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));

کاتلین

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

فعال کردن پشتیبان‌گیری ابری

برای فعال کردن پشتیبان‌گیری ابری، متد setShouldBackupToCloud() به شیء StoreBytesData خود اضافه کنید. Block Store به صورت دوره‌ای از بایت‌های ذخیره شده در فضای ابری پشتیبان‌گیری می‌کند، زمانی که setShouldBackupToCloud() روی true تنظیم شده باشد.

نمونه زیر نحوه فعال کردن پشتیبان‌گیری ابری را فقط در زمانی که پشتیبان‌گیری ابری رمزگذاری سرتاسری شده است، نشان می‌دهد:

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

چگونه آزمایش کنیم

برای آزمایش جریان‌های بازیابی، در طول توسعه از روش‌های زیر استفاده کنید.

همان دستگاه را حذف/نصب مجدد کنید

اگر کاربر سرویس‌های پشتیبان‌گیری را فعال کند (می‌توانید آن را در تنظیمات > گوگل > پشتیبان‌گیری بررسی کنید)، داده‌های مسدودسازی فروشگاه در طول حذف/نصب مجدد برنامه حفظ می‌شوند.

برای تست می‌توانید مراحل زیر را دنبال کنید:

  1. API فروشگاه بلوک را با برنامه آزمایشی خود ادغام کنید.
  2. از برنامه آزمایشی برای فراخوانی API فروشگاه بلوک جهت ذخیره داده‌های خود استفاده کنید.
  3. برنامه آزمایشی خود را حذف نصب کنید و سپس برنامه خود را دوباره در همان دستگاه نصب کنید.
  4. از برنامه آزمایشی برای فراخوانی API فروشگاه بلوک و بازیابی داده‌های خود استفاده کنید.
  5. تأیید کنید که بایت‌های بازیابی‌شده همان‌هایی هستند که قبل از حذف نصب ذخیره شده‌اند.

دستگاه به دستگاه

در بیشتر موارد، این کار نیاز به تنظیم مجدد کارخانه دستگاه هدف دارد. سپس می‌توانید وارد جریان بازیابی بی‌سیم اندروید یا بازیابی کابل گوگل (برای دستگاه‌های پشتیبانی‌شده) شوید.

بازیابی ابری

  1. API فروشگاه بلوک را با برنامه آزمایشی خود ادغام کنید. برنامه آزمایشی باید به فروشگاه Play ارسال شود.
  2. در دستگاه مبدا، از برنامه آزمایشی برای فراخوانی API فروشگاه بلوک جهت ذخیره داده‌های خود استفاده کنید، در حالی که shouldBackUpToCloud روی true تنظیم شده است.
  3. برای دستگاه‌های O و بالاتر، می‌توانید به صورت دستی پشتیبان‌گیری ابری Block Store را فعال کنید: به تنظیمات > گوگل > پشتیبان‌گیری بروید، روی دکمه «Backup Now» کلیک کنید.
    1. برای تأیید موفقیت‌آمیز بودن پشتیبان‌گیری ابری Block Store، می‌توانید:
      1. پس از اتمام پشتیبان‌گیری، خطوط گزارش با برچسب «CloudSyncBpTkSvc» را جستجو کنید.
      2. شما باید خطوطی مانند این را ببینید: "......، CloudSyncBpTkSvc: نتیجه همگام‌سازی: موفقیت، ...، اندازه آپلود شده: XXX بایت ..."
    2. پس از پشتیبان‌گیری ابری Block Store، یک دوره ۵ دقیقه‌ای «آرام‌سازی» وجود دارد. در این ۵ دقیقه، کلیک بر روی دکمه «اکنون پشتیبان‌گیری کنید» باعث ایجاد پشتیبان‌گیری ابری دیگری در Block Store نخواهد شد.
  4. دستگاه هدف را به تنظیمات کارخانه برگردانید و از طریق یک جریان بازیابی ابری اقدام کنید. برای بازیابی برنامه آزمایشی خود در طول جریان بازیابی، گزینه بازیابی را انتخاب کنید. برای اطلاعات بیشتر در مورد جریان‌های بازیابی ابری، به جریان‌های بازیابی ابری پشتیبانی‌شده مراجعه کنید.
  5. در دستگاه هدف، از برنامه آزمایشی برای فراخوانی API فروشگاه بلوک و بازیابی داده‌های خود استفاده کنید.
  6. تأیید کنید که بایت‌های بازیابی‌شده همان بایت‌های ذخیره‌شده در دستگاه مبدا هستند.

الزامات دستگاه

رمزگذاری سرتاسری

  • رمزگذاری سرتاسری (End to End) در دستگاه‌هایی که اندروید ۹ (API 29) و بالاتر دارند پشتیبانی می‌شود.
  • برای فعال شدن رمزگذاری سرتاسری و رمزگذاری صحیح داده‌های کاربر، دستگاه باید دارای قفل صفحه با پین، الگو یا رمز عبور باشد.

جریان بازیابی دستگاه به دستگاه

بازیابی دستگاه به دستگاه به یک دستگاه مبدا و یک دستگاه مقصد نیاز دارد. این دو دستگاه، دستگاه‌هایی هستند که داده‌ها را منتقل می‌کنند.

دستگاه‌های مبدا برای پشتیبان‌گیری باید اندروید ۶ (API 23) و بالاتر را اجرا کنند.

دستگاه‌هایی که اندروید ۹ (API 29) و بالاتر دارند را هدف قرار دهید تا قابلیت بازیابی داشته باشند.

اطلاعات بیشتر در مورد جریان بازیابی دستگاه به دستگاه را می‌توانید اینجا بیابید.

جریان پشتیبان‌گیری و بازیابی ابری

پشتیبان‌گیری و بازیابی ابری به یک دستگاه مبدا و یک دستگاه مقصد نیاز دارد.

دستگاه‌های مبدا برای پشتیبان‌گیری باید اندروید ۶ (API 23) و بالاتر را اجرا کنند.

دستگاه‌های هدف بر اساس فروشندگانشان پشتیبانی می‌شوند. دستگاه‌های پیکسل می‌توانند از این ویژگی از اندروید ۹ (API 29) استفاده کنند و سایر دستگاه‌ها باید اندروید ۱۲ (API 31) یا بالاتر را اجرا کنند.