مسدود کردن فروشگاه

بسیاری از کاربران هنوز هنگام راه‌اندازی دستگاه جدید مجهز به 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 به فایل ساخت 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)
  }

درحال بازیابی کد

بعداً، وقتی کاربر جریان بازیابی را در دستگاه جدیدی طی می‌کند، خدمات Google Play ابتدا کاربر را تأیید می‌کند، سپس داده‌های 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<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));

کاتلین

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

حذف همه کدها

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

جاوا

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

کاتلین

// Delete all data.
val deleteAllRequest = DeleteBytesRequest.Builder()
        .setDeleteAll(true)
        .build()
client.deleteBytes(deleteAllRequest)
  .addOnSuccessListener { result -> Log.d(TAG, "Any data found and deleted? $result") }

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

برای اینکه رمزگذاری سرتاسر دردسترس قرار گیرد، دستگاه باید Android نسخه ۹ یا بالاتر داشته باشد و کاربر باید قفل صفحه (پین، الگو، یا گذرواژه) برای دستگاهش تنظیم کرده باشد. با تماس با isEndToEndEncryptionAvailable() می‌توانید بررسی کنید که آیا رمزگذاری در دستگاه دردسترس خواهد بود یا نه.

نمونه زیر نشان می‌دهد که چگونه می‌توان بررسی کرد که آیا رمزگذاری درطول پشتیبان‌گیری ابری دردسترس خواهد بود یا نه:

client.isEndToEndEncryptionAvailable()
        .addOnSuccessListener { result ->
          Log.d(TAG, "Will Block Store cloud backup be end-to-end encrypted? $result")
        }

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

برای فعال کردن پشتیبان‌گیری ابری، روش setShouldBackupToCloud() را به شیء StoreBytesData اضافه کنید. وقتی setShouldBackupToCloud() روی درست تنظیم شده باشد، 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 درصورت حذف نصب و بازنصب برنامه حفظ می‌شود.

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

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

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

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

بازیابی ابری

  1. Block Store API را با برنامه آزمایشی‌تان ادغام کنید. برنامه آزمایشی باید به «فروشگاه Play» ارسال شود.
  2. در دستگاه منبع، از برنامه آزمایشی برای فراخوانی Block Store API برای ذخیره کردن داده‌هایتان استفاده کنید، درحالی‌که shouldBackUpToCloud روی true تنظیم شده است.
  3. در دستگاه‌های دارای Android نسخه ۸.۰ (سطح API‏ ۲۶) یا جدیدتر، می‌توانید به‌صورت دستی پشتیبان‌گیری ابری Block Store را راه‌اندازی کنید: به تنظیمات > Google > پشتیبان‌گیری بروید، روی دکمه «اکنون پشتیبان‌گیری شود» کلیک کنید.
    1. برای تأیید اینکه پشتیبان‌گیری ابری Block Store موفقیت‌آمیز بوده است، می‌توانید:
      1. پس‌از تکمیل پشتیبان‌گیری، خطوط گزارش را با برچسب [CloudSyncBackupTaskService] جستجو کنید.
      2. باید خطوطی مثل این ببینید: ..., [CloudSyncBackupTaskService] sync result: SUCCESS, ..., uploaded size: [size] bytes ...
    2. پس‌از پشتیبان‌گیری ابری Block Store، یک دوره «آرامش» ۵ دقیقه‌ای وجود دارد. در این ۵ دقیقه، کلیک کردن روی دکمه «اکنون پشتیبان‌گیری شود» پشتیبان‌گیری ابری Block Store دیگری را راه‌اندازی نمی‌کند.
  4. دستگاه هدف را بازنشانی کارخانه‌ای کنید و جریان بازیابی ابری را طی کنید. را انتخاب کنید تا برنامه آزمایشی‌تان را درطول جریان بازیابی، بازیابی کنید. برای اطلاعات بیشتر درباره جریان‌های بازیابی ابری، به جریان‌های بازیابی ابری پشتیبانی‌شده مراجعه کنید.
  5. در دستگاه هدف، از برنامه آزمایشی برای فراخوانی Block store API برای بازیابی داده‌هایتان استفاده کنید.
  6. تأیید کنید که بایت‌های بازیابی‌شده با بایت‌های ذخیره‌شده در دستگاه منبع یکسان است.

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

الزامات زیر را برای دستگاه‌های شرکت‌کننده در عملیات Block Store مرور کنید.

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

  • رمزگذاری سرتاسر در دستگاه‌های دارای Android 9 (میانای برنامه‌سازی کاربردی ۲۹) یا جدیدتر پشتیبانی می‌شود.
  • دستگاه باید قفل صفحه‌ای داشته باشد که با پین، الگو، یا گذرواژه تنظیم شده باشد تا رمزگذاری سرتاسر فعال شود و داده‌های کاربر را به‌درستی رمزگذاری کند.

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

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

دستگاه‌های منبع برای پشتیبان‌گیری باید Android 6 (میانای برنامه‌سازی کاربردی ۲۳) یا بالاتر داشته باشند.

دستگاه‌های هدف که Android 9 (میانای برنامه‌سازی کاربردی ۲۹) یا بالاتر را اجرا می‌کنند، قابلیت بازیابی را دارند.

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

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

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

دستگاه‌های منبع برای پشتیبان‌گیری باید Android 6 (میانای برنامه‌سازی کاربردی ۲۳) یا بالاتر داشته باشند.

دستگاه‌های هدف براساس فروشندگانشان پشتیبانی می‌شوند. دستگاه‌های Pixel می‌توانند از Android 9 (میانای برنامه‌سازی کاربردی ۲۹) از این ویژگی استفاده کنند و همه دستگاه‌های دیگر باید Android 12 (میانای برنامه‌سازی کاربردی ۳۱) یا بالاتر را اجرا کنند.