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

بسیاری از کاربران هنگام راه‌اندازی دستگاه جدید مجهز به Android همچنان خودشان اطلاعات اعتباری‌شان را مدیریت می‌کنند. این فرایند دستی می‌تواند چالش‌برانگیز شود و اغلب منجر به تجربه کاربری ضعیف می‌شود. «میانای برنامه‌سازی کاربردی فروشگاه بلوک» که کتابخانه‌ای با پشتیبانی خدمات Google Play است، با ارائه روشی برای ذخیره کردن اطلاعات اعتباری کاربر توسط برنامه‌ها بدون پیچیدگی یا خطر امنیتی مرتبط با ذخیره کردن گذرواژه‌های کاربر، به‌دنبال حل این مشکل است.

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

مزایای استفاده از Block Store شامل موارد زیر است:

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

قبل‌از شروع

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

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

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

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

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

وابستگی خدمات Google Play را برای «میانای برنامه‌سازی کاربردی Block Store» به فایل ساخت Gradle واحد اضافه کنید، که معمولاً app/build.gradle است:

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

روش کار

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

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

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

درحال ذخیره کردن کد

وقتی کاربری به سیستم برنامه‌تان وارد می‌شود، می‌توانید نشان اصالت‌سنجی را که برای آن کاربر تولید می‌کنید در «فروشگاه بلوک» ذخیره کنید. می‌توانید این کد را بااستفاده از مقدار جفت کلید منحصربه‌فردی که حداکثر ۴ کیلوبایت در هر ورودی دارد ذخیره کنید. برای ذخیره کردن کد، در نمونه‌ای از StoreBytesData.Builder، setBytes() و setKey() را فراخوانی کنید تا اعتبارنامه‌های کاربر در دستگاه منبع ذخیره شود. پس‌از ذخیره کردن رمز با 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 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 اضافه کنید. وقتی setShouldBackupToCloud() روی درست تنظیم شده باشد، «فروشگاه مسدود» به‌صورت دوره‌ای از بایت‌های ذخیره‌شده در ابر نسخه پشتیبان تهیه می‌کند.

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

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 > پشتیبان‌گیری بررسی کنید)، داده‌های «مسدود کردن فروشگاه» درطول حذف/نصب مجدد برنامه حفظ می‌شود.

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

  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. برای دستگاه‌های دارای سیستم‌عامل O و نسخه‌های بالاتر، می‌توانید پشتیبان‌گیری ابری «فروشگاه بلوک» را به‌صورت دستی راه‌اندازی کنید: به تنظیمات > Google > پشتیبان‌گیری بروید، روی دکمه «پشتیبان‌گیری، اکنون» کلیک کنید.
    1. برای درستی‌سنجی اینکه پشتیبان‌گیری ابری «فروشگاه بلوک» موفقیت‌آمیز بوده است، می‌توانید:
      1. پس‌از اتمام پشتیبان‌گیری، خطوط گزارش را با برچسب «CloudSyncBpTkSvc» جستجو کنید.
      2. باید خطوطی مانند این ببینید: «......, CloudSyncBpTkSvc: sync result: SUCCESS, ..., uploaded size: XXX bytes ...»
    2. پس‌از پشتیبان‌گیری ابری «فروشگاه بلوک»، یک دوره «خنک‌سازی» ۵ دقیقه‌ای وجود دارد. در این ۵ دقیقه، کلیک کردن روی دکمه «پشتیبان‌گیری، اکنون» باعث راه‌اندازی پشتیبان‌گیری ابری دیگری از Block Store نخواهد شد.
  4. دستگاه هدف را بازنشانی کارخانه‌ای کنید و جریان بازیابی از فضای ابری را انجام دهید. برای بازگرداندن برنامه آزمایشی‌تان درطول جریان بازگرداندن، انتخاب کنید. برای اطلاعات بیشتر درباره جریان‌های بازگردانی از فضای ابری، به جریان‌های بازگردانی از فضای ابری پشتیبانی‌شده مراجعه کنید.
  5. در دستگاه هدف، از برنامه آزمایشی برای فراخوانی Block store API به‌منظور بازیابی داده‌هایتان استفاده کنید.
  6. تأیید کنید که بایت‌های بازیابی‌شده با بایت‌های ذخیره‌شده در دستگاه منبع یکسان باشند.

پیش‌نیازهای دستگاه

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

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

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

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

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

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

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

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

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

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

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