משתמשים רבים עדיין מנהלים את פרטי הכניסה שלהם כשהם מגדירים מכשיר חדש עם Android. התהליך הידני הזה יכול להיות מאתגר ולרוב הוא גורם לחוויית משתמש גרועה. כדי לפתור את הבעיה הזו, Block Store API, ספרייה שמבוססת על Google Play Services, מאפשרת לאפליקציות לשמור את פרטי הכניסה של המשתמשים בלי המורכבות או סיכון האבטחה שקשורים לשמירת סיסמאות של משתמשים.
Block Store API מאפשר לאפליקציה לאחסן נתונים שאפשר לאחזר מאוחר יותר כדי לבצע אימות מחדש של משתמשים במכשיר חדש. כך תוכלו לספק למשתמש חוויה חלקה יותר, כי הוא לא יצטרך לראות מסך כניסה כשהאפליקציה תופעל בפעם הראשונה במכשיר החדש.
היתרונות של השימוש ב-Block Store כוללים:
- פתרון לאחסון פרטי כניסה מוצפנים למפתחים. כשהדבר אפשרי, פרטי הכניסה מוצפנים מקצה לקצה.
- שומרים אסימונים במקום שמות משתמשים וסיסמאות.
- ביטול ההתנגדות בתהליכי הכניסה לחשבון.
- משחררים את המשתמשים מהטרחה של ניהול סיסמאות מורכבות.
- Google מאמתת את הזהות של המשתמש.
לפני שמתחילים
כדי להכין את האפליקציה, מבצעים את השלבים שמפורטים בקטעים הבאים.
הגדרת האפליקציה
בקובץ build.gradle
ברמת הפרויקט, צריך לכלול את מאגר Maven של Google גם בקטע buildscript
וגם בקטע allprojects
:
buildscript {
repositories {
google()
mavenCentral()
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
מוסיפים את התלות של Google Play Services ב-Block Store API לקובץ ה-build של Gradle של המודול, שבדרך כלל הוא app/build.gradle
:
dependencies {
implementation 'com.google.android.gms:play-services-auth-blockstore:16.4.0'
}
איך זה עובד
Block Store מאפשר למפתחים לשמור ולשחזר מערכי בייטים של עד 16 בייטים. כך תוכלו לשמור מידע חשוב לגבי סשן המשתמש הנוכחי, ותוכלו לשמור את המידע הזה בכל דרך שתרצו. אפשר להצפין את הנתונים האלה מקצה לקצה, והתשתית שתומכת ב-Block Store מבוססת על תשתית הגיבוי והשחזור.
במדריך הזה נסביר על תרחיש לדוגמה של שמירת אסימון של משתמש ב-Block Store. השלבים הבאים מתארים את אופן הפעולה של אפליקציה שמשתמשת ב-Block Store:
- במהלך תהליך האימות של האפליקציה, או בכל שלב אחר כך, אפשר לאחסן את אסימון האימות של המשתמש ב-Block Store כדי לאחזר אותו מאוחר יותר.
- האסימון יישמר באופן מקומי, וניתן גם לגבות אותו בענן, מוצפן מקצה לקצה כשהדבר אפשרי.
- הנתונים מועברים כשהמשתמש מתחיל תהליך שחזור במכשיר חדש.
- אם המשתמש משחזר את האפליקציה במהלך תהליך השחזור, האפליקציה יכולה לאחזר את האסימון השמור מ-Block Store במכשיר החדש.
שמירת הטוקן
כשמשתמש נכנס לאפליקציה, אפשר לשמור את אסימון האימות שיצרתם עבור המשתמש הזה ב-Block Store. אפשר לאחסן את האסימון הזה באמצעות ערך ייחודי של זוג מפתחות, שגודל כל רשומה בו הוא 4KB לכל היותר. כדי לשמור את האסימון, צריך להפעיל את הפונקציות 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. המשתמש כבר הסכים לשחזור נתוני האפליקציה כחלק מתהליך השחזור, כך שאין צורך בהסכמות נוספות. כשהמשתמש פותח את האפליקציה, אפשר לבקש את האסימון מ-Block Store באמצעות קריאה ל-retrieveBytes()
. לאחר מכן אפשר להשתמש באסימון שאוחזר כדי שהמשתמש יישאר מחובר במכשיר החדש.
בדוגמה הבאה מוסבר איך לאחזר כמה אסימונים על סמך מפתחות ספציפיים.
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 ListrequestedKeys = 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 ListrequestedKeys = 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 keyBlockstoreClient.DEFAULT_BYTES_DATA_KEY
can be used in theRetrieveBytesRequest
instance in order to get your saved data
The following example shows how to retrieve the default key.
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.")
}
}
איך בודקים
במהלך הפיתוח, אפשר להשתמש בשיטות הבאות כדי לבדוק את תהליכי השחזור.
הסרה/התקנה מחדש באותו מכשיר
אם המשתמש מפעיל את שירותי הגיבוי (אפשר לבדוק זאת בקטע הגדרות > Google > גיבוי), הנתונים של Block Store נשמרים גם אחרי הסרת האפליקציה או ההתקנה מחדש שלה.
כדי לבדוק את הדברים הבאים:
- משלבים את Block Store API באפליקציית הבדיקה.
- משתמשים באפליקציית הבדיקה כדי להפעיל את Block Store API לאחסון הנתונים.
- מסירים את אפליקציית הבדיקה ומתקינים אותה מחדש באותו מכשיר.
- משתמשים באפליקציית הבדיקה כדי להפעיל את Block Store API לאחזור הנתונים.
- מוודאים שהבייטים שאוחזרו זהים לאלה ששמורים לפני ההסרה.
מכשיר-מכשיר
ברוב המקרים, תצטרכו לאפס את מכשיר היעד להגדרות המקוריות. לאחר מכן תוכלו להיכנס לתהליך השחזור האלחוטי של Android או לשחזור באמצעות כבל של Google (למכשירים נתמכים).
שחזור ב-Cloud
- משלבים את Block Store API באפליקציית הבדיקה. צריך לשלוח את אפליקציית הבדיקה אל חנות Play.
- במכשיר המקור, משתמשים באפליקציית הבדיקה כדי להפעיל את Block Store API לאחסון הנתונים, כאשר הערך של
shouldBackUpToCloud
מוגדר כ-true
. - במכשירים מדגם O ואילך, אפשר להפעיל באופן ידני גיבוי בענן של Block Store: עוברים אל הגדרות > Google > גיבוי ולוחצים על הלחצן 'גיבוי עכשיו'.
- כדי לוודא שהגיבוי בענן של Block Store הצליח, אפשר:
- אחרי שהגיבוי מסתיים, מחפשים שורות ביומן עם התג 'CloudSyncBpTkSvc'.
- אמורות להופיע שורות כמו: “......, CloudSyncBpTkSvc: sync result: SUCCESS, ..., uploaded size: XXX bytes …”
- אחרי גיבוי בענן של Block Store, יש תקופת 'השהיה' של 5 דקות. במהלך 5 הדקות האלה, לחיצה על הלחצן 'גיבוי עכשיו' לא תפעיל גיבוי נוסף בענן של Block Store.
- כדי לוודא שהגיבוי בענן של Block Store הצליח, אפשר:
- מאפסים את מכשיר היעד להגדרות המקוריות ומבצעים תהליך שחזור בענן. בוחרים לשחזר את אפליקציית הבדיקה במהלך תהליך השחזור. למידע נוסף על תהליכי שחזור בענן, ראו תהליכי שחזור נתמכים בענן.
- במכשיר היעד, משתמשים באפליקציית הבדיקה כדי להפעיל את Block store API כדי לאחזר את הנתונים.
- מוודאים שהבייטים שאוחזרו זהים לאלה שנשמרו במכשיר המקור.
דרישות לגבי מכשירים
הצפנה מקצה לקצה
- ההצפנה מקצה לקצה נתמכת במכשירים עם Android מגרסה 9 (API 29) ואילך.
- כדי שההצפנה מקצה לקצה תופעל ותצפין בצורה נכונה את נתוני המשתמש, צריך להגדיר במכשיר נעילת מסך עם קוד אימות, קו ביטול נעילה או סיסמה.
תהליך השחזור ממכשיר למכשיר
כדי לבצע שחזור ממכשיר למכשיר, צריך מכשיר מקור ומכשיר יעד. אלה יהיו שני המכשירים שמעבירים את הנתונים.
כדי ליצור גיבוי, במכשירי המקור צריכה להיות מותקנת מערכת Android מגרסה 6 (API 23) ואילך.
טירגטו מכשירים עם Android מגרסה 9 (API 29) ואילך כדי שתהיה לכם אפשרות לשחזר.
כאן אפשר למצוא מידע נוסף על תהליך השחזור ממכשיר למכשיר.
תהליך הגיבוי והשחזור ב-Cloud
כדי לגבות ולשחזר ב-Cloud, נדרשים מכשיר מקור ומכשיר יעד.
כדי ליצור גיבוי, במכשירי המקור צריכה להיות מותקנת מערכת Android מגרסה 6 (API 23) ואילך.
יש תמיכה במכשירי Target בהתאם לספקים שלהם. במכשירי Pixel אפשר להשתמש בתכונה הזו החל מגרסה 9 של Android (API 29), ובכל שאר המכשירים צריכה לפעול מערכת Android מגרסה 12 (API 31) ואילך.