חסימת החנות

משתמשים רבים עדיין מנהלים את פרטי הכניסה שלהם כשמגדירים מכשיר Android חדש במכשיר. התהליך הידני הזה יכול להיות מאתגר ולרוב חוויית המשתמש. Block Store API – ספרייה שמופעלת על ידי Google Play שירותים, נראה כדי לפתור את הבעיה על ידי מתן אפשרות לאפליקציות לשמור פרטי הכניסה של המשתמש, ללא המורכבות או סיכון האבטחה הכרוכים בשמירה סיסמאות של משתמשים.

ה-API של חנות החסימה מאפשר לאפליקציה שלך לאחסן נתונים שניתן יהיה להשתמש בהם מאוחר יותר אחזור לצורך אימות מחדש של משתמשים במכשיר חדש. כך אנחנו יכולים לספק לחוויית משתמש חלקה, כי הוא לא צריך לראות מסך כניסה כשמפעילים את האפליקציה בפעם הראשונה במכשיר החדש.

היתרונות של השימוש ב-block Store כוללים את היתרונות הבאים:

  • פתרון מוצפן לאחסון פרטי כניסה עבור מפתחים. פרטי הכניסה הם הצפנה מקצה לקצה, כשזה אפשרי.
  • שמירת אסימונים במקום שמות משתמש וסיסמאות.
  • למנוע את הקשיים בתהליכי הכניסה.
  • הצילו את המשתמשים מהנטל של ניהול סיסמאות מורכבות.
  • Google מאמתת את זהות המשתמש.

לפני שמתחילים

כדי להכין את האפליקציה, צריך לבצע את השלבים בקטעים הבאים.

הגדרת האפליקציה

בקובץ build.gradle ברמת הפרויקט, כוללים את Maven של Google במאגר גם ב-buildscript ו-allprojects:

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

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

הוספה של Google Play Services תלויות ב-block Store API קובץ Gradle של המודול, בדרך כלל app/build.gradle:

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

איך זה עובד

חסימת החנות מאפשרת למפתחים לשמור ולשחזר מערכים של עד 16 בייטים. כך אפשר לשמור מידע חשוב לגבי הסשן הנוכחי של המשתמש, וגם לשמור את המידע הזה איך שרוצים. הנתונים האלה יכולים להיות מוצפנים מקצה לקצה, והתשתית שתומכת ב-block Store מבוססת על תשתית הגיבוי והשחזור.

מדריך זה עוסק בתרחיש לדוגמה של שמירת אסימון של משתמש ב-block Store. השלבים הבאים מתארים איך תפעל אפליקציה שמשתמשת ב-Block Store:

  1. במהלך תהליך האימות של האפליקציה, או בכל שלב לאחר מכן, תוכלו לאחסן אסימון האימות של המשתמש כדי לחסום את החנות לאחזור מאוחר יותר.
  2. האסימון יאוחסן באופן מקומי ואפשר גם לגבות אותו בענן, הצפנה מקצה לקצה, כשזה אפשרי.
  3. הנתונים מועברים כשהמשתמש מתחיל תהליך שחזור במכשיר חדש.
  4. אם המשתמש ישחזר את האפליקציה במהלך השחזור, האפליקציה תוכל לאחר מכן לאחזר את האסימון שנשמר מ-block Store במכשיר החדש.

שמירת האסימון

כשמשתמש נכנס לאפליקציה שלכם, אתם יכולים לשמור את אסימון האימות שיצרתם בשבילו ב'חסימת החנות'. אפשר לשמור את האסימון הזה באמצעות ערך ייחודי של זוג מפתחות שכולל עד 4kb לכל רשומה. כדי לאחסן את האסימון, צריך להתקשר למספר 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 Services מאמתת קודם את המשתמש ואז מאחזרת את החסימה אחסון נתונים. המשתמש כבר הסכים לשחזר את נתוני האפליקציה שלך במסגרת את תהליך השחזור, כך שלא נדרשת הסכמה נוספת. כשהמשתמש פותח ניתן לבקש את האסימון מ-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

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

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

List requestedKeys = 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()
client.deleteBytes(deleteAllRequest)
  .addOnSuccessListener { result: Boolean ->
    Log.d(TAG,
          "Any data found and deleted? $result")
  }

הצפנה מקצה לקצה

כדי שההצפנה מקצה לקצה תהיה זמינה, המכשיר צריך להיות עם Android מגרסה 9 ואילך, והמשתמש צריך להגדיר נעילת מסך (קוד אימות, קו ביטול נעילה או סיסמה) למכשיר. אפשר לוודא שההצפנה יהיה זמין במכשיר באמצעות הטלפון isEndToEndEncryptionAvailable().

הדוגמה הבאה מראה איך לוודא שההצפנה תהיה זמינה גיבוי בענן:

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

הפעלת גיבוי בענן

כדי להפעיל גיבוי בענן, צריך להוסיף את הקוד setShouldBackupToCloud() לשיטה StoreBytesData לאובייקט. כאשר הערך של 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.")
          }
        }

איך בודקים את ההגדרות

יש להשתמש בשיטות הבאות במהלך הפיתוח כדי לבדוק את השחזור זורמים.

הסרה/התקנה מחדש באותו מכשיר

אם המשתמש מפעיל שירותי גיבוי (ניתן לבדוק זאת בהגדרות > Google > גיבוי), נתוני החסימה של החנות הם מופיעים באופן עקבי בכל מקום בהסרה/התקנה מחדש של האפליקציה.

אפשר לבצע את השלבים הבאים כדי לבדוק את:

  1. משלבים את BlockStore API באפליקציית הבדיקה.
  2. כדי לאחסן את הנתונים, צריך להשתמש באפליקציית הבדיקה כדי להפעיל את BlockStore API.
  3. צריך להסיר את אפליקציית הבדיקה ולהתקין אותה מחדש באותו מכשיר.
  4. כדי לאחזר את הנתונים, צריך להשתמש באפליקציית הבדיקה כדי להפעיל את BlockStore API.
  5. צריך לוודא שהבייטים שאוחזרו זהים לאלה שאוחסנו לפני כן הסרה.

מכשיר למכשיר

ברוב המקרים, צריך לאפס את מכשיר היעד להגדרות המקוריות. אפשר ולאחר מכן עוברים אל תהליך השחזור האלחוטי של Android. או לשחזור כבל של Google (למכשירים נתמכים).

שחזור ענן

  1. משלבים את ה-API של Blockstore באפליקציית הבדיקה. אפליקציית הבדיקה צריכה להיות שנשלחו לחנות Play.
  2. במכשיר המקור, משתמשים באפליקציית הבדיקה כדי להפעיל את Blockstore API לאחסון כאשר הנתונים שלך צריכים להיות True, כאשר הפרמטר צריךBackUpToCloud מוגדר כ-true.
  3. במכשירים עם O ואילך, אפשר להפעיל באופן ידני גיבוי בענן של Block Store: עבור אל הגדרות > Google > גיבוי, לוחצים על הלחצן 'גיבוי עכשיו'.
    1. כדי לוודא שהגיבוי בענן של Block Store בוצע בהצלחה, אתם יכולים:
      1. כשהגיבוי מסתיים, מחפשים שורות יומן עם תג "CloudSyncBpTkSvc"
      2. אתם אמורים לראות שורות כאלה: "......, CloudSyncBpTkSvc: sync" result: הצלחה, ..., גודל שהועלה: XXX בייטים ..."
    2. אחרי גיבוי בענן ב-Block Store, יש פרק זמן של 5 דקות 'קירור'. במהלך 5 הדקות האלה, לחיצה על הלחצן 'גיבוי עכשיו' לא תופעל גיבוי בענן נוסף של Block Store.
  4. צריך לאפס את מכשיר היעד להגדרות המקוריות ולבצע תהליך שחזור בענן. יש לבחור באחת מהאפשרויות הבאות כדי לשחזר את אפליקציית הבדיקה במהלך השחזור. מידע נוסף על למידע על תהליכי שחזור ענן, ראו תהליכי שחזור ענן נתמכים.
  5. במכשיר היעד, משתמשים באפליקציית הבדיקה כדי להפעיל את Blockstore API כדי לאחזר את הנתונים שלכם.
  6. צריך לוודא שהבייטים שאוחזרו זהים לאלה שאוחסנו מכשיר המקור.

דרישות מכשיר

הצפנה מקצה לקצה

  • ההצפנה מקצה לקצה נתמכת במכשירים עם Android מגרסה 9 (API 29) ואילך.
  • כדי שההצפנה מקצה לקצה תופעל והצפנה נכונה של נתוני המשתמש, צריכה להיות במכשיר נעילת מסך עם קוד אימות, קו ביטול נעילה או סיסמה.

תהליך שחזור מכשיר למכשיר

כדי לשחזר מכשיר למכשיר, צריך שיהיה לך מכשיר מקור ומכשיר יעד. אלה יהיו שני המכשירים שמעבירים נתונים.

כדי לגבות, במכשירים עם המקור צריכה לפעול מערכת Android בגרסה 6 (API 23) ואילך.

כדי שתהיה אפשרות לשחזר נתונים, צריך לטרגט מכשירים עם Android מגרסה 9 (API 29) ואילך.

מידע נוסף על תהליך השחזור של המכשיר למכשיר זמין כאן.

תהליך גיבוי ושחזור בענן

כדי לגבות ולשחזר בענן, יש צורך במכשיר מקור ומכשיר יעד.

כדי לגבות, במכשירים עם המקור צריכה לפעול מערכת Android בגרסה 6 (API 23) ואילך.

מכשירי טירגוט נתמכים בהתאם לספקים שלהם. במכשירי Pixel אפשר להשתמש בתכונה הזו החל מ-Android 9 (API 29) ובכל שאר המכשירים צריכה לפעול מערכת Android בגרסה 12 (API 31) ואילך.