חסימת החנות

משתמשים רבים עדיין מנהלים את פרטי הכניסה שלהם כשהם מגדירים מכשיר 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.2.0'
}

איך זה עובד

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

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

  1. בתהליך האימות של האפליקציה, או בכל שלב לאחר מכן, תוכלו לאחסן את אסימון האימות של המשתמש ב-block Store כדי לאחזר אותו מאוחר יותר.
  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 blockstoreDataMap = result.getBlockstoreDataMap();
          for (Map.Entry 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 blockstoreDataMap = result.getBlockstoreDataMap();
          for (Map.Entry 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")
        }

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

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

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

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

מכשיר למכשיר

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

שחזור ענן

  1. משלבים את ה-API של Blockstore באפליקציית הבדיקה. צריך לשלוח את אפליקציית הבדיקה לחנות Play.
  2. במכשיר המקור, משתמשים באפליקציית הבדיקה כדי להפעיל את ה-API של Blockstore כדי לאחסן את הנתונים, כש-BackUpToCloud צריך להגדיר את הערך True.
  3. עבור מכשירים O ואילך, אפשר להפעיל באופן ידני גיבוי בענן של Block Store: עוברים להגדרות > Google > גיבוי ולוחצים על הלחצן 'Backup Now'.
    1. כדי לוודא שהגיבוי בענן של Block Store בוצע בהצלחה, אתם יכולים:
      1. כשהגיבוי מסתיים, מחפשים שורות יומן עם התג "CloudSyncBpTkSvc".
      2. אמורות להופיע שורות כך: "......, CloudSyncBpTkSvc: sync result: Success, ..., upload size: XXX byte ..."
    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) ואילך.