Chặn cửa hàng

Nhiều người dùng vẫn quản lý thông tin xác thực của riêng họ khi thiết lập một thiết bị Android mới. Quy trình thủ công này có thể trở nên khó khăn và thường dẫn đến trải nghiệm người dùng kém. API Block Store, một thư viện do Dịch vụ Google Play cung cấp, tìm cách giải quyết vấn đề này bằng cách cung cấp một cách để các ứng dụng lưu thông tin xác thực người dùng mà không gặp phải sự phức tạp hoặc rủi ro bảo mật liên quan đến việc lưu mật khẩu người dùng.

API Block Store cho phép ứng dụng của bạn lưu trữ dữ liệu mà sau này ứng dụng có thể truy xuất để xác thực lại người dùng trên một thiết bị mới. Điều này giúp người dùng có trải nghiệm liền mạch hơn vì họ không cần xem màn hình đăng nhập khi khởi chạy ứng dụng lần đầu tiên trên thiết bị mới.

Lợi ích của việc sử dụng Block Store là:

  • Giải pháp lưu trữ thông tin xác thực đã mã hoá cho nhà phát triển. Thông tin xác thực được mã hoá hai đầu khi có thể.
  • Lưu mã thông báo thay vì tên người dùng và mật khẩu.
  • Loại bỏ phiền hà khỏi quy trình đăng nhập.
  • Giúp người dùng không còn phải quản lý các mật khẩu phức tạp.
  • Google xác minh danh tính của người dùng.

Trước khi bắt đầu

Để chuẩn bị cho ứng dụng của bạn, hãy hoàn tất các bước trong những phần sau.

Định cấu hình ứng dụng

Trong tệp build.gradle cấp dự án, hãy thêm kho lưu trữ Maven của Google vào cả hai mục buildscriptallprojects:

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

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

Thêm phần phụ thuộc Dịch vụ Google Play dành cho API Block Store vào tệp bản dựng Gradle của mô-đun, thường là app/build.gradle:

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

Cách thức hoạt động

Block Store cho phép nhà phát triển lưu và khôi phục tối đa mảng 16 byte. Điều này cho phép bạn lưu thông tin quan trọng về phiên người dùng hiện tại và cho phép bạn linh hoạt lưu thông tin này theo bất kỳ cách nào bạn muốn. Dữ liệu này có thể được mã hóa hai đầu và cơ sở hạ tầng hỗ trợ Block Store được xây dựng dựa trên cơ sở hạ tầng Sao lưu và khôi phục.

Hướng dẫn này sẽ bao gồm trường hợp sử dụng lưu mã của người dùng vào Block Store. Các bước sau đây phác thảo cách thức hoạt động của ứng dụng sử dụng Block Store:

  1. Trong quy trình xác thực của ứng dụng hoặc bất cứ lúc nào sau đó, bạn có thể lưu trữ mã xác thực của người dùng vào Block Store để truy xuất sau này.
  2. Mã thông báo sẽ được lưu trữ trên máy và cũng có thể được sao lưu vào đám mây, được mã hoá hai đầu khi có thể.
  3. Dữ liệu được truyền khi người dùng bắt đầu một quy trình khôi phục trên thiết bị mới.
  4. Nếu người dùng khôi phục ứng dụng trong quy trình khôi phục, thì ứng dụng của bạn có thể truy xuất mã thông báo đã lưu từ Block Store trên thiết bị mới.

Đang lưu mã thông báo

Khi người dùng đăng nhập vào ứng dụng, bạn có thể lưu mã xác thực mà bạn tạo cho người dùng đó vào Block Store. Bạn có thể lưu trữ mã thông báo này bằng cách sử dụng giá trị cặp khóa duy nhất có tối đa 4kb cho mỗi mục nhập. Để lưu trữ mã thông báo, hãy gọi setBytes()setKey() trên thực thể của StoreBytesData.Builder để lưu thông tin xác thực của người dùng vào thiết bị nguồn. Sau khi bạn lưu mã thông báo bằng Block Store, mã thông báo sẽ được mã hoá và lưu trữ cục bộ trên thiết bị.

Mẫu sau đây cho biết cách lưu mã thông báo xác thực vào thiết bị cục bộ:

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

Sử dụng mã thông báo mặc định

Dữ liệu được lưu bằng StoreBytes không có khóa sẽ sử dụng khóa mặc định là 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)
    }

Đang truy xuất mã thông báo

Sau này, khi người dùng thực hiện quy trình khôi phục trên thiết bị mới, Dịch vụ Google Play sẽ xác minh người dùng trước tiên, sau đó truy xuất dữ liệu của bạn từ Block Store. Người dùng đã đồng ý khôi phục dữ liệu ứng dụng của bạn trong quy trình khôi phục, do đó không cần có sự đồng ý bổ sung. Khi người dùng mở ứng dụng, bạn có thể yêu cầu mã thông báo từ Block Store bằng cách gọi retrieveBytes(). Sau đó, mã thông báo được truy xuất có thể dùng để duy trì trạng thái đăng nhập của người dùng trên thiết bị mới.

Mẫu sau đây cho biết cách truy xuất nhiều mã thông báo dựa trên các khoá cụ thể.

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

Đang truy xuất tất cả mã thông báo.

Dưới đây là ví dụ về cách truy xuất tất cả mã thông báo đã lưu vào 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)
  }

Dưới đây là ví dụ về cách truy xuất khóa mặc định.

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)

Xóa mã thông báo

Có thể cần phải xóa mã thông báo khỏi BlockStore vì những lý do sau:

  • Người dùng trải qua quy trình đăng xuất của người dùng.
  • Mã thông báo đã bị thu hồi hoặc không hợp lệ.

Tương tự như truy xuất mã thông báo, bạn có thể chỉ định mã thông báo cần xóa bằng cách đặt một mảng khóa cần xóa.

Dưới đây là ví dụ về cách xóa một số khóa nhất định.

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)

Xóa tất cả mã thông báo

Ví dụ bên dưới sẽ xóa tất cả mã thông báo hiện được lưu vào 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")
  }

Mã hoá hai đầu

Để có thể sử dụng phương thức mã hoá hai đầu, thiết bị phải chạy Android 9 trở lên và người dùng phải đặt khoá màn hình (mã PIN, hình mở khoá hoặc mật khẩu) cho thiết bị. Bạn có thể xác minh xem mã hoá có trên thiết bị hay không bằng cách gọi isEndToEndEncryptionAvailable().

Mẫu sau đây cho biết cách xác minh xem tính năng mã hoá có dùng được trong quá trình sao lưu trên đám mây hay không:

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

Bật sao lưu đám mây

Để bật tính năng sao lưu trên đám mây, hãy thêm phương thức setShouldBackupToCloud() vào đối tượng StoreBytesData. Block Store sẽ sao lưu định kỳ vào đám mây các byte được lưu trữ khi setShouldBackupToCloud() được đặt là true.

Mẫu sau đây cho biết cách bật tính năng sao lưu trên đám mây chỉ khi tính năng sao lưu trên đám mây được mã hoá hai đầu:

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.")
          }
        }

Cách kiểm tra

Sử dụng các phương thức sau trong quá trình phát triển để kiểm thử quy trình khôi phục.

Gỡ cài đặt/cài đặt lại cùng một thiết bị

Nếu người dùng bật Dịch vụ sao lưu (bạn có thể kiểm tra tại Cài đặt > Google > Sao lưu), thì dữ liệu Block Store sẽ vẫn tồn tại trong quá trình gỡ cài đặt/cài đặt lại ứng dụng.

Bạn có thể làm theo các bước sau để kiểm tra:

  1. Tích hợp API BlockStore vào ứng dụng kiểm thử.
  2. Sử dụng ứng dụng thử nghiệm để gọi API BlockStore để lưu trữ dữ liệu của bạn.
  3. Gỡ cài đặt ứng dụng thử nghiệm rồi cài đặt lại ứng dụng trên cùng một thiết bị.
  4. Sử dụng ứng dụng thử nghiệm để gọi API BlockStore để truy xuất dữ liệu của bạn.
  5. Xác minh rằng các byte được truy xuất giống với những byte đã được lưu trữ trước khi gỡ cài đặt.

Thiết bị này đến thiết bị khác

Trong hầu hết trường hợp, bạn phải đặt lại thiết bị đích về trạng thái ban đầu. Sau đó, bạn có thể nhập quy trình khôi phục không dây cho Android hoặc khôi phục cáp của Google (đối với các thiết bị được hỗ trợ).

Khôi phục đám mây

  1. Tích hợp API Blockstore vào ứng dụng kiểm thử. Bạn cần gửi ứng dụng kiểm thử đến Cửa hàng Play.
  2. Trên thiết bị nguồn, hãy sử dụng ứng dụng kiểm thử để gọi API Blockstore để lưu trữ dữ liệu, trong khi ShouldBackUpToCloud được đặt thành đúng.
  3. Đối với các thiết bị O trở lên, bạn có thể kích hoạt theo cách thủ công một bản sao lưu trên đám mây qua Cửa hàng Play: hãy chuyển đến phần Cài đặt > Google > Sao lưu, nhấp vào nút “Sao lưu ngay”.
    1. Để xác minh rằng tính năng sao lưu trên đám mây của Block Store đã thành công, bạn có thể:
      1. Sau khi sao lưu xong, hãy tìm dòng nhật ký có thẻ “CloudSyncBpTkSvc”.
      2. Bạn sẽ thấy các dòng như sau: “......, CloudSyncBpTkSvc: sync kết quả: SUCCESS, ..., upload size: XXX byte ...”
    2. Sau khi sao lưu trên đám mây qua Block Store, thời gian "giải nhiệt" là 5 phút. Trong vòng 5 phút đó, việc nhấp vào nút "Sao lưu ngay" sẽ không kích hoạt một bản sao lưu đám mây Block Store khác.
  4. Đặt lại thiết bị đích về trạng thái ban đầu và hoàn tất quy trình khôi phục trên đám mây. Chọn để khôi phục ứng dụng kiểm thử trong quá trình khôi phục. Để biết thêm thông tin về quy trình khôi phục đám mây được hỗ trợ, hãy xem phần Quy trình khôi phục đám mây được hỗ trợ.
  5. Trên thiết bị mục tiêu, hãy sử dụng ứng dụng kiểm thử để gọi API Blockstore để truy xuất dữ liệu của bạn.
  6. Xác minh rằng các byte được truy xuất giống với những byte được lưu trữ trong thiết bị nguồn.

Yêu cầu về thiết bị

Mã hóa hai đầu

  • Tính năng mã hóa hai đầu được hỗ trợ trên các thiết bị chạy Android 9 (API 29) trở lên.
  • Thiết bị phải được đặt khóa màn hình bằng mã PIN, hình mở khóa hoặc mật khẩu thì mới có thể bật và mã hóa dữ liệu của người dùng một cách chính xác.

Quy trình Khôi phục thiết bị

Để khôi phục thiết bị, bạn cần có thiết bị nguồn và thiết bị mục tiêu. Đây sẽ là hai thiết bị chuyển dữ liệu.

Thiết bị nguồn phải chạy Android 6 (API 23) trở lên để sao lưu.

Nhắm mục tiêu thiết bị chạy Android 9 (API 29) trở lên để có khả năng khôi phục.

Bạn có thể xem thêm thông tin về quy trình khôi phục thiết bị qua thiết bị tại đây.

Quy trình Sao lưu và khôi phục trên đám mây

Bạn phải có thiết bị nguồn và thiết bị đích để sao lưu và khôi phục trên đám mây.

Thiết bị nguồn phải chạy Android 6 (API 23) trở lên để sao lưu.

Các thiết bị mục tiêu được hỗ trợ dựa trên nhà cung cấp của các thiết bị đó. Thiết bị Pixel có thể sử dụng tính năng này trên Android 9 (API 29) và tất cả các thiết bị khác phải chạy Android 12 (API 31) trở lên.