Di chuyển từ dịch vụ Người dùng App Engine sang Cloud Identity Platform (Mô-đun 21)

1. Tổng quan

Loạt lớp học lập trình về Trạm di chuyển không máy chủ (hướng dẫn thực hành theo tiến độ riêng) và video có liên quan nhằm giúp các nhà phát triển Google Cloud không máy chủ không hiện đại hoá các ứng dụng của họ bằng cách hướng dẫn họ thực hiện một hoặc nhiều quá trình di chuyển, chủ yếu là ngừng sử dụng các dịch vụ cũ. Việc này giúp ứng dụng của bạn dễ di chuyển hơn, đồng thời mang đến cho bạn nhiều lựa chọn và độ linh hoạt hơn, cho phép bạn tích hợp và truy cập vào nhiều sản phẩm của Cloud hơn, đồng thời dễ dàng nâng cấp lên bản phát hành ngôn ngữ mới hơn. Mặc dù ban đầu tập trung vào những người dùng Cloud sớm nhất, chủ yếu là các nhà phát triển App Engine (môi trường tiêu chuẩn), nhưng loạt bài này đủ rộng để bao gồm các nền tảng không máy chủ khác như Cloud FunctionsCloud Run hoặc ở những nơi khác nếu có.

Mục đích của lớp học lập trình này là hướng dẫn các nhà phát triển App Engine sử dụng Python 2 cách di chuyển từ dịch vụ/API người dùng App Engine sang Cloud Identity Platform (GCIP). Ngoài ra còn có quá trình di chuyển ngầm từ App Engine NDB sang Cloud NDB để truy cập Datastore (chủ yếu được đề cập trong Mô-đun di chuyển 2) cũng như nâng cấp lên Python 3.

Mô-đun 20 trình bày cách thêm việc sử dụng API Người dùng vào ứng dụng mẫu của Mô-đun 1. Trong học phần này, bạn sẽ sử dụng ứng dụng hoàn chỉnh của Mô-đun 20 và di chuyển mức sử dụng ứng dụng đó sang Cloud Identity Platform.

Bạn sẽ tìm hiểu cách

  • Thay thế việc sử dụng dịch vụ Người dùng App Engine bằng Nền tảng Cloud Identity
  • Thay thế việc sử dụng App Engine NDB bằng Cloud NDB (xem thêm Mô-đun 2)
  • Thiết lập nhiều nhà cung cấp danh tính để xác thực bằng tính năng Xác thực Firebase
  • Sử dụng Cloud Resource Manager API để lấy thông tin IAM của dự án
  • Sử dụng SDK quản trị của Firebase để nhận thông tin người dùng
  • Chuyển ứng dụng mẫu sang Python 3

Bạn cần có

Khảo sát

Bạn sẽ sử dụng hướng dẫn này như thế nào?

Chỉ đọc qua Đọc và hoàn thành bài tập

Bạn đánh giá thế nào về trải nghiệm sử dụng Python?

Người mới tập Trung cấp Thành thạo

Bạn đánh giá thế nào về trải nghiệm sử dụng các dịch vụ của Google Cloud?

Người mới tập Trung cấp Thành thạo

2. Thông tin khái quát

Dịch vụ người dùng App Engine là hệ thống xác thực người dùng để các ứng dụng App Engine sử dụng. Dịch vụ này cung cấp tính năng Đăng nhập bằng Google làm nhà cung cấp danh tính, cung cấp đường liên kết đăng nhập và đăng xuất thuận tiện để sử dụng trong ứng dụng, đồng thời hỗ trợ khái niệm người dùng quản trị và chức năng chỉ dành cho quản trị viên. Để cải thiện khả năng di chuyển của ứng dụng, bạn nên chuyển từ các dịch vụ đi kèm của App Engine cũ sang các dịch vụ độc lập trên Cloud, chẳng hạn như từ dịch vụ Người dùng sang Cloud Identity Platform và nhiều dịch vụ khác.

Nền tảng danh tính dựa trên tính năng Xác thực Firebase và bổ sung một số tính năng dành cho doanh nghiệp như xác thực đa yếu tố, OIDC (OpenID Connect) và Hỗ trợ đăng nhập một lần (SSO) dựa trên SAML, nhiều khách hàng thuê, đạt 99, 95% theo thoả thuận mức độ cung cấp dịch vụ (SLA) và nhiều lợi ích khác. Những khác biệt này cũng được làm nổi bật trên trang so sánh sản phẩm Xác thực Firebase và Nền tảng nhận dạng. Cả hai sản phẩm đều có nhiều tính năng hơn đáng kể so với chức năng do dịch vụ Người dùng cung cấp.

Lớp học lập trình Mô-đun 21 này minh hoạ việc chuyển đổi phương thức xác thực người dùng của ứng dụng từ Dịch vụ người dùng sang các tính năng Nền tảng nhận dạng mà phản ánh gần nhất chức năng được minh hoạ trong Mô-đun 20. Mô-đun 21 cũng trình bày việc di chuyển từ App Engine NDB sang Cloud NDB để truy cập vào Datastore, lặp lại quá trình di chuyển Mô-đun 2.

Mặc dù mã Mô-đun 20 là "được quảng cáo" dưới dạng ứng dụng mẫu Python 2, bản thân nguồn tương thích với Python 2 và 3, và nó vẫn như vậy ngay cả sau khi chuyển sang Identity Platform (và Cloud NDB) ở đây trong Mô-đun 21. Bạn có thể tiếp tục sử dụng dịch vụ Người dùng trong khi nâng cấp lên Python 3 vì việc di chuyển sang Nền tảng nhận dạng là không bắt buộc. Xem và video Lớp học lập trình về Mô-đun 17 để tìm hiểu cách tiếp tục sử dụng các dịch vụ đi kèm trong khi nâng cấp lên môi trường thời gian chạy thế hệ thứ 2 như Python 3.

Hướng dẫn này bao gồm các bước sau:

  1. Thiết lập/Chuẩn bị
  2. Cập nhật cấu hình
  3. Sửa đổi mã xử lý ứng dụng

3. Thiết lập/Chuẩn bị

Phần này giải thích cách:

  1. Thiết lập dự án trên Cloud
  2. Tải ứng dụng mẫu cơ sở
  3. (Triển khai lại) và xác thực ứng dụng cơ sở
  4. Bật các API/dịch vụ Google Cloud mới

Các bước này giúp đảm bảo bạn đang bắt đầu với đoạn mã đang hoạt động, đã sẵn sàng để di chuyển sang các dịch vụ Cloud độc lập.

1. Thiết lập dự án

Nếu bạn đã hoàn thành lớp học lập trình Mô-đun 20, hãy sử dụng lại chính dự án (và mã) đó. Ngoài ra, hãy tạo một dự án hoàn toàn mới hoặc sử dụng lại một dự án hiện có khác. Hãy đảm bảo dự án này có một tài khoản thanh toán đang hoạt động và có một ứng dụng App Engine đã bật. Tìm mã dự án của bạn để sử dụng trong lớp học lập trình này và sử dụng mã này bất cứ khi nào bạn gặp biến PROJ_ID.

2. Tải ứng dụng mẫu cơ sở

Một trong những điều kiện tiên quyết là ứng dụng App Engine Mô-đun 20 đang hoạt động, vì vậy, hãy hoàn thành lớp học lập trình của ứng dụng (đề xuất; đường liên kết ở trên) hoặc sao chép mã Mô-đun 20 từ kho lưu trữ. Cho dù bạn sử dụng tài khoản của bạn hay của chúng tôi, đây là nơi chúng tôi sẽ bắt đầu ("START"). Lớp học lập trình này sẽ hướng dẫn bạn thực hiện quá trình di chuyển, kết thúc bằng mã tương tự như những gì trong thư mục kho lưu trữ Mô-đun 21 ("Finish").

Sao chép thư mục kho lưu trữ Mô-đun 20. Kết quả này sẽ có dạng như dưới đây và có thể có thư mục lib nếu bạn thực hiện lớp học lập trình Mô-đun 20:

$ ls
README.md               appengine_config.py     templates
app.yaml                main.py                 requirements.txt

3. (Triển khai lại) và xác thực ứng dụng cơ sở

Thực thi các bước sau để triển khai ứng dụng Mô-đun 20:

  1. Xoá thư mục lib (nếu có) rồi chạy pip install -t lib -r requirements.txt để điền lại thư mục đó. Có thể bạn sẽ phải sử dụng pip2 nếu đã cài đặt cả Python 2 và 3.
  2. Hãy đảm bảo bạn đã cài đặtkhởi chạy công cụ dòng lệnh gcloud, đồng thời xem lại việc sử dụng công cụ này.
  3. Nếu bạn không muốn nhập PROJ_ID với mỗi lệnh gcloud được đưa ra, trước tiên, hãy thiết lập dự án trên đám mây với gcloud config set project PROJ_ID.
  4. Triển khai ứng dụng mẫu bằng gcloud app deploy
  5. Kiểm tra để đảm bảo ứng dụng chạy như mong đợi mà không gặp lỗi. Nếu bạn đã hoàn thành lớp học lập trình Mô-đun 20, ứng dụng sẽ hiển thị thông tin đăng nhập của người dùng (email của người dùng, có thể là "huy hiệu quản trị viên" và nút đăng nhập/đăng xuất) ở trên cùng cùng với những lượt truy cập gần đây nhất (minh hoạ bên dưới).

907e64c19ef964f8.pngS

Việc đăng nhập với tư cách là người dùng thông thường sẽ làm cho địa chỉ email của người dùng hiển thị, còn nút "Đăng nhập" nút chuyển thành "Đăng xuất" nút:

ad7b59916b69a035.png

Khi đăng nhập với tư cách là người dùng quản trị, địa chỉ email của người dùng đó sẽ xuất hiện cùng với "(quản trị viên)" bên cạnh bài đăng đó:

867bcb3334149e4.png.

4. Bật API/dịch vụ Google Cloud mới

Giới thiệu

Ứng dụng Mô-đun 20 sử dụng API Người dùng và NDB của App Engine, các dịch vụ đi kèm không yêu cầu thiết lập bổ sung, nhưng các dịch vụ đám mây độc lập thì có. Ứng dụng được cập nhật sẽ sử dụng cả Cloud Identity Platform và Kho dữ liệu đám mây (thông qua thư viện ứng dụng Cloud NDB). Ngoài ra, nhu cầu xác định người dùng quản trị của App Engine cũng yêu cầu sử dụng Cloud Resource Manager API.

Chi phí

  • App Engine và Cloud Datastore có "Luôn miễn phí" và miễn là bạn không phải trả các giới hạn đó, bạn sẽ không phải chịu phí khi hoàn thành hướng dẫn này. Ngoài ra, hãy xem trang giá của App Enginetrang giá của Cloud Datastore để biết thêm chi tiết.
  • Việc sử dụng Cloud Identity Platform sẽ được tính phí tuỳ thuộc vào số lượng người dùng hoạt động hằng tháng (MAU) hoặc số lượt xác thực; một số phiên bản của "miễn phí" có sẵn cho từng mô hình sử dụng. Hãy xem trang giá để biết thêm chi tiết. Ngoài ra, mặc dù App Engine và Cloud Datastore đều yêu cầu thanh toán, nhưng việc sử dụng GCIP là không bắt buộc phải bật tính năng thanh toán, miễn là bạn không vượt quá hạn mức hằng ngày không dùng công cụ. Vì vậy, hãy cân nhắc việc này đối với những dự án Cloud không liên quan đến Cloud API/dịch vụ yêu cầu thanh toán.
  • Bạn có thể sử dụng miễn phí Cloud Resource Manager API đối với hầu hết các phần trên trang giá của dịch vụ này.

Người dùng bật Cloud API từ bảng điều khiển Cloud hoặc từ dòng lệnh (thông qua lệnh gcloud, một phần của SDK đám mây), tuỳ thuộc vào lựa chọn ưu tiên của bạn. Hãy bắt đầu với Cloud Datastore và Cloud Resource Manager API.

Trên Cloud Console

Truy cập vào trang Thư viện của Trình quản lý API (để tìm đúng dự án) trong Cloud Console và tìm một API bằng thanh tìm kiếm. c7a740304e9d35b.png

Bật các API này:

Tìm và nhấp vào nút Bật cho từng API riêng biệt – bạn có thể được nhắc cung cấp thông tin thanh toán. Ví dụ: đây là trang cho Resource Manager API (API Trình quản lý tài nguyên):

fc7bd8f4c49d12e5.png

Nút này sẽ chuyển thành Quản lý khi được bật (thường sau vài giây):

8eca12d6cc7b45b0.pngS

Bật Cloud Datastore theo cách tương tự:

83811599b110e46b.png.

Từ dòng lệnh

Mặc dù thông tin trực quan để bật các API từ bảng điều khiển, một số người lại thích dòng lệnh hơn. Không chỉ vậy, bạn còn có thể bật nhiều API cùng một lúc. Phát lệnh này để bật cả API Cloud Datastore lẫn Cloud Resource Manager rồi chờ thao tác hoàn tất, như minh hoạ dưới đây:

$ gcloud services enable cloudresourcemanager.googleapis.com datastore.googleapis.com
Operation "operations/acat.p2-aaa-bbb-ccc-ddd-eee-ffffff" finished successfully.

Bạn có thể được nhắc cung cấp thông tin thanh toán.

Các "URL" đối với mỗi API dùng trong lệnh trên được gọi là tên dịch vụ API. Bạn có thể tìm thấy các tên này ở cuối trang thư viện cho từng API. Nếu muốn bật các Cloud API khác cho ứng dụng của mình, bạn có thể tìm tên dịch vụ tương ứng trên trang API tương ứng. Lệnh này liệt kê tất cả tên dịch vụ của những API mà bạn có thể bật:

gcloud services list --available --filter="name:googleapis.com".

Bất kể là trong bảng điều khiển Cloud hay trên dòng lệnh, sau khi bạn hoàn tất các bước trên, giờ đây, mẫu của chúng tôi sẽ có thể truy cập vào các API đó. Các bước tiếp theo là bật Cloud Identity Platform và thay đổi mã cần thiết.

Bật và thiết lập Cloud Identity Platform (chỉ bảng điều khiển Cloud)

Cloud Identity Platform là một dịch vụ trên Marketplace vì dịch vụ này kết nối hoặc phụ thuộc vào một tài nguyên bên ngoài Google Cloud, ví dụ: dịch vụ Xác thực Firebase. Hiện tại, bạn chỉ có thể bật các dịch vụ của Marketplace trên bảng điều khiển Cloud. Hãy làm theo các bước sau:

  1. Truy cập vào trang Cloud Identity Platform trong Cloud Marketplace rồi nhấp vào nút Bật ở đó. Nâng cấp từ tính năng Xác thực Firebase nếu được nhắc. Làm như vậy sẽ mở khoá các tính năng bổ sung, chẳng hạn như các tính năng được mô tả trước đó trong phần Nền. Dưới đây là trang Thị trường, trong đó có nút Bật được làm nổi bật: 28475f1c9b29de69.pngS
  2. Sau khi bật Nền tảng danh tính, có thể bạn sẽ tự động được đưa đến trang Nhà cung cấp danh tính. Nếu chưa, hãy sử dụng đường liên kết thuận tiện này để truy cập vào trang đó. fc2d92d42a5d1dd7.png
  3. Bật nhà cung cấp dịch vụ Xác thực của Google. Nếu chưa thiết lập nhà cung cấp nào, hãy nhấp vào Thêm nhà cung cấp rồi chọn Google. Khi quay lại màn hình này, mục Google sẽ được bật. Google là nhà cung cấp dịch vụ xác thực duy nhất mà chúng tôi sử dụng trong hướng dẫn này để phản ánh dịch vụ Người dùng App Engine dưới dạng một dịch vụ Đăng nhập bằng Google gọn nhẹ. Trong các ứng dụng của riêng mình, bạn có thể bật các nhà cung cấp dịch vụ xác thực khác.
  4. Khi bạn đã chọn và thiết lập Google và các nhà cung cấp dịch vụ xác thực mà bạn muốn khác, hãy nhấp vào Chi tiết thiết lập ứng dụng. Trong cửa sổ hộp thoại đảm bảo, hãy sao chép apiKeyauthDomain trong đối tượng config trên thẻ Web, lưu cả hai ở nơi an toàn. Tại sao không sao chép toàn bộ? Đoạn mã trong hộp thoại này được cố định giá trị trong mã và ghi ngày tháng, vì vậy, chỉ cần lưu các bit quan trọng nhất và sử dụng chúng trong mã của chúng tôi với mức sử dụng Xác thực Firebase đồng thời hơn. Sau khi bạn sao chép các giá trị và lưu chúng ở nơi an toàn, hãy nhấp vào nút Đóng, hoàn tất tất cả các bước thiết lập cần thiết. bbb09dcdd9be538e.png

4. Cập nhật cấu hình

Các cập nhật về cấu hình bao gồm thay đổi nhiều tệp cấu hình cũng như tạo phiên bản tương đương của App Engine nhưng trong hệ sinh thái Cloud Identity Platform.

appengine_config.py

  • Nếu nâng cấp lên Python 3, hãy xoá appengine_config.py
  • Nếu bạn có kế hoạch hiện đại hoá thành Identity Platform (Nền tảng nhận dạng) nhưng vẫn sử dụng Python 2, vui lòng không xoá tệp này. Thay vào đó, chúng tôi sẽ cập nhật mã này sau trong quá trình điều chỉnh cho phiên bản cũ của Python 2.

requirements.txt

Tệp requirements.txt của Mô-đun 20 chỉ được liệt kê Flask. Đối với Mô-đun 21, hãy thêm các gói sau:

Lúc này, nội dung của requirements.txt sẽ có dạng như sau:

flask
google-auth
google-cloud-ndb
google-cloud-resource-manager
firebase-admin

app.yaml

  • Nâng cấp lên Python 3 có nghĩa là đơn giản hoá tệp app.yaml. Xoá mọi thứ ngoại trừ lệnh thời gian chạy và đặt lệnh đó thành một phiên bản Python 3 hiện được hỗ trợ. Ví dụ này hiện sử dụng phiên bản 3.10.
  • Nếu vẫn tiếp tục sử dụng Python 2, bạn chưa cần làm gì cả.

TRƯỚC KHI:

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

Ứng dụng mẫu Module 20 không có trình xử lý tệp tĩnh. Nếu các ứng dụng của bạn có thay đổi, hãy giữ nguyên chúng. Bạn có thể xoá tất cả trình xử lý tập lệnh của mình nếu muốn hoặc chỉ cần để chúng ở đó để tham khảo, miễn là bạn thay đổi tên người dùng của các trình xử lý đó thành auto, như mô tả trong hướng dẫn di chuyển app.yaml. Với những thay đổi này, app.yaml đã cập nhật cho Python 3 được đơn giản hoá thành:

SAU KHI:

runtime: python310

Các nội dung cập nhật khác về cấu hình

Dù vẫn ở trên Python 2 hay chuyển sang Python 3, nếu bạn có thư mục lib, hãy xoá thư mục đó.

5. Sửa đổi mã xử lý ứng dụng

Phần này giới thiệu các bản cập nhật cho tệp ứng dụng chính, main.py, thay thế việc sử dụng dịch vụ Người dùng App Engine bằng Cloud Identity Platform. Sau khi cập nhật ứng dụng chính, bạn sẽ cập nhật mẫu dành cho web (templates/index.html).

Cập nhật lệnh nhập và khởi chạy

Hãy làm theo các bước bên dưới để cập nhật các lệnh nhập và khởi tạo tài nguyên ứng dụng:

  1. Để nhập, hãy thay thế App Engine NDB bằng Cloud NDB.
  2. Cùng với Cloud NDB, hãy nhập cả Cloud Resource Manager (Trình quản lý tài nguyên đám mây).
  3. Nền tảng nhận dạng dựa trên tính năng Xác thực Firebase, vì vậy, hãy nhập SDK quản trị của Firebase.
  4. Cloud API đòi hỏi bạn phải dùng một ứng dụng API, vì vậy hãy khởi động ứng dụng này cho Cloud NDB ngay bên dưới khi khởi chạy Flask.

Mặc dù gói Cloud Resource Manager (Trình quản lý tài nguyên đám mây) được nhập tại đây, nhưng chúng ta sẽ sử dụng gói này ở giai đoạn sau trong quá trình khởi chạy ứng dụng. Dưới đây là các mục nhập và khởi tạo từ Mô-đun 20, theo sau là cách các phần này sẽ trông như thế nào sau khi triển khai các thay đổi ở trên:

TRƯỚC KHI:

from flask import Flask, render_template, request
from google.appengine.api import users
from google.appengine.ext import ndb

app = Flask(__name__)

SAU KHI:

from flask import Flask, render_template, request
from google.auth import default
from google.cloud import ndb, resourcemanager
from firebase_admin import auth, initialize_app

# initialize Flask and Cloud NDB API client
app = Flask(__name__)
ds_client = ndb.Client()

Dịch vụ hỗ trợ dành cho người dùng Quản trị trên App Engine

Sau đây là 2 thành phần bạn cần thêm vào ứng dụng để hỗ trợ việc nhận dạng người dùng quản trị:

  • _get_gae_admins() — nhóm người dùng quản trị tổng hợp; đã gọi một lần và đã lưu
  • is_admin() – kiểm tra xem người dùng đã đăng nhập có phải là người dùng quản trị hay không; được gọi trên bất kỳ thông tin đăng nhập nào của người dùng

Hàm hiệu dụng _get_gae_admins() sẽ gọi Resource Manager API (API Trình quản lý tài nguyên) để tìm nạp Cloud IAM allow-policy hiện tại. Chính sách cho phép xác định và thực thi những vai trò được cấp cho người dùng chính (người dùng là người, tài khoản dịch vụ, v.v.). Quá trình thiết lập bao gồm:

  • Đang tìm nạp mã dự án trên Cloud (PROJ_ID)
  • Tạo ứng dụng API Trình quản lý tài nguyên (rm_client)
  • Tạo nhóm vai trò Quản trị viên App Engine (chỉ đọc) (_TARGETS)

Trình quản lý tài nguyên yêu cầu mã dự án trên Cloud, vì vậy, hãy nhập google.auth.default() và gọi hàm đó để lấy mã dự án. Lệnh gọi đó có một tham số trông giống như một URL nhưng là phạm vi quyền của OAuth2. Khi chạy các ứng dụng trên đám mây, chẳng hạn như trên ứng dụng VM hoặc App Engine của Compute Engine, một tài khoản dịch vụ mặc định sẽ được cung cấp và có các đặc quyền rộng. Theo phương pháp hay nhất về đặc quyền tối thiểu, bạn nên tạo tài khoản dịch vụ do người dùng quản lý của riêng mình.

Đối với các lệnh gọi API, bạn nên thêm nữa giảm phạm vi ứng dụng xuống mức tối thiểu cần thiết để hoạt động đúng cách. Lệnh gọi API Trình quản lý tài nguyên mà chúng ta đang thực hiện là get_iam_policy() cần một trong các phạm vi sau để hoạt động:

  • https://www.googleapis.com/auth/cloud-platform
  • https://www.googleapis.com/auth/cloud-platform.read-only
  • https://www.googleapis.com/auth/cloudplatformprojects
  • https://www.googleapis.com/auth/cloudplatformprojects.readonly

Ứng dụng mẫu chỉ cần quyền chỉ có thể đọc đối với chính sách cho phép. Công cụ này không sửa đổi chính sách cũng như không cần quyền truy cập vào toàn bộ dự án. Điều đó có nghĩa là ứng dụng không cần bất kỳ quyền nào trong số 3 quyền đầu tiên. Bước cuối cùng là tất cả những gì cần thiết và đó là việc chúng ta đang triển khai cho ứng dụng mẫu.

Phần nội dung chính của hàm này tạo ra một nhóm trống gồm những người dùng quản trị (admins), tìm nạp allow_policy qua get_iam_policy() và lặp lại thông qua tất cả các liên kết của hàm để tìm cụ thể vai trò Quản trị viên của App Engine:

  • roles/viewer
  • roles/editor
  • roles/owner
  • roles/appengine.appAdmin

Đối với mỗi vai trò mục tiêu được tìm thấy, hệ thống sẽ tổng hợp những người dùng thuộc vai trò đó, thêm họ vào nhóm người dùng quản trị chung. Tùy chọn này kết thúc bằng cách trả về tất cả người dùng quản trị được tìm thấy và lưu vào bộ nhớ đệm dưới dạng hằng số (_ADMINS) trong suốt thời gian hoạt động của phiên bản App Engine này. Chúng tôi sẽ sớm thấy cuộc gọi đó.

Thêm định nghĩa hàm _get_gae_admins() sau đây vào main.py ngay bên dưới bản sao ứng dụng Cloud NDB API (ds_client):

def _get_gae_admins():
    'return set of App Engine admins'
    # setup constants for calling Cloud Resource Manager API
    _, PROJ_ID = default(  # Application Default Credentials and project ID
            ['https://www.googleapis.com/auth/cloudplatformprojects.readonly'])
    rm_client = resourcemanager.ProjectsClient()
    _TARGETS = frozenset((     # App Engine admin roles
            'roles/viewer',
            'roles/editor',
            'roles/owner',
            'roles/appengine.appAdmin',
    ))

    # collate users who are members of at least one GAE admin role (_TARGETS)
    admins = set()                      # set of all App Engine admins
    allow_policy = rm_client.get_iam_policy(resource='projects/%s' % PROJ_ID)
    for b in allow_policy.bindings:     # bindings in IAM allow-policy
        if b.role in _TARGETS:          # only look at GAE admin roles
            admins.update(user.split(':', 1).pop() for user in b.members)
    return admins

Khi người dùng đăng nhập vào ứng dụng, những điều sau sẽ xảy ra:

  1. Google sẽ kiểm tra nhanh dựa trên mẫu trên web sau khi người dùng đăng nhập vào Firebase.
  2. Khi trạng thái xác thực thay đổi trong mẫu, lệnh gọi fetch() kiểu Ajax được thực hiện đến /is_admin, trong đó trình xử lý là hàm tiếp theo, is_admin().
  3. Mã thông báo của mã nhận dạng Firebase được chuyển vào phần nội dung POST cho is_admin(). Thao tác này sẽ lấy mã này khỏi các tiêu đề và gọi SDK quản trị Firebase để xác thực mã đó. Nếu đó là người dùng hợp lệ, hãy trích xuất địa chỉ email của họ và kiểm tra xem đó có phải là người dùng quản trị hay không.
  4. Sau đó, kết quả Boolean được trả về mẫu là 200 thành công.

Thêm is_admin() vào main.py ngay sau _get_gae_admins():

@app.route('/is_admin', methods=['POST'])
def is_admin():
    'check if user (via their Firebase ID token) is GAE admin (POST) handler'
    id_token = request.headers.get('Authorization')
    email = auth.verify_id_token(id_token).get('email')
    return {'admin': email in _ADMINS}, 200

Tất cả mã từ cả hai hàm này đều phải sao chép chức năng có sẵn từ dịch vụ Người dùng, cụ thể là hàm is_current_user_admin(). Lệnh gọi hàm này trong Mô-đun 20 đã thực hiện toàn bộ công việc khó khăn, không giống như Mô-đun 21 khi chúng ta triển khai giải pháp thay thế. Tin vui là ứng dụng không còn phụ thuộc vào một dịch vụ chỉ dành cho App Engine, tức là bạn có thể chuyển ứng dụng của mình sang Cloud Run hoặc các dịch vụ khác. Ngoài ra, bạn cũng có thể thay đổi định nghĩa về "người dùng quản trị" cho ứng dụng của riêng bạn chỉ bằng cách chuyển sang các vai trò mong muốn trong _TARGETS, còn dịch vụ Người dùng được mã hoá cứng cho các vai trò quản trị viên App Engine.

Khởi chạy tính năng Xác thực Firebase và lưu người dùng quản trị vào bộ nhớ đệm của App Engine

Chúng ta có thể đã khởi chạy tính năng Xác thực Firebase ở trên cùng gần vị trí mà ứng dụng Flask được khởi chạy và ứng dụng Cloud NDB API được tạo, nhưng không cần phải làm cho đến khi tất cả mã quản trị đã được xác định, chính là vị trí hiện tại của chúng ta. Tương tự, giờ đây _get_gae_admins() đã được định nghĩa, hãy gọi hàm này để lưu danh sách người dùng quản trị vào bộ nhớ đệm.

Thêm các dòng sau ngay bên dưới phần nội dung hàm is_admin():

# initialize Firebase and fetch set of App Engine admins
initialize_app()
_ADMINS = _get_gae_admins()

Xem thông tin cập nhật về mô hình dữ liệu

Mô hình dữ liệu Visit không thay đổi. Để truy cập vào kho dữ liệu, bạn phải sử dụng rõ ràng trình quản lý ngữ cảnh ứng dụng Cloud NDB API, ds_client.context(). Trong đoạn mã, điều này có nghĩa là bạn gói các lệnh gọi Datastore trong cả store_visit()fetch_visits() bên trong các khối with Python. Bản cập nhật này giống với Mô-đun 2. Thực hiện các thay đổi như sau:

TRƯỚC KHI:

class Visit(ndb.Model):
    'Visit entity registers visitor IP address & timestamp'
    visitor   = ndb.StringProperty()
    timestamp = ndb.DateTimeProperty(auto_now_add=True)

def store_visit(remote_addr, user_agent):
    'create new Visit entity in Datastore'
    Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()

def fetch_visits(limit):
    'get most recent visits'
    return Visit.query().order(-Visit.timestamp).fetch(limit)

SAU KHI:

class Visit(ndb.Model):
    'Visit entity registers visitor IP address & timestamp'
    visitor   = ndb.StringProperty()
    timestamp = ndb.DateTimeProperty(auto_now_add=True)

def store_visit(remote_addr, user_agent):
    'create new Visit entity in Datastore'
    with ds_client.context():
        Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()

def fetch_visits(limit):
    'get most recent visits'
    with ds_client.context():
        return Visit.query().order(-Visit.timestamp).fetch(limit)

Di chuyển logic đăng nhập của người dùng sang mẫu web

Dịch vụ Người dùng App Engine là phía máy chủ trong khi Dịch vụ Xác thực Firebase và Cloud Identity Platform chủ yếu ở phía máy khách. Do đó, phần lớn mã quản lý người dùng trong ứng dụng Mô-đun 20 chuyển sang mẫu web của Mô-đun 21.

Trong main.py, ngữ cảnh web truyền 5 phần dữ liệu thiết yếu đến mẫu, 4 phần dữ liệu đầu tiên được liệt kê gắn liền với hoạt động quản lý người dùng và khác nhau tuỳ thuộc vào việc người dùng đã đăng nhập hay chưa:

  • who – email của người dùng nếu đã đăng nhập hoặc người dùng nếu không
  • admin – huy hiệu (quản trị viên) nếu người dùng đã đăng nhập là quản trị viên
  • sign – hiện nút Đăng nhập hoặc Đăng xuất
  • link — liên kết đăng nhập hoặc đăng xuất khi nhấp vào nút
  • visits — lượt truy cập gần đây nhất

TRƯỚC KHI:

@app.route('/')
def root():
    'main application (GET) handler'
    store_visit(request.remote_addr, request.user_agent)
    visits = fetch_visits(10)

    # put together users context for web template
    user = users.get_current_user()
    context = {  # logged in
        'who':   user.nickname(),
        'admin': '(admin)' if users.is_current_user_admin() else '',
        'sign':  'Logout',
        'link':  '/_ah/logout?continue=%s://%s/' % (
                      request.environ['wsgi.url_scheme'],
                      request.environ['HTTP_HOST'],
                  ),  # alternative to users.create_logout_url()
    } if user else {  # not logged in
        'who':   'user',
        'admin': '',
        'sign':  'Login',
        'link':  users.create_login_url('/'),
    }

    # add visits to context and render template
    context['visits'] = visits  # display whether logged in or not
    return render_template('index.html', **context)

Toàn bộ hoạt động quản lý người dùng sẽ chuyển sang mẫu web, vì vậy chúng ta chỉ còn lại các lượt truy cập, đưa trình xử lý chính trở lại giao diện mà chúng ta đã có trong ứng dụng Mô-đun 1:

SAU KHI:

@app.route('/')
def root():
    'main application (GET) handler'
    store_visit(request.remote_addr, request.user_agent)
    visits = fetch_visits(10)
    return render_template('index.html', visits=visits)

Cập nhật mẫu web

Tất cả nội dung cập nhật từ phần trước trong mẫu trông như thế nào? Chúng tôi chủ yếu di chuyển hoạt động quản lý người dùng từ ứng dụng sang tính năng Xác thực Firebase chạy trong mẫu và một phần cổng của tất cả mã mà chúng tôi đã chuyển sang JavaScript. Chúng tôi nhận thấy main.py thu hẹp lại khá nhiều, vì vậy, dự kiến mức tăng trưởng tương tự vào templates/index.html.

TRƯỚC KHI:

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
</head>
<body>
<p>
Welcome, {{ who }} <code>{{ admin }}</code>
<button id="logbtn">{{ sign }}</button>
</p><hr>

<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
    <li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>

<script>
document.getElementById("logbtn").onclick = () => {
    window.location.href = '{{ link }}';
};
</script>
</body>
</html>

Thay thế toàn bộ mẫu web bằng nội dung dưới đây:

SAU KHI:

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>

<script type="module">
// import Firebase module attributes
import {
        initializeApp
} from "https://www.gstatic.com/firebasejs/9.10.0/firebase-app.js";
import {
        GoogleAuthProvider,
        getAuth,
        onAuthStateChanged,
        signInWithPopup,
        signOut
} from "https://www.gstatic.com/firebasejs/9.10.0/firebase-auth.js";

// Firebase config:
// 1a. Go to: console.cloud.google.com/customer-identity/providers
// 1b. May be prompted to enable GCIP and upgrade from Firebase
// 2. Click: "Application Setup Details" button
// 3. Copy: 'apiKey' and 'authDomain' from 'config' variable
var firebaseConfig = {
        apiKey: "YOUR_API_KEY",
        authDomain: "YOUR_AUTH_DOMAIN",
};

// initialize Firebase app & auth components
initializeApp(firebaseConfig);
var auth = getAuth();
var provider = new GoogleAuthProvider();
//provider.setCustomParameters({prompt: 'select_account'});

// define login and logout button functions
function login() {
    signInWithPopup(auth, provider);
};

function logout() {
    signOut(auth);
};

// check if admin & switch to logout button on login; reset everything on logout
onAuthStateChanged(auth, async (user) => {
    if (user && user != null) {
        var email = user.email;
        who.innerHTML = email;
        logbtn.onclick = logout;
        logbtn.innerHTML = "Logout";
        var idToken = await user.getIdToken();
        var rsp = await fetch("/is_admin", {
                method: "POST",
                headers: {Authorization: idToken}
        });
        var data = await rsp.json();
        if (data.admin) {
            admin.style.display = "inline";
        }
    } else {
        who.innerHTML = "user";
        admin.style.display = "none";
        logbtn.onclick = login;
        logbtn.innerHTML = "Login";
    }
});
</script>
</head>

<body>
<p>
Welcome, <span id="who"></span> <span id="admin"><code>(admin)</code></span>
<button id="logbtn"></button>
</p><hr>

<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
    <li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>

<script>
var who    = document.getElementById("who");
var admin  = document.getElementById("admin");
var logbtn = document.getElementById("logbtn");
</script>
</body>
</html>

Có nhiều thành phần trong phần nội dung HTML này, vì vậy, hãy lấy từng thành phần một.

Nhập dữ liệu Firebase

Khi vẫn ở trong tiêu đề của tài liệu HTML, sau khi qua tiêu đề trang, hãy nhập các thành phần Firebase cần thiết. Các thành phần của Firebase hiện được chia thành nhiều mô-đun để tăng tính hiệu quả. Mã để khởi chạy Firebase được nhập từ mô-đun ứng dụng Firebase chính trong khi các chức năng quản lý tính năng xác thực Firebase, Google với vai trò là nhà cung cấp thông tin xác thực, đăng nhập và đăng xuất, cũng như thay đổi trạng thái xác thực "lệnh gọi lại" đều được nhập từ mô-đun Xác thực Firebase:

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>

<script type="module">
// import Firebase module attributes
import {
        initializeApp
} from "https://www.gstatic.com/firebasejs/9.10.0/firebase-app.js";
import {
        GoogleAuthProvider,
        getAuth,
        onAuthStateChanged,
        signInWithPopup,
        signOut
} from "https://www.gstatic.com/firebasejs/9.10.0/firebase-auth.js";

Cấu hình Firebase

Trước đó trong phần thiết lập Nền tảng nhận dạng của hướng dẫn này, bạn đã lưu apiKeyauthDomain từ hộp thoại Chi tiết thiết lập ứng dụng. Thêm các giá trị đó vào biến firebaseConfig trong phần tiếp theo. Chúng tôi có một đường liên kết đến phần hướng dẫn chi tiết hơn trong phần bình luận:

// Firebase config:
// 1a. Go to: console.cloud.google.com/customer-identity/providers
// 1b. May be prompted to enable GCIP and upgrade from Firebase
// 2. Click: "Application Setup Details" button
// 3. Copy: 'apiKey' and 'authDomain' from 'config' variable
var firebaseConfig = {
        apiKey: "YOUR_API_KEY",
        authDomain: "YOUR_AUTH_DOMAIN",
};

Khởi chạy Firebase

Phần tiếp theo sẽ khởi chạy Firebase bằng thông tin cấu hình này.

// initialize Firebase app & auth components
initializeApp(firebaseConfig);
var auth = getAuth();
var provider = new GoogleAuthProvider();
//provider.setCustomParameters({prompt: 'select_account'});

Điều này giúp bạn có thể sử dụng Google làm nhà cung cấp dịch vụ xác thực và cung cấp tùy chọn có chú thích để hiển thị bộ chọn tài khoản ngay cả khi chỉ có một Tài khoản Google được đăng ký trong phiên trình duyệt của bạn. Nói cách khác, khi có nhiều tài khoản, bạn sẽ nhìn thấy "công cụ chọn tài khoản" này như mong đợi: a38369389b7c4c7e.png Tuy nhiên, nếu chỉ có một người dùng trong phiên, thì quá trình đăng nhập sẽ tự động hoàn tất mà không cần bất kỳ sự tương tác nào của người dùng. (Cửa sổ bật lên xuất hiện rồi biến mất.) Bạn có thể buộc hộp thoại bộ chọn tài khoản hiển thị cho một người dùng (thay vì đăng nhập ngay vào ứng dụng) bằng cách huỷ nhận xét về dòng thông số tuỳ chỉnh. Nếu bạn bật chế độ này, thì ngay cả những người dùng một người dùng cũng sẽ thấy bộ chọn tài khoản: b75624cb68d94557.png

Chức năng đăng nhập và đăng xuất

Các dòng mã tiếp theo tạo thành hàm để nhấp vào nút đăng nhập hoặc đăng xuất:

// define login and logout button functions
function login() {
    signInWithPopup(auth, provider);
};

function logout() {
    signOut(auth);
};

Thao tác đăng nhập và đăng xuất

Phần chính cuối cùng trong khối <script> này là hàm được gọi cho mọi thay đổi xác thực (đăng nhập hoặc đăng xuất).

// check if admin & switch to logout button on login; reset everything on logout
onAuthStateChanged(auth, async (user) => {
    if (user && user != null) {
        var email = user.email;
        who.innerHTML = email;
        logbtn.onclick = logout;
        logbtn.innerHTML = "Logout";
        var idToken = await user.getIdToken();
        var rsp = await fetch("/is_admin", {
                method: "POST",
                headers: {Authorization: idToken}
        });
        var data = await rsp.json();
        if (data.admin) {
            admin.style.display = "inline";
        }
    } else {
        who.innerHTML = "user";
        admin.style.display = "none";
        logbtn.onclick = login;
        logbtn.innerHTML = "Login";
    }
});
</script>
</head>

Mã trong Mô-đun 20 xác định xem có gửi một "người dùng đã đăng nhập" hay không bối cảnh mẫu so với "người dùng đã đăng xuất" ngữ cảnh được chuyển đổi ở đây. Điều kiện ở trên cùng sẽ tạo ra true nếu người dùng đăng nhập thành công, kích hoạt các hành động sau:

  1. Địa chỉ email của người dùng được đặt để hiển thị.
  2. Nút Đăng nhập sẽ chuyển thành Đăng xuất.
  3. Lệnh gọi kiểu Ajax đến /is_admin được thực hiện để xác định xem có hiển thị huy hiệu người dùng quản trị (admin) hay không.

Khi người dùng đăng xuất, mệnh đề else sẽ được thực thi để đặt lại tất cả thông tin người dùng:

  1. Tên người dùng được đặt là user
  2. Mọi huy hiệu của quản trị viên đã bị xoá
  3. Nút Đăng xuất đã chuyển lại thành Đăng nhập

Biến mẫu

Sau khi phần tiêu đề kết thúc, phần nội dung chính bắt đầu bằng các biến mẫu được thay thế bằng các phần tử HTML có thể thay đổi khi cần:

  1. Tên người dùng được hiển thị
  2. Huy hiệu quản trị viên (admin) (nếu có)
  3. Nút Đăng nhập hoặc Đăng xuất
<body>
<p>
Welcome, <span id="who"></span> <span id="admin"><code>(admin)</code></span>
<button id="logbtn"></button>
</p><hr>

Lượt truy cập gần đây nhất và các biến phần tử HTML

Mã lượt truy cập gần đây nhất không thay đổi và khối <script> cuối cùng sẽ đặt biến cho các phần tử HTML thay đổi đối với hoạt động đăng nhập và đăng xuất được liệt kê ở trên:

<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
    <li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>

<script>
var who    = document.getElementById("who");
var admin  = document.getElementById("admin");
var logbtn = document.getElementById("logbtn");
</script>
</body>
</html>

Đến đây, bạn không còn phải thực hiện những thay đổi cần thiết trong mẫu web và ứng dụng để chuyển từ App Engine NDB và API Người dùng sang Cloud NDB và Nền tảng nhận dạng cũng như nâng cấp lên Python 3. Chúc mừng bạn đã đến với ứng dụng mẫu Mô-đun 21 mới! Bạn có thể xem phiên bản của chúng tôi trong thư mục kho lưu trữ Mô-đun 21b.

Phần tiếp theo của lớp học lập trình này là không bắt buộc (*) và chỉ dành cho những người dùng có ứng dụng phải tiếp tục sử dụng Python 2. Hướng dẫn bạn thực hiện các bước cần thiết để có được ứng dụng Python 2 Module 21 đang hoạt động.

6. *Điều chỉnh cho phiên bản cũ của Python 2

Phần không bắt buộc này dành cho các nhà phát triển di chuyển Nền tảng nhận dạng nhưng phải tiếp tục chạy trên môi trường thời gian chạy Python 2. Nếu bạn không lo ngại về điều này, hãy bỏ qua phần này.

Để tạo một phiên bản Python 2 hoạt động được của ứng dụng Mô-đun 21, bạn cần có:

  1. Yêu cầu về thời gian chạy: Các tệp cấu hình hỗ trợ Python 2 và bắt buộc thực hiện các thay đổi trong ứng dụng chính để tránh việc không tương thích với Python 3
  2. Thay đổi nhỏ về thư viện: Python 2 không được dùng nữa trước khi một số tính năng bắt buộc được thêm vào thư viện ứng dụng Resource Manager (Trình quản lý tài nguyên). Do đó, bạn cần có một cách khác để truy cập vào chức năng còn thiếu đó.

Hãy thực hiện các bước đó ngay bây giờ, bắt đầu với cấu hình.

Khôi phục appengine_config.py

Ở phần trước của hướng dẫn này, bạn đã được hướng dẫn cách xoá appengine_config.py vì thời gian chạy Python 3 App Engine không sử dụng mã này. Đối với Python 2, bạn không chỉ phải giữ nguyên API này mà còn cần cập nhật appengine_config.py của Mô-đun 20 để hỗ trợ việc sử dụng thư viện bên thứ ba tích hợp sẵn, cụ thể là grpciosetuptools. Bạn cần phải có các gói này bất cứ khi nào ứng dụng App Engine của bạn sử dụng các thư viện ứng dụng Cloud như các thư viện dành cho Cloud NDB và Cloud Resource Manager.

Bạn sẽ thêm các gói đó vào app.yaml trong giây lát, nhưng để ứng dụng của bạn truy cập vào các gói đó, hàm pkg_resources.working_set.add_entry() từ setuptools phải được gọi. Điều này cho phép các thư viện bên thứ ba đã sao chép (tự nhóm hoặc cung cấp) được cài đặt trong thư mục lib có thể giao tiếp với các thư viện tích hợp.

Triển khai các nội dung cập nhật sau đây cho tệp appengine_config.py để thực hiện những thay đổi này:

TRƯỚC KHI:

from google.appengine.ext import vendor

# Set PATH to your libraries folder.
PATH = 'lib'
# Add libraries installed in the PATH folder.
vendor.add(PATH)

Chỉ riêng mã này thì không đủ để hỗ trợ việc sử dụng setuptoolsgrpcio. Cần thêm một vài dòng nữa, vì vậy, hãy cập nhật appengine_config.py để thành phần này như sau:

SAU KHI:

import pkg_resources
from google.appengine.ext import vendor

# Set PATH to your libraries folder.
PATH = 'lib'
# Add libraries installed in the PATH folder.
vendor.add(PATH)
# Add libraries to pkg_resources working set to find the distribution.
pkg_resources.working_set.add_entry(PATH)

Bạn có thể xem thêm thông tin về những thay đổi cần thiết để hỗ trợ thư viện ứng dụng Cloud trong tài liệu về việc di chuyển các dịch vụ đi kèm.

app.yaml

Tương tự như appengine_config.py, tệp app.yaml phải được khôi phục về một tệp hỗ trợ Python 2. Hãy bắt đầu từ app.yaml của Mô-đun 20 gốc:

TRƯỚC KHI:

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

Ngoài setuptoolsgrpcio như đã đề cập trước đó, còn có một phần phụ thuộc (không liên quan rõ ràng đến việc di chuyển Nền tảng danh tính) yêu cầu sử dụng thư viện ứng dụng Cloud Storagethư viện đó cần có một gói khác tích hợp của bên thứ ba là ssl. Thêm cả ba thành phần vào phần libraries mới, chọn "mới nhất" phiên bản có sẵn của các gói đó, lên app.yaml:

SAU KHI:

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

libraries:
- name: grpcio
  version: latest
- name: setuptools
  version: latest
- name: ssl
  version: latest

requirements.txt

Đối với Mô-đun 21, chúng tôi đã thêm tính năng Xác thực của Google, Cloud NDB, Cloud Resource ManagerSDK quản trị của Firebase vào Python 3 requirements.txt. Tình huống của Python 2 phức tạp hơn:

  • API Trình quản lý tài nguyên cung cấp chức năng chính sách cho phép cần thiết cho ứng dụng mẫu. Rất tiếc, tính năng hỗ trợ này chưa có trong phiên bản Python 2 cuối cùng của thư viện ứng dụng Cloud Resource Manager. (Chỉ có trong phiên bản Python 3.)
  • Do đó, bạn cần phải có một cách khác để truy cập vào tính năng này từ API. Giải pháp là sử dụng thư viện ứng dụng Google API cấp thấp hơn để giao tiếp với API. Để chuyển sang thư viện ứng dụng này, hãy thay thế google-cloud-resource-manager bằng gói google-api-python-client ở cấp thấp hơn.
  • Python 2 đã ngừng hoạt động, biểu đồ phần phụ thuộc hỗ trợ Mô-đun 21 yêu cầu khoá một số gói nhất định với các phiên bản cụ thể. Một số gói phải được gọi ngay cả khi các gói đó không được chỉ định trong app.yaml trong Python 3.

TRƯỚC KHI:

flask

Bắt đầu từ requirements.txt của Mô-đun 20, hãy cập nhật thành như sau để ứng dụng Mô-đun 21 hoạt động:

SAU KHI:

grpcio==1.0.0
protobuf<3.18.0
six>=1.13.0
flask
google-gax<0.13.0
google-api-core==1.31.1
google-api-python-client<=1.11.0
google-auth<2.0dev
google-cloud-datastore==1.15.3
google-cloud-firestore==1.9.0
google-cloud-ndb
google-cloud-pubsub==1.7.0
firebase-admin

Số gói và số phiên bản sẽ được cập nhật trong kho lưu trữ khi các phần phụ thuộc thay đổi, nhưng app.yaml này là đủ cho một ứng dụng đang hoạt động tại thời điểm viết bài này.

Các nội dung cập nhật khác về cấu hình

Nếu bạn chưa xoá thư mục lib trước đó trong lớp học lập trình này, hãy xoá ngay. Với requirements.txt mới cập nhật, hãy phát hành lệnh quen thuộc này để cài đặt các yêu cầu sau vào lib:

pip install -t lib -r requirements.txt  # or pip2

Nếu đã cài đặt cả Python 2 và 3 trên hệ thống phát triển, bạn có thể phải sử dụng pip2 thay vì pip.

Sửa đổi mã xử lý ứng dụng

May mắn thay, hầu hết thay đổi bắt buộc đều nằm trong các tệp cấu hình. Thay đổi duy nhất cần thiết trong mã xử lý ứng dụng là một bản cập nhật nhỏ để sử dụng thư viện ứng dụng Google API cấp thấp hơn thay vì thư viện ứng dụng Trình quản lý tài nguyên để truy cập API. Bạn không cần phải cập nhật mẫu web templates/index.html.

Cập nhật các lệnh nhập và khởi chạy

Thay thế thư viện ứng dụng Trình quản lý tài nguyên (google.cloud.resourcemanager) bằng thư viện ứng dụng API của Google (googleapiclient.discovery), như minh hoạ bên dưới:

TRƯỚC KHI:

from flask import Flask, render_template, request
from google.auth import default
from google.cloud import ndb, resourcemanager
from firebase_admin import auth, initialize_app

SAU KHI:

from flask import Flask, render_template, request
from google.auth import default
from google.cloud import ndb
from googleapiclient import discovery
from firebase_admin import auth, initialize_app

Dịch vụ hỗ trợ dành cho người dùng quản trị của App Engine

Cần một vài thay đổi trong _get_gae_admins() để hỗ trợ việc sử dụng thư viện ứng dụng cấp thấp hơn. Trước tiên, hãy thảo luận về những gì sẽ thay đổi, sau đó cung cấp cho bạn tất cả các mã để cập nhật.

Mã Python 2 yêu cầu sử dụng cả thông tin xác thực và mã dự án được google.auth.default() trả về. Thông tin đăng nhập không được sử dụng trong Python 3, vì vậy, thông tin này được gán cho biến giả gạch dưới chung ( _ ). Vì cần có phiên bản Python 2, hãy thay đổi dấu gạch dưới thành CREDS. Ngoài ra, thay vì tạo ứng dụng API Trình quản lý tài nguyên, bạn sẽ tạo điểm cuối dịch vụ API, tương tự như khái niệm ứng dụng API, vì vậy, chúng ta sẽ giữ nguyên tên biến (rm_client). Có một điểm khác biệt là việc tạo thực thể cho điểm cuối của dịch vụ cần có thông tin đăng nhập (CREDS).

Những thay đổi này được phản ánh trong mã bên dưới:

TRƯỚC KHI:

_, PROJ_ID = default(  # Application Default Credentials and project ID
        ['https://www.googleapis.com/auth/cloudplatformprojects.readonly'])
rm_client = resourcemanager.ProjectsClient()

SAU KHI:

CREDS, PROJ_ID = default(  # Application Default Credentials and project ID
        ['https://www.googleapis.com/auth/cloud-platform'])
rm_client = discovery.build('cloudresourcemanager', 'v1', credentials=CREDS)

Điểm khác biệt khác là thư viện ứng dụng Trình quản lý tài nguyên trả về các đối tượng chính sách cho phép sử dụng ký hiệu thuộc tính dấu chấm trong khi thư viện ứng dụng cấp thấp trả về từ điển Python trong đó sử dụng dấu ngoặc vuông ( [ ] ), ví dụ: sử dụng binding.role cho thư viện ứng dụng Trình quản lý tài nguyên so với binding['role'] cho thư viện cấp thấp hơn. Mã số trước cũng sử dụng "underscore_dựa" so với thư viện cấp thấp hơn thích "CamelCased" cùng với một cách hơi khác để truyền tham số API.

Những khác biệt về cách sử dụng được thể hiện dưới đây:

TRƯỚC KHI:

allow_policy = rm_client.get_iam_policy(resource='projects/%s' % PROJ_ID)
for b in allow_policy.bindings:     # bindings in IAM allow-policy
    if b.role in _TARGETS:          # only look at GAE admin roles
        admins.update(user.split(':', 1).pop() for user in b.members)

SAU KHI:

allow_policy = rm_client.projects().getIamPolicy(resource=PROJ_ID).execute()
for b in allow_policy['bindings']:  # bindings in IAM allow-policy
    if b['role'] in _TARGETS:       # only look at GAE admin roles
        admins.update(user.split(':', 1).pop() for user in b['members'])

Tập hợp tất cả những thay đổi này lại với nhau, thay thế _get_gae_admins() Python 3 bằng phiên bản Python 2 tương đương sau:

def _get_gae_admins():
    'return set of App Engine admins'
    # setup constants for calling Cloud Resource Manager API
    CREDS, PROJ_ID = default(  # Application Default Credentials and project ID
            ['https://www.googleapis.com/auth/cloud-platform'])
    rm_client = discovery.build('cloudresourcemanager', 'v1', credentials=CREDS)
    _TARGETS = frozenset((     # App Engine admin roles
            'roles/viewer',
            'roles/editor',
            'roles/owner',
            'roles/appengine.appAdmin',
    ))

    # collate users who are members of at least one GAE admin role (_TARGETS)
    admins = set()                      # set of all App Engine admins
    allow_policy = rm_client.projects().getIamPolicy(resource=PROJ_ID).execute()
    for b in allow_policy['bindings']:  # bindings in IAM allow-policy
        if b['role'] in _TARGETS:       # only look at GAE admin roles
            admins.update(user.split(':', 1).pop() for user in b['members'])
    return admins

Hàm is_admin() không yêu cầu cập nhật vì hàm này dựa vào _get_gae_admins() đã được cập nhật.

Điều này kết thúc những thay đổi cần thiết để điều chỉnh cho phiên bản cũ ứng dụng Python 3 Module 21 sang Python 2. Chúc mừng bạn đã đến với ứng dụng mẫu Mô-đun 21 mà bạn cập nhật! Bạn sẽ tìm thấy tất cả mã trong thư mục kho lưu trữ Mô-đun 21a.

7. Tóm tắt/Dọn dẹp

Bước cuối cùng trong lớp học lập trình này là đảm bảo các tài khoản chính (người dùng hoặc tài khoản dịch vụ) đang chạy ứng dụng này có quyền thích hợp để làm vậy, sau đó triển khai ứng dụng để xác nhận rằng ứng dụng hoạt động như mong đợi và các thay đổi sẽ được phản ánh trong kết quả.

Có thể đọc chính sách cho phép IAM

Trước đó, chúng tôi đã giới thiệu với bạn 4 vai trò bắt buộc để được công nhận là người dùng quản trị của App Engine, nhưng bây giờ có 1/5 vai trò mà bạn cần nắm bắt:

  • roles/viewer
  • roles/editor
  • roles/owner
  • roles/appengine.appAdmin
  • roles/resourcemanager.projectIamAdmin (đối với những người quản lý có quyền truy cập vào chính sách cho phép quản lý danh tính và quyền truy cập (IAM))

Vai trò roles/resourcemanager.projectIamAdmin cho phép người dùng chính xác định xem người dùng cuối có phải là thành viên của bất kỳ vai trò quản trị viên nào trên App Engine hay không. Nếu bạn không có gói thành viên trong roles/resourcemanager.projectIamAdmin, các lệnh gọi đến Cloud Resource Manager API để nhận chính sách cho phép sẽ không thực hiện được.

Bạn không cần làm gì ở đây vì ứng dụng của bạn sẽ chạy trong tài khoản dịch vụ mặc định của App Engine. Tài khoản này sẽ tự động được cấp tư cách thành viên trong vai trò này. Ngay cả khi sử dụng tài khoản dịch vụ mặc định trong giai đoạn phát triển, bạn vẫn nên tạo và sử dụng tài khoản dịch vụ do người dùng quản lý có các quyền tối thiểu cần thiết để ứng dụng của bạn hoạt động đúng cách. Để cấp tư cách thành viên cho một tài khoản dịch vụ đó, hãy chạy lệnh sau:

$ gcloud projects add-iam-policy-binding PROJ_ID --member="serviceAccount:USR_MGD_SVC_ACCT@PROJ_ID.iam.gserviceaccount.com" --role=roles/resourcemanager.projectIamAdmin

PROJ_ID là mã dự án trên Cloud và USR_MGD_SVC_ACCT@PROJ_ID.iam.gserviceaccount.com là tài khoản dịch vụ do người dùng quản lý mà bạn tạo cho ứng dụng của mình. Lệnh này sẽ đưa ra chính sách IAM đã cập nhật cho dự án của bạn, qua đó bạn có thể xác nhận tài khoản dịch vụ có tư cách thành viên trong roles/resourcemanager.projectIamAdmin. Để biết thêm thông tin, hãy xem tài liệu tham khảo. Lặp lại, bạn không cần đưa ra lệnh đó trong lớp học lập trình này, nhưng hãy lưu lệnh này làm tài liệu tham khảo để hiện đại hoá ứng dụng của riêng bạn.

Triển khai và xác minh ứng dụng

Tải ứng dụng lên đám mây bằng lệnh gcloud app deploy tiêu chuẩn. Sau khi triển khai, bạn sẽ thấy chức năng gần giống với ứng dụng Mô-đun 20, ngoại trừ việc bạn đã thay thế thành công dịch vụ Người dùng App Engine bằng Cloud Identity Platform (và Xác thực Firebase) để quản lý người dùng:

3a83ae745121d70.pngS

Một điểm khác biệt so với Mô-đun 20 là việc nhấp vào kết quả Đăng nhập trong một cửa sổ bật lên thay vì một lệnh chuyển hướng. Như vậy, bạn sẽ thấy một số ảnh chụp màn hình ở bên dưới. Tuy nhiên, giống như Mô-đun 20, hành vi hơi khác một chút tuỳ thuộc vào số lượng Tài khoản Google đã được đăng ký với trình duyệt.

Nếu chưa có người dùng nào đăng ký với trình duyệt hoặc có một người dùng chưa đăng nhập, một cửa sổ bật lên chung để đăng nhập bằng Google sẽ xuất hiện:

8437f5f3d489a942.pngS

Nếu một người dùng được đăng ký bằng trình duyệt của bạn nhưng lại đăng nhập ở nơi khác thì sẽ không có hộp thoại nào xuất hiện (hoặc hộp thoại bật lên và đóng ngay lập tức) và ứng dụng sẽ chuyển sang trạng thái đã đăng nhập (hiển thị email người dùng và nút Đăng xuất).

Một số nhà phát triển có thể muốn cung cấp công cụ chọn tài khoản, ngay cả đối với một người dùng duy nhất:

b75624cb68d94557.png

Để triển khai việc này, hãy huỷ nhận xét về dòng provider.setCustomParameters({prompt: 'select_account'}); trong mẫu web như mô tả ở trên.

Nếu có nhiều người dùng, hộp thoại bộ chọn tài khoản sẽ bật lên (xem bên dưới). Nếu chưa đăng nhập, người dùng sẽ được nhắc. Nếu bạn đã đăng nhập, cửa sổ bật lên sẽ biến mất và ứng dụng sẽ chuyển sang trạng thái đã đăng nhập.

c454455b6020d5e4.png

Trạng thái đã đăng nhập của Mô-đun 21 trông giống với giao diện người dùng của Mô-đun 20:

49ebe4dcc1eff11f.png.

Điều này cũng đúng đối với trường hợp người dùng quản trị đã đăng nhập:

44302f35b39856eb.png.

Không giống như Mô-đun 21, Mô-đun 20 luôn truy cập logic cho nội dung mẫu web từ ứng dụng (mã phía máy chủ). Một lỗi của Mô-đun 20 là một lượt truy cập được đăng ký khi người dùng cuối truy cập vào ứng dụng lần đầu tiên và một lượt truy cập khác được đăng ký khi người dùng đăng nhập.

Đối với Mô-đun 21, logic đăng nhập chỉ diễn ra trong mẫu web (mã phía máy khách). Bạn không cần phải di chuyển phía máy chủ để xác định nội dung cần hiển thị. Lệnh gọi duy nhất được thực hiện đến máy chủ là kiểm tra người dùng quản trị sau khi người dùng cuối đăng nhập. Điều này có nghĩa là hoạt động đăng nhập và đăng xuất sẽ không đăng ký thêm lượt truy cập, do đó, danh sách lượt truy cập gần đây nhất luôn cố định cho các thao tác quản lý người dùng. Lưu ý rằng các ảnh chụp màn hình ở trên hiển thị cùng một nhóm 4 lượt truy cập trên nhiều lần đăng nhập của người dùng.

Ảnh chụp màn hình của Mô-đun 20 minh hoạ "lỗi truy cập hai lần" ở đầu lớp học lập trình này. Các nhật ký lượt truy cập riêng biệt được hiển thị cho mỗi thao tác đăng nhập hoặc đăng xuất. Kiểm tra dấu thời gian của lượt truy cập gần đây nhất cho từng ảnh chụp màn hình hiển thị thứ tự thời gian.

Dọn dẹp

Giải pháp chung

Nếu bạn đã hoàn tất, chúng tôi khuyên bạn nên tắt ứng dụng App Engine để tránh phát sinh thanh toán. Tuy nhiên, nếu bạn muốn kiểm tra hoặc thử nghiệm thêm, nền tảng App Engine có hạn mức miễn phí, và bạn sẽ không bị tính phí, miễn là bạn không vượt quá cấp sử dụng đó. Đó là cho dịch vụ điện toán nhưng bạn cũng có thể bị tính phí cho các dịch vụ có liên quan của App Engine, vì vậy hãy xem trang giá của dịch vụ này để biết thêm thông tin. Nếu quá trình di chuyển này liên quan đến các dịch vụ khác trên Google Cloud, thì những dịch vụ đó sẽ được tính phí riêng. Trong cả hai trường hợp (nếu có), hãy xem phần "Dành riêng cho lớp học lập trình này" phần dưới đây.

Để công bố đầy đủ thông tin, việc triển khai cho một nền tảng điện toán không máy chủ của Google Cloud như App Engine sẽ làm phát sinh chi phí bản dựng và bộ nhớ thấp. Cloud Build cũng có hạn mức miễn phí riêng, tương tự như Cloud Storage. Việc lưu trữ hình ảnh đó sẽ sử dụng hết một phần hạn mức đó. Tuy nhiên, bạn có thể sống ở một khu vực không có bậc miễn phí như vậy, vì vậy hãy chú ý đến mức sử dụng bộ nhớ của bạn để giảm thiểu chi phí tiềm ẩn. "Thư mục" cụ thể trên Cloud Storage bạn nên xem xét, bao gồm:

  • console.cloud.google.com/storage/browser/LOC.artifacts.PROJECT_ID.appspot.com/containers/images
  • console.cloud.google.com/storage/browser/staging.PROJECT_ID.appspot.com
  • Các đường liên kết lưu trữ ở trên phụ thuộc vào PROJECT_IDLOC của bạn, chẳng hạn như "us" nếu ứng dụng của bạn được lưu trữ ở Hoa Kỳ.

Mặt khác, nếu bạn không định tiếp tục sử dụng ứng dụng này hoặc các lớp học lập trình di chuyển khác có liên quan và muốn xoá hoàn toàn mọi thứ, hãy ngừng dự án của bạn.

Dành riêng cho lớp học lập trình này

Các dịch vụ được liệt kê dưới đây là những dịch vụ dành riêng cho lớp học lập trình này. Hãy tham khảo tài liệu của từng sản phẩm để biết thêm thông tin:

  • Dịch vụ App Engine Datastore do Cloud Datastore (Cloud Firestore ở chế độ Datastore) cung cấp, cũng có một bậc miễn phí; hãy xem trang giá của YouTube để biết thêm thông tin.
  • Mức độ "miễn phí" của việc sử dụng Cloud Identity Platform tuỳ thuộc vào việc bạn sử dụng dịch vụ nào của Google. Hãy xem trang giá để biết thêm chi tiết.
  • Bạn có thể sử dụng miễn phí Cloud Resource Manager API đối với hầu hết các phần trên trang giá của dịch vụ này.

Các bước tiếp theo

Ngoài hướng dẫn này, bạn cũng cần cân nhắc những mô-đun di chuyển khác tập trung vào việc ngừng sử dụng các dịch vụ cũ theo gói:

  • Mô-đun 2: di chuyển từ App Engine ndb sang Cloud NDB
  • Mô-đun 7-9: di chuyển từ Hàng đợi tác vụ của App Engine (tác vụ đẩy) sang Cloud Tasks
  • Mô-đun 12-13: di chuyển từ App Engine Memcache sang Cloud Memorystore
  • Mô-đun 15-16: di chuyển từ App Engine Blobstore sang Cloud Storage
  • Mô-đun 18-19: di chuyển từ Hàng đợi tác vụ của App Engine (Tác vụ kéo) sang Cloud Pub/Sub

App Engine không còn là nền tảng không máy chủ duy nhất trên Google Cloud nữa. Nếu bạn có một ứng dụng App Engine nhỏ hoặc một ứng dụng có chức năng hạn chế và muốn biến ứng dụng đó thành một dịch vụ vi mô độc lập, hoặc bạn muốn chia một ứng dụng nguyên khối thành nhiều thành phần có thể sử dụng lại, thì đây là những lý do chính đáng để bạn cân nhắc chuyển sang Cloud Functions. Nếu việc tích hợp vùng chứa đã trở thành một phần trong quy trình phát triển ứng dụng, đặc biệt là khi quy trình đó bao gồm một quy trình CI/CD (tích hợp liên tục/phân phối hoặc triển khai liên tục), hãy cân nhắc chuyển sang Cloud Run. Những trường hợp này được đề cập trong các mô-đun sau:

  • Di chuyển từ App Engine sang Cloud Functions: xem Học phần 11
  • Di chuyển từ App Engine sang Cloud Run: xem Mô-đun 4 để vùng chứa ứng dụng của bạn bằng Docker, hoặc Mô-đun 5 để triển khai mà không cần vùng chứa, kiến thức về Docker hoặc Dockerfile

Việc chuyển sang một nền tảng không máy chủ khác là không bắt buộc. Bạn nên cân nhắc các lựa chọn phù hợp nhất cho ứng dụng và trường hợp sử dụng của mình trước khi điều chỉnh bất cứ điều gì.

Bất kể bạn cân nhắc mô-đun di chuyển nào tiếp theo, bạn đều có thể truy cập vào tất cả nội dung của Trạm di chuyển không máy chủ (lớp học lập trình, video, mã nguồn [nếu có]) tại kho lưu trữ nguồn mở của chúng. README của kho lưu trữ này cũng cung cấp hướng dẫn về những quá trình di chuyển cần xem xét và mọi "đơn đặt hàng" có liên quan Mô-đun di chuyển.

8. Tài nguyên khác

Dưới đây là các tài nguyên bổ sung để nhà phát triển tìm hiểu thêm về mô-đun này hoặc các mô-đun di chuyển có liên quan. Bạn có thể đưa ra ý kiến phản hồi về nội dung này ở bên dưới, tìm các đường liên kết đến mã nguồn và nhiều thông tin hữu ích khác của tài liệu.

Vấn đề/ý kiến phản hồi về lớp học lập trình

Nếu bạn gặp vấn đề với lớp học lập trình này, vui lòng tìm vấn đề của bạn trước khi gửi. Đường liên kết để tìm kiếm và báo cáo vấn đề mới:

Tài nguyên di chuyển

Bạn có thể tìm thấy các đường liên kết đến các thư mục repo cho Mô-đun 20 (START) và Mô-đun 21 (Finish) trong bảng bên dưới.

Codelab

Python 2

Python 3

Học phần 20

(không áp dụng)

Mô-đun 21 (lớp học lập trình này)

Tài liệu tham khảo trực tuyến

Dưới đây là các tài nguyên có liên quan cho hướng dẫn này:

Cloud Identity Platform và Cloud Marketplace

Trình quản lý tài nguyên đám mây, Cloud IAM, SDK quản trị của Firebase

Người dùng App Engine, App Engine NDB, Cloud NDB, Cloud Datastore

Các tài liệu tham khảo khác về Mô-đun di chuyển

Di chuyển App Engine

Nền tảng App Engine

SDK đám mây

Thông tin khác về đám mây

Video

Giấy phép

Tác phẩm này được cấp phép theo Giấy phép chung Ghi nhận tác giả Creative Commons 2.0.