Nhóm trang web có liên quan: hướng dẫn cho nhà phát triển

Nhóm trang web có liên quan (RWS) là một cơ chế nền tảng web giúp trình duyệt hiểu được mối quan hệ giữa một tập hợp các miền. Nhờ đó, trình duyệt có thể đưa ra quyết định quan trọng để bật một số chức năng của trang web (chẳng hạn như có cho phép truy cập vào cookie trên nhiều trang web hay không) và hiển thị thông tin này cho người dùng.

Khi Chrome ngừng sử dụng cookie của bên thứ ba, mục tiêu của Chrome là duy trì các trường hợp sử dụng chính trên web, đồng thời cải thiện quyền riêng tư cho người dùng. Ví dụ: nhiều trang web dựa vào nhiều miền để cung cấp một trải nghiệm người dùng duy nhất. Các tổ chức có thể muốn duy trì các miền cấp cao nhất khác nhau cho nhiều trường hợp sử dụng như miền dành riêng cho từng quốc gia hoặc miền dịch vụ để lưu trữ hình ảnh hoặc video. Bộ trang web có liên quan cho phép các trang web chia sẻ dữ liệu giữa các miền, với các biện pháp kiểm soát cụ thể.

Nhìn chung, Bộ trang web có liên quan là một tập hợp các miền mà trong đó chỉ có một "đặt chính" và có thể có nhiều "thành viên nhóm".

Trong ví dụ sau, primary liệt kê miền chính, còn associatedSites liệt kê các miền đáp ứng yêu cầu của tập hợp con được liên kết.

{
  "primary": "https://primary.com",
  "associatedSites": ["https://associate1.com", "https://associate2.com", "https://associate3.com"]
}

Danh sách Bộ trang web có liên quan chính tắc là danh sách có thể xem công khai ở định dạng tệp JSON được lưu trữ trong kho lưu trữ GitHub về Bộ trang web có liên quan, đóng vai trò là nguồn dữ liệu cho tất cả các bộ. Chrome sử dụng tệp này để áp dụng cho hành vi của tệp.

Chỉ những người có quyền quản trị đối với một miền mới có thể tạo nhóm với miền đó. Người gửi bắt buộc phải khai báo mối quan hệ giữa từng "thành viên tập hợp" với "thành viên được đặt chính". Thành phần của tập hợp có thể bao gồm nhiều loại miền và phải thuộc một tập hợp con dựa trên trường hợp sử dụng.

Nếu ứng dụng của bạn phụ thuộc vào quyền truy cập vào cookie trên nhiều trang web (còn gọi là cookie của bên thứ ba) trên các trang web trong cùng một Bộ trang web có liên quan, thì bạn có thể sử dụng Storage Access API (SAA)requestStorageAccessFor API để yêu cầu quyền truy cập vào các cookie đó. Tuỳ thuộc vào tập hợp con của mỗi trang web, trình duyệt có thể xử lý yêu cầu theo cách khác nhau.

Để tìm hiểu thêm về quy trình và yêu cầu đối với việc gửi bộ, hãy xem nguyên tắc gửi. Các bộ nhóm mà bạn gửi sẽ trải qua nhiều bước kiểm tra về mặt kỹ thuật để xác thực nội dung được gửi.

Bộ trang web có liên quan rất phù hợp với các trường hợp mà tổ chức cần một dạng danh tính chung trên các trang web cấp cao nhất khác nhau.

Bộ trang web có liên quan có một số trường hợp sử dụng sau:

  • Tuỳ chỉnh quốc gia. Sử dụng các trang web đã bản địa hoá trong khi dựa vào cơ sở hạ tầng dùng chung (example.co.uk có thể dựa vào dịch vụ do example.ca lưu trữ).
  • Tích hợp miền dịch vụ. Sử dụng các miền dịch vụ mà người dùng không bao giờ tương tác trực tiếp nhưng cung cấp dịch vụ trên các trang web của cùng một tổ chức (example-CDN).
  • Phân tách nội dung người dùng. Việc truy cập dữ liệu trên những miền khác nhau sẽ tách biệt nội dung do người dùng tải lên với nội dung khác trên trang web vì lý do bảo mật, trong khi vẫn cho phép miền hộp cát truy cập vào các cookie xác thực (và các cookie khác). Nếu đang phân phát nội dung không hoạt động do người dùng tải lên, bạn cũng có thể lưu trữ nội dung đó một cách an toàn trên cùng một miền bằng cách làm theo các phương pháp hay nhất.
  • Nội dung đã xác thực được nhúng. Hỗ trợ nội dung được nhúng trên các tài sản liên kết (video, tài liệu hoặc tài nguyên chỉ dành cho người dùng đã đăng nhập trên trang web cấp cao nhất).
  • Đăng nhập. Hỗ trợ đăng nhập trên các tài sản liên kết. API FedCM cũng có thể phù hợp cho một số trường hợp sử dụng.
  • Số liệu phân tích. Triển khai số liệu phân tích và đo lường hành trình của người dùng trên các tài sản liên kết để cải thiện chất lượng dịch vụ.

Storage Access API

Hỗ trợ trình duyệt

  • 119
  • 85
  • 65
  • 11,1

Nguồn

API Truy cập bộ nhớ (SAA) cho phép nội dung được nhúng trên nhiều nguồn gốc truy cập vào bộ nhớ mà thường chỉ có quyền truy cập trong ngữ cảnh bên thứ nhất.

Tài nguyên được nhúng có thể dùng phương thức SAA để kiểm tra xem hiện có quyền truy cập vào bộ nhớ hay không, cũng như để yêu cầu quyền truy cập từ tác nhân người dùng.

Khi bạn chặn cookie của bên thứ ba nhưng Nhóm trang web có liên quan (RWS) đang bật, Chrome sẽ tự động cấp quyền trong ngữ cảnh nội bộ RWS và sẽ hiển thị lời nhắc cho người dùng. ("Ngữ cảnh nội bộ RWS" là một ngữ cảnh, chẳng hạn như iframe, có trang web được nhúng và trang web cấp cao nhất nằm trong cùng RWS.)

Kiểm tra và yêu cầu quyền truy cập vào bộ nhớ

Để kiểm tra xem hiện có quyền truy cập vào bộ nhớ hay không, các trang web được nhúng có thể dùng phương thức Document.hasStorageAccess().

Phương thức này trả về một lời hứa sẽ phân giải bằng giá trị boolean cho biết liệu tài liệu đã có quyền truy cập vào cookie hay chưa. Lời hứa cũng trả về giá trị true nếu iframe đó có cùng nguồn gốc với khung trên cùng.

Để yêu cầu quyền truy cập vào cookie trong ngữ cảnh nhiều trang web được nhúng, bạn có thể dùng Document.requestStorageAccess() (rSA).

API requestStorageAccess() sẽ được gọi từ bên trong iframe. iframe đó phải vừa nhận được tương tác của người dùng (cử chỉ của người dùng mà tất cả các trình duyệt đều yêu cầu), nhưng Chrome cũng yêu cầu tại một thời điểm nào đó trong 30 ngày qua, người dùng đã truy cập vào trang web sở hữu iframe đó và đã tương tác cụ thể với trang web đó dưới dạng tài liệu cấp cao nhất, chứ không phải trong iframe.

requestStorageAccess() trả về một lời hứa sẽ giải quyết nếu được cấp quyền truy cập vào bộ nhớ. Lời hứa sẽ bị từ chối, kèm theo lý do nếu quyền truy cập bị từ chối vì bất kỳ lý do gì.

requestStorageAccessFor trong Chrome

Hỗ trợ trình duyệt

  • 119
  • 119
  • x
  • x

Nguồn

Storage Access API (API Truy cập bộ nhớ) chỉ cho phép các trang web được nhúng yêu cầu quyền truy cập vào bộ nhớ từ bên trong các phần tử <iframe> đã nhận được sự tương tác của người dùng.

Điều này đặt ra những thách thức trong việc áp dụng Storage Access API cho các trang web cấp cao nhất sử dụng hình ảnh trên nhiều trang web hoặc thẻ tập lệnh cần cookie.

Để giải quyết vấn đề này, Chrome đã triển khai một cách để các trang web cấp cao nhất yêu cầu quyền truy cập vào bộ nhớ thay cho những nguồn gốc cụ thể thông qua Document.requestStorageAccessFor() (rSAFor).

 document.requestStorageAccessFor('https://target.site')

API requestStorageAccessFor() được gọi bằng một tài liệu cấp cao nhất. Tài liệu đó cũng phải vừa nhận được tương tác của người dùng. Tuy nhiên, không giống như requestStorageAccess(), Chrome không kiểm tra lượt tương tác trong tài liệu cấp cao nhất trong vòng 30 ngày qua vì người dùng đã có quyền truy cập vào trang.

Kiểm tra quyền truy cập bộ nhớ

Quyền truy cập vào một số tính năng của trình duyệt, như máy ảnh hoặc vị trí địa lý, dựa trên các quyền do người dùng cấp. API Quyền cung cấp cách kiểm tra trạng thái quyền truy cập vào một API – cho dù API đó đã được cấp, bị từ chối hay cần có một số hình thức tương tác của người dùng, chẳng hạn như nhấp vào lời nhắc hoặc tương tác với trang.

Bạn có thể truy vấn trạng thái quyền bằng navigator.permissions.query().

Để kiểm tra quyền truy cập vào bộ nhớ đối với ngữ cảnh hiện tại, bạn cần truyền vào chuỗi 'storage-access':

navigator.permissions.query({name: 'storage-access'})

Để kiểm tra quyền truy cập vào bộ nhớ của một nguồn gốc đã chỉ định, bạn cần truyền vào chuỗi 'top-level-storage-access':

navigator.permissions.query({name: 'top-level-storage-access', requestedOrigin: 'https://target.site'})

Xin lưu ý rằng để bảo vệ tính toàn vẹn của nguồn gốc được nhúng, thao tác này chỉ kiểm tra các quyền do tài liệu cấp cao nhất cấp bằng document.requestStorageAccessFor.

Tuỳ thuộc vào việc quyền có thể được cấp tự động hay yêu cầu cử chỉ của người dùng, ứng dụng sẽ trả về prompt hoặc granted.

Trên mỗi mô hình khung

Việc cấp quyền rSA được áp dụng theo từng khung hình. Các khoản cấp rSA và rSAFor được coi là các quyền riêng biệt.

Mỗi khung mới sẽ cần phải yêu cầu quyền truy cập riêng vào bộ nhớ và khung đó sẽ tự động được cấp quyền truy cập. Chỉ yêu cầu đầu tiên cần đến cử chỉ của người dùng, mọi yêu cầu tiếp theo do iframe khởi tạo (chẳng hạn như thao tác di chuyển hoặc tài nguyên phụ) sẽ không cần phải đợi cử chỉ của người dùng vì cử chỉ đó sẽ được yêu cầu ban đầu cấp cho phiên duyệt web.

Để làm mới, tải lại hoặc tạo lại iframe, bạn sẽ phải yêu cầu lại quyền truy cập.

Cookie phải chỉ định cả thuộc tính SameSite=NoneSecure vì rSA chỉ cung cấp quyền truy cập cho các cookie đã được đánh dấu để sử dụng trong ngữ cảnh nhiều trang web.

Những cookie có SameSite=Lax, SameSite=Strict hoặc không có thuộc tính SameSite chỉ dành cho bên thứ nhất và sẽ không bao giờ được chia sẻ trong bối cảnh nhiều trang web, bất kể quy trình tuân thủ chính sách bảo mật (rSA).

Bảo mật

Đối với rSAFor, các yêu cầu về tài nguyên phụ phải có tiêu đề Chia sẻ tài nguyên trên nhiều nguồn gốc (CORS) hoặc thuộc tính crossorigin trên tài nguyên để đảm bảo chọn sử dụng một cách rõ ràng.

Ví dụ về cấu hình triển khai

Yêu cầu quyền truy cập vào bộ nhớ qua một iframe được nhúng trên nhiều nguồn gốc

Sơ đồ cho thấy một trang web được nhúng trên trang top-level.site
Sử dụng requestStorageAccess() trong quá trình nhúng trên một trang web khác.

Kiểm tra xem bạn có quyền truy cập vào bộ nhớ hay không

Để kiểm tra xem bạn đã có quyền truy cập vào bộ nhớ hay chưa, hãy sử dụng document.hasStorageAccess().

Nếu lời hứa được giải quyết thành công, bạn có thể truy cập vào bộ nhớ trong ngữ cảnh nhiều trang web. Nếu kết quả phân giải là "false", bạn cần yêu cầu quyền truy cập vào bộ nhớ.

document.hasStorageAccess().then((hasAccess) => {
    if (hasAccess) {
      // You can access storage in this context
    } else {
      // You have to request storage access
    }
});

Yêu cầu quyền truy cập vào bộ nhớ

Nếu bạn cần yêu cầu quyền truy cập vào bộ nhớ, trước tiên, hãy kiểm tra quyền truy cập vào bộ nhớ navigator.permissions.query({name: 'storage-access'}) để xem việc đó có yêu cầu cử chỉ của người dùng hay không hoặc quyền đó có thể được cấp tự động hay không.

Nếu quyền là granted, bạn có thể gọi document.requestStorageAccess() và thao tác này sẽ thành công mà không cần cử chỉ của người dùng.

Nếu trạng thái quyền là prompt, bạn cần bắt đầu lệnh gọi document.requestStorageAccess() sau cử chỉ của người dùng, chẳng hạn như nhấp vào nút.

Ví dụ:

navigator.permissions.query({name: 'storage-access'}).then(res => {
  if (res.state === 'granted') {
    // Permission has already been granted
    // You can request storage access without any user gesture
    rSA();
  } else if (res.state === 'prompt') {
    // Requesting storage access requires user gesture
    // For example, clicking a button
    const btn = document.createElement("button");
    btn.textContent = "Grant access";
    btn.addEventListener('click', () => {
      // Request storage access
      rSA();
    });
    document.body.appendChild(btn);
  }
});

function rSA() {
  if ('requestStorageAccess' in document) {
    document.requestStorageAccess().then(
      (res) => {
        // Use storage access
      },
      (err) => {
        // Handle errors
      }
    );
  }
}

Các yêu cầu tiếp theo từ trong khung, các thao tác điều hướng hoặc tài nguyên phụ, sẽ tự động có quyền truy cập vào cookie trên nhiều trang web. hasStorageAccess() trả về cookie true và cookie trên nhiều trang web từ cùng một Bộ trang web có liên quan sẽ được gửi theo những yêu cầu đó mà không cần thêm lệnh gọi JavaScript nào.

Sơ đồ cho thấy requestStorageAccessFor() đang được dùng trên trang web cấp cao nhất chứ không phải trong trang web được nhúng
Sử dụng requestStorageAccessFor() trên trang web cấp cao nhất cho một nguồn gốc khác

Các trang web cấp cao nhất có thể dùng requestStorageAccessFor() để yêu cầu quyền truy cập vào bộ nhớ thay cho những nguồn gốc cụ thể.

hasStorageAccess() chỉ kiểm tra xem trang web gọi ứng dụng có quyền truy cập vào bộ nhớ hay không. Vì vậy, trang web cấp cao nhất có thể kiểm tra quyền đối với một nguồn gốc khác.

Để biết liệu người dùng có được nhắc hay không hoặc liệu quyền truy cập bộ nhớ đã được cấp cho nguồn gốc chỉ định hay chưa, hãy gọi navigator.permissions.query({name: 'top-level-storage-access', requestedOrigin: 'https://target.site'}).

Nếu quyền là granted, bạn có thể gọi document.requestStorageAccessFor('https://target.site'). Quá trình này sẽ thành công mà không cần cử chỉ của người dùng.

Nếu quyền là prompt, thì bạn cần nối lệnh gọi document.requestStorageAccessFor('https://target.site') phía sau cử chỉ của người dùng, chẳng hạn như nhấp vào nút.

Ví dụ:

navigator.permissions.query({name:'top-level-storage-access',requestedOrigin: 'https://target.site'}).then(res => {
  if (res.state === 'granted') {
    // Permission has already been granted
    // You can request storage access without any user gesture
    rSAFor();
  } else if (res.state === 'prompt') {
    // Requesting storage access requires user gesture
    // For example, clicking a button
    const btn = document.createElement("button");
    btn.textContent = "Grant access";
    btn.addEventListener('click', () => {
      // Request storage access
      rSAFor();
    });
    document.body.appendChild(btn);
  }
});

function rSAFor() {
  if ('requestStorageAccessFor' in document) {
    document.requestStorageAccessFor().then(
      (res) => {
        // Use storage access
      },
      (err) => {
        // Handle errors
      }
    );
  }
}

Sau khi lệnh gọi requestStorageAccessFor() thành công, các yêu cầu trên nhiều trang web sẽ bao gồm cookie nếu chúng bao gồm CORS hoặc thuộc tính nhiều nguồn gốc, vì vậy, các trang web nên đợi trước khi kích hoạt yêu cầu.

Các yêu cầu đó phải dùng tuỳ chọn credentials: 'include' và các tài nguyên phải có thuộc tính crossorigin="use-credentials".

function checkCookie() {
    fetch('https://related-website-sets.glitch.me/getcookies.json', {
        method: 'GET',
        credentials: 'include'
      })
      .then((response) => response.json())
      .then((json) => {
      // Do something
      });
  }

Cách thử nghiệm cục bộ

Điều kiện tiên quyết

Để thử nghiệm cục bộ Bộ trang web có liên quan, hãy sử dụng Chrome 119 trở lên chạy từ dòng lệnh và bật cờ Chrome test-third-party-cookie-phaseout.

Bật cờ Chrome

Để bật cờ Chrome cần thiết, hãy điều hướng đến chrome://flags#test-third-party-cookie-phaseout từ thanh địa chỉ và thay đổi cờ thành Enabled. Hãy nhớ khởi động lại trình duyệt sau khi thay đổi cờ.

Để chạy Chrome bằng Bộ trang web có liên quan được khai báo cục bộ, hãy tạo một đối tượng JSON chứa các URL là thành viên của một nhóm rồi truyền đối tượng đó đến --use-related-website-set.

Tìm hiểu thêm về cách chạy Chromium có cờ.

--use-related-website-set="{\"primary\": \"https://related-website-sets.glitch.me\", \"associatedSites\": [\"https://rws-member-1.glitch.me\"]}" \
https://related-website-sets.glitch.me/

Ví dụ:

Để bật Bộ trang web có liên quan cục bộ, bạn cần bật test-third-party-cookie-phaseout trong chrome://flags và chạy Chrome từ dòng lệnh với cờ --use-related-website-set chứa đối tượng JSON chứa các URL là thành viên của một tập hợp.

--use-related-website-set="{\"primary\": \"https://related-website-sets.glitch.me\", \"associatedSites\": [\"https://rws-member-1.glitch.me\"]}" \
https://related-website-sets.glitch.me/

Xác minh rằng bạn có quyền truy cập vào cookie trên nhiều trang web

Hãy gọi các API (rSA hoặc rSAFor) từ các trang web đang được kiểm tra và xác thực quyền truy cập vào cookie trên nhiều trang web.

Để khai báo mối quan hệ giữa các miền và chỉ định tập hợp con chứa các miền đó, hãy làm theo các bước sau:

  1. Xác định các miền có liên quan, trong đó bao gồm nhóm chínhnhóm thành viên. Đây sẽ là một phần của Bộ trang web có liên quan. Đồng thời, xác định loại tập hợp con nào thuộc về mỗi thành phần trong tập hợp.
  2. Đảm bảo các yêu cầu đối với việc thiết lập trình đơnbộ yêu cầu xác thực đã được thiết lập đúng cách.
  3. Khai báo Bộ trang web có liên quan ở đúng định dạng JSON.
  4. Gửi Bộ trang web có liên quan bằng cách tạo một yêu cầu kéo (PR) tới related_website_sets.JSON nơi Chrome sẽ lưu trữ danh sách Bộ trang web có liên quan chính tắc. (Bạn cần có tài khoản GitHub để tạo PR và bạn cần ký Thoả thuận cấp phép cho cộng tác viên (CLA) để đóng góp vào danh sách.)

Sau khi tạo PR, một loạt các bước kiểm tra sẽ diễn ra để xác thực rằng các yêu cầu ở bước 2 đã được áp dụng.

Nếu thành công, PR sẽ cho biết rằng các bước kiểm tra đã hoàn tất. Các PR đã được phê duyệt sẽ được hợp nhất theo hàng loạt vào danh sách Bộ trang web có liên quan chính tắc mỗi tuần một lần (12:00 trưa thứ Ba theo giờ Miền Đông).

Nếu bất kỳ bước kiểm tra nào không thực hiện được, thì người gửi sẽ nhận được thông báo lỗi PR trên GitHub. Người gửi có thể sửa các lỗi này và cập nhật nội dung PR và xin lưu ý rằng:

  • Khi PR không thành công, một thông báo lỗi sẽ cung cấp thêm thông tin về lý do có thể khiến việc gửi không thành công (ví dụ).
  • Tất cả các bước kiểm tra kỹ thuật chi phối việc gửi nhóm đều được thực hiện trên GitHub, và do đó, tất cả lỗi gửi do việc kiểm tra kỹ thuật đều có thể xem được trên GitHub.

Chính sách doanh nghiệp

Để đáp ứng nhu cầu của người dùng doanh nghiệp, Chrome có sẵn một số chính sách doanh nghiệp:

  • Những hệ thống không thể tích hợp với Bộ trang web có liên quan có thể tắt tính năng Bộ trang web có liên quan trong mọi phiên bản Chrome dành cho doanh nghiệp theo chính sách RelatedWebsiteSetsEnabled.
  • Một số hệ thống doanh nghiệp có các trang web chỉ dành cho nội bộ (chẳng hạn như mạng nội bộ) với miền có thể đăng ký khác với miền trong Bộ trang web có liên quan. Nếu cần coi các trang web này là một phần trong Bộ trang web có liên quan mà không tiết lộ công khai (vì miền có thể là thông tin mật), thì họ có thể bổ sung hoặc thay thế danh sách Bộ trang web có liên quan công khai bằng chính sách RelatedWebsiteSetsOverrides.

"Lời nhắc của người dùng" và "cử chỉ của người dùng"

"Lời nhắc của người dùng" và "cử chỉ của người dùng" là hai khái niệm khác nhau. Chrome sẽ không hiển thị lời nhắc cấp quyền cho người dùng đối với các trang web nằm trong cùng một Bộ trang web có liên quan, nhưng Chrome vẫn yêu cầu người dùng đã tương tác với trang. Trước khi cấp quyền, Chrome yêu cầu thực hiện cử chỉ của người dùng, còn gọi là "tương tác của người dùng" hoặc "kích hoạt của người dùng". Lý do là việc sử dụng Storage Access API bên ngoài ngữ cảnh của Nhóm trang web có liên quan (cụ thể là requestStorageAccess()) cũng đòi hỏi phải có cử chỉ của người dùng, do các nguyên tắc thiết kế nền tảng web.

Truy cập vào cookie hoặc bộ nhớ của các trang web khác

Bộ trang web có liên quan không hợp nhất bộ nhớ cho nhiều trang web: chỉ cho phép các lệnh gọi requestStorageAccess() dễ dàng hơn (không cần nhắc). Trang web có liên quan Đặt chỉ giúp người dùng dễ dàng sử dụng Storage Access API, nhưng không cho biết việc cần làm sau khi quyền truy cập được khôi phục. Nếu A và B là các trang web khác nhau trong cùng một Bộ trang web có liên quan và A nhúng B, thì B có thể gọi requestStorageAccess() và nhận quyền truy cập vào bộ nhớ của bên thứ nhất mà không cần nhắc người dùng. Bộ trang web có liên quan không thực hiện bất kỳ hoạt động giao tiếp nào trên nhiều trang web. Ví dụ: việc thiết lập Bộ trang web có liên quan sẽ không khiến các cookie thuộc B bắt đầu được gửi đến A. Nếu muốn chia sẻ dữ liệu đó, bạn sẽ phải tự chia sẻ, ví dụ: bằng cách gửi window.postMessage từ iframe B đến một khung.

Bộ trang web có liên quan không cho phép truy cập ngầm ẩn và không được phân vùng vào cookie mà không gọi bất kỳ API nào. Cookie trên nhiều trang web không được cung cấp theo mặc định trong tập hợp; Bộ trang web có liên quan chỉ cho phép các trang web trong tập hợp bỏ qua lời nhắc cấp quyền API Truy cập bộ nhớ. iframe phải gọi document.requestStorageAccess() nếu muốn truy cập vào cookie, hoặc trang cấp cao nhất có thể gọi document.requestStorageAccessFor().

Chia sẻ phản hồi

Việc gửi một tập hợp trên GitHub cũng như làm việc với Storage Access API và API requestStorageAccessFor là các cơ hội để bạn chia sẻ trải nghiệm về quy trình và mọi vấn đề bạn gặp phải.

Để tham gia thảo luận về Bộ trang web có liên quan, hãy làm như sau: