1. Tổng quan
Loạt lớp học lập trình Serverless Migration Station (hướng dẫn thực hành theo tốc độ của riêng bạn) và các video liên quan nhằm giúp các nhà phát triển Google Cloud không máy chủ 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 quy trình di chuyển, chủ yếu là chuyển từ các dịch vụ cũ. Làm như vậy sẽ giúp ứng dụng của bạn dễ dàng di chuyển hơn, đồng thời mang đến cho bạn nhiều lựa chọn và sự 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 trên đám mây hơn, cũng như dễ dàng nâng cấp lên các 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 Functions và Cloud Run, hoặc ở những nơi khác nếu có thể.
Mục đích của lớp học lập trình này là hướng dẫn nhà phát triển App Engine Python 2 cách di chuyển từ App Engine Users API/service sang Cloud Identity Platform (GCIP). Ngoài ra, còn có một quy trình di chuyển ngầm từ App Engine NDB sang Cloud NDB để truy cập vào Datastore (chủ yếu được đề cập trong Mô-đun di chuyển 2) cũng như một quy trì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 Users API vào ứng dụng mẫu trong Mô-đun 1. Trong mô-đun này, bạn sẽ lấy ứng dụng hoàn chỉnh trong Mô-đun 20 và di chuyển việ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 Cloud Identity Platform
- 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 Firebase Auth
- Sử dụng API Cloud Resource Manager để lấy thông tin IAM của dự án
- Sử dụng SDK của Firebase dành cho quản trị viên để lấy thông tin người dùng
- Chuyển ứng dụng mẫu sang Python 3
Bạn cần có
- Một dự án trên Google Cloud Platform có một tài khoản thanh toán GCP đang hoạt động
- Kỹ năng cơ bản về Python
- Có kiến thức cơ bản về các lệnh Linux thường dùng
- Kiến thức cơ bản về cách phát triển và triển khai các ứng dụng App Engine
- Một ứng dụng mẫu Module 20 App Engine đang hoạt động
Khảo sát
Bạn sẽ sử dụng hướng dẫn này như thế nào?
Bạn đánh giá thế nào về trải nghiệm của mình với Python?
Bạn đánh giá thế nào về trải nghiệm khi sử dụng các dịch vụ của Google Cloud?
2. Thông tin khái quát
Dịch vụ Người dùng App Engine là một hệ thống xác thực người dùng để các ứng dụng App Engine sử dụng. Nền tảng này cung cấp tính năng Đăng nhập bằng Google làm nhà cung cấp dịch vụ danh tính, cung cấp các đường liên kết đăng nhập và đăng xuất thuận tiện để sử dụng trong các ứng dụng, đồng thời hỗ trợ khái niệm về 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, Google Cloud khuyến nghị bạn di chuyển từ các dịch vụ đi kèm 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, cùng nhiều dịch vụ khác.
Identity Platform dựa trên Xác thực Firebase và bổ sung một số tính năng dành cho doanh nghiệp, bao gồm xác thực đa yếu tố, hỗ trợ OIDC và SAML SSO, nhiều đối tượng thuê, SLA 99, 95% và nhiều tính năng khác. Những điểm khác biệt này cũng được nêu bật trên trang so sánh sản phẩm Identity Platform và Xác thực Firebase. Cả hai sản phẩm này đề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ạ cách chuyển hoạt động 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 của Nền tảng nhận dạng. Đây là những tính năng phản ánh gần nhất chức năng được minh hoạ trong Mô-đun 20. Module 21 cũng có một quy trình di chuyển từ App Engine NDB sang Cloud NDB để truy cập vào Datastore, lặp lại quy trình di chuyển trong Module 2.
Mặc dù mã của Mô-đun 20 được "quảng cáo" là một ứng dụng mẫu Python 2, nhưng bản thân nguồn này tương thích với cả Python 2 và 3, đồng thời vẫn giữ nguyên như vậy ngay cả sau khi di chuyển sang Identity Platform (và Cloud NDB) ở 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 Identity Platform là không bắt buộc. Hãy xem lớp học lập trình Mô-đun 17 và video để 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 thời gian chạy thế hệ thứ 2 như Python 3.
Hướng dẫn này có các bước sau:
- Thiết lập/Công việc chuẩn bị
- Cập nhật cấu hình
- Sửa đổi mã xử lý ứng dụng
3. Thiết lập/Công việc chuẩn bị
Phần này giải thích cách:
- Thiết lập dự án trên đám mây
- Tải ứng dụng mẫu cơ sở
- (Triển khai lại) và xác thực ứng dụng cơ sở
- Bật các dịch vụ/API mới của Google Cloud
Các bước này đảm bảo bạn bắt đầu bằng mã 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 dùng lại chính dự án (và mã) đó. Ngoài ra, bạn có thể 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. Đảm bảo dự án có một tài khoản thanh toán đang hoạt động và một ứng dụng App Engine đã bật. Tìm mã dự án của bạn và lưu giữ mã này trong suốt lớp học lập trình này, đồng thời 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 hoạt động của Mô-đun 20, vì vậy, hãy hoàn tất lớp học lập trình của ứng dụng đó (nên dùng; đường liên kết ở trên) hoặc sao chép mã Mô-đun 20 từ kho lưu trữ. Dù bạn dùng mã của mình hay mã của chúng tôi, đây là nơi chúng ta sẽ bắt đầu ("START"). Lớp học lập trình này sẽ hướng dẫn bạn quy trình di chuyển, kết thúc bằng mã tương tự như mã trong thư mục kho lưu trữ Mô-đun 21 ("FINISH").
- BẮT ĐẦU: Thư mục Mô-đun 20 (Python 2)
- HOÀN TẤT: Thư mục Mô-đun 21 ( Python 2 hoặc Python 3)
- Toàn bộ kho lưu trữ (để sao chép hoặc tải tệp ZIP xuống)
Sao chép thư mục kho lưu trữ Module 20. Thư mục này sẽ có dạng như đầu ra bên dưới 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 hiện các bước sau để triển khai ứng dụng Module 20:
- Xoá thư mục
lib(nếu có) rồi chạypip install -t lib -r requirements.txtđể điền lại thư mục đó. Bạn có thể cần dùngpip2nếu đã cài đặt cả Python 2 và 3. - Đảm bảo bạn đã cài đặt và khởi chạy công cụ dòng lệnh
gcloud, đồng thời đã xem xét cách sử dụng công cụ này. - Nếu bạn không muốn nhập
PROJ_IDcho mỗi lệnhgcloudđược phát hành, hãy đặt dự án trên đám mây bằnggcloud config set projectPROJ_IDtrước. - Triển khai ứng dụng mẫu bằng
gcloud app deploy - Xác nhận rằng ứng dụng chạy như dự kiến 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 người dùng, "huy hiệu quản trị" (nếu có) và nút đăng nhập/đăng xuất) ở trên cùng cùng với các lượt truy cập gần đây nhất (minh hoạ bên dưới).

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

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:

4. Bật các API/dịch vụ mới của Google Cloud
Giới thiệu
Ứng dụng Module 20 sử dụng App Engine NDB và Users API, 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ụ Cloud độc lập thì có, và ứng dụng đã cập nhật sẽ sử dụng cả Cloud Identity Platform và Cloud Datastore (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ị App Engine cũng đòi hỏi việc sử dụng Cloud Resource Manager API.
Chi phí
- App Engine và Cloud Datastore có hạn mức "Luôn miễn phí". Vì vậy, miễn là bạn không vượt quá những hạn mức đó, bạn sẽ không phải trả phí khi hoàn thành hướng dẫn này. Bạn cũng có thể xem trang thông tin về giá của App Engine và trang thông tin về giá của Cloud Datastore để biết thêm thông tin chi tiết.
- Việc sử dụng Nền tảng Cloud Identity 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 minh danh tính; một số phiên bản "miễn phí" được cung cấp cho từng mô hình sử dụng. Hãy xem trang thông tin về giá của công cụ này để biết thêm thông tin. Hơn nữa, mặc dù App Engine và Cloud Datastore yêu cầu thanh toán, nhưng việc sử dụng GCIP không yêu cầu bạn 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 có công cụ. Vì vậy, hãy cân nhắc điều này cho các dự án trên Cloud không liên quan đến các API/dịch vụ trên Cloud yêu cầu thanh toán.
- Hầu hết các hoạt động sử dụng Cloud Resource Manager API đều miễn phí theo trang định giá của API 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 Cloud SDK), tuỳ theo lựa chọn ưu tiên của bạn. Hãy bắt đầu với API Cloud Datastore và Cloud Resource Manager.
Trên Cloud Console
Chuyển đến trang Thư viện của Trình quản lý API (đối với dự án phù hợp) trong Cloud Console, rồi tìm kiếm một API bằng thanh tìm kiếm. 
Bật các API sau:
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 dành cho Resource Manager API:

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

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

Từ dòng lệnh
Mặc dù việc bật API từ bảng điều khiển mang tính trực quan, nhưng một số người lại thích dòng lệnh. Bạn sẽ nhận được thêm lợi ích là có thể bật cùng lúc nhiều API. Hãy đưa ra lệnh này để bật cả API Cloud Datastore và Cloud Resource Manager, đồng thời chờ thao tác hoàn tất, như minh hoạ ở đâ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 nhập thông tin thanh toán.
"URL" cho mỗi API được dùng trong lệnh ở trên được gọi là tên dịch vụ API và bạn có thể tìm thấy tên dịch vụ ở cuối trang thư viện của mỗi API. Nếu muốn bật các Cloud APIs khác cho ứng dụng của riêng mình, bạn có thể tìm thấy tên dịch vụ tương ứng của các API đó trên các trang API tương ứng. Lệnh này liệt kê tất cả tên dịch vụ cho các API mà bạn có thể bật:
gcloud services list --available --filter="name:googleapis.com".
Dù bạn dùng bảng điều khiển Cloud hay dòng lệnh, sau khi bạn hoàn tất các bước trên, mẫu của chúng tôi hiện có thể truy cập vào những API đó. Các bước tiếp theo là bật Nền tảng Cloud Identity và thực hiện các thay đổi cần thiết về mã.
Bật và thiết lập Cloud Identity Platform (chỉ trên 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 với hoặc phụ thuộc vào một tài nguyên bên ngoài Google Cloud, chẳng hạn như 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:
- Chuyển đến trang Cloud Identity Platform trong Cloud Marketplace rồi nhấp vào nút Bật. Nâng cấp từ Xác thực Firebase nếu được nhắc. Việc này sẽ mở khoá các tính năng bổ sung, chẳng hạn như những tính năng được mô tả trước đó trong phần Thông tin chung. Sau đây là trang Marketplace làm nổi bật nút Bật:

- Sau khi bật Identity Platform, bạn có thể được chuyển tự động đến trang Nhà cung cấp dịch vụ danh tính. Nếu không, hãy dùng đường liên kết thuận tiện này để truy cập.

- Bật trình cung cấp Google Auth. Nếu bạn 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 bạn 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 đơn giản. Trong các ứng dụng của riêng mình, bạn có thể bật thêm các nhà cung cấp dịch vụ uỷ quyền.
- Sau khi bạn chọn và thiết lập Google cũng như các nhà cung cấp dịch vụ uỷ quyền mong muốn khác, hãy nhấp vào Application Setup Details (Thông tin thiết lập ứng dụng) rồi sao chép
apiKeyvàauthDomaintrong đối tượngconfigtrên thẻ Web, đồng thời lưu cả hai đối tượng này ở 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 mã hoá cứng và đã cũ, vì vậy, bạn chỉ cần lưu những phần quan trọng nhất và sử dụng chúng trong mã của chúng ta với mức sử dụng Firebase Auth đồng thời cao 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.
4. Cập nhật cấu hình
Các nội dung cập nhật trong cấu hình bao gồm cả việc thay đổi nhiều tệp cấu hình cũng như tạo nội dung tương đương với App Engine nhưng nằm 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 dự định hiện đại hoá lên Identity Platform nhưng vẫn giữ Python 2, thì đừng xoá tệp này. Thay vào đó, chúng ta sẽ cập nhật sau trong quá trình chuyển ngược Python 2.
requirements.txt
Tệp requirements.txt của mô-đun 20 chỉ liệt kê Flask. Đối với Mô-đun 21, hãy thêm các gói sau:
Nội dung của requirements.txt giờ đây 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ừ chỉ thị thời gian chạy và đặt chỉ thị đó thành một phiên bản Python 3 hiện được hỗ trợ. Ví dụ này hiện đang dùng phiên bản 3.10. - Nếu bạn vẫn sử dụng Python 2, thì hiện tại bạn không cần làm gì cả.
TRƯỚC:
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 ứng dụng của bạn có sử dụng, hãy giữ nguyên. Bạn có thể xoá tất cả trình xử lý tập lệnh nếu muốn hoặc chỉ cần để nguyên chúng để tham khảo miễn là bạn thay đổi 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 cập nhật cho Python 3 sẽ được đơn giản hoá thành:
SAU:
runtime: python310
Các nội dung cập nhật khác về cấu hình
Cho dù bạn vẫn dùng Python 2 hay chuyển sang Python 3, nếu 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 có các nội dung 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 web, templates/index.html.
Cập nhật các hoạt động nhập và khởi tạo
Hãy làm theo các bước dưới đây để cập nhật các nội dung nhập và khởi tạo tài nguyên ứng dụng:
- Đối với các lượt nhập, hãy thay thế App Engine NDB bằng Cloud NDB.
- Ngoài Cloud NDB, hãy nhập cả Cloud Resource Manager.
- Identity Platform dựa trên Firebase Auth, vì vậy hãy nhập SDK của Firebase dành cho quản trị viên.
- Các Cloud APIs yêu cầu sử 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 động Flask.
Mặc dù gói Cloud Resource Manager đượ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 hoạt động nhập và khởi tạo từ Mô-đun 20, theo sau là cách các phần sẽ xuất hiện sau khi triển khai các thay đổi nêu trên:
TRƯỚC:
from flask import Flask, render_template, request
from google.appengine.api import users
from google.appengine.ext import ndb
app = Flask(__name__)
SAU:
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ị App Engine
Có hai thành phầ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()– đối chiếu tập hợp người dùng quản trị; được gọi một lần và lưuis_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 khi có người dùng đăng nhập
Hàm hiệu dụng _get_gae_admins() gọi Resource Manager API để 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 các thực thể (người dùng, tài khoản dịch vụ, v.v.). Quá trình thiết lập bao gồm:
- Tìm nạp mã dự án trên đám mây (
PROJ_ID) - Tạo một ứng dụng Resource Manager API (
rm_client) - Tạo một 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 đám mây, 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ố có vẻ giống như một URL nhưng lại là một phạm vi quyền OAuth2. Khi chạy các ứng dụng trên đám mây, chẳng hạn như trên một máy ảo Compute Engine hoặc ứng dụng App Engine, một tài khoản dịch vụ mặc định sẽ được cung cấp và có nhiều đặc quyền. Để tuân thủ 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, tốt nhất là bạn nên giảm thêm phạm vi của ứng dụng xuống mức tối thiểu cần thiết để hoạt động bình thường. Lệnh gọi API Resource Manager mà chúng ta sẽ thực hiện là get_iam_policy(). Lệnh gọi này cần một trong các phạm vi sau để hoạt động:
https://www.googleapis.com/auth/cloud-platformhttps://www.googleapis.com/auth/cloud-platform.read-onlyhttps://www.googleapis.com/auth/cloudplatformprojectshttps://www.googleapis.com/auth/cloudplatformprojects.readonly
Ứng dụng mẫu chỉ cần quyền truy cập chỉ đọc vào chính sách cho phép. Công cụ này không sửa đổi chính sách và cũng không cần có 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ạn chỉ cần triển khai phương thức cuối cùng và đó là phương thức mà chúng ta sẽ triển khai cho ứng dụng mẫu.
Phần chính của hàm này tạo một nhóm trống gồm những người dùng quản trị (admins), tìm nạp allow_policy thông qua get_iam_policy() và lặp lại tất cả các mối liên kết của nhóm đó, đặc biệt là tìm kiếm các vai trò Quản trị viên App Engine:
roles/viewerroles/editorroles/ownerroles/appengine.appAdmin
Đối với mỗi vai trò mục tiêu tìm được, công cụ này sẽ đối chiếu những người dùng thuộc vai trò đó, rồi thêm họ vào nhóm người dùng quản trị chung. Thao tác này kết thúc bằng cách trả về tất cả người dùng quản trị đã 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 ta sẽ thấy cuộc gọi đó ngay sau đây.
Thêm định nghĩa hàm _get_gae_admins() sau đây vào main.py ngay bên dưới việc tạo thực thể cho ứng dụng API Cloud NDB (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:
- Hệ thống sẽ kiểm tra nhanh mẫu web sau khi người dùng đăng nhập vào Firebase.
- Khi trạng thái uỷ quyền thay đổi trong mẫu, một lệnh gọi
fetch()theo kiểu Ajax sẽ được thực hiện đến/is_admin, trong đó trình xử lý là hàm tiếp theo,is_admin(). - Mã thông báo nhận dạng Firebase được truyền trong phần nội dung POST đến
is_admin(), mã này sẽ lấy mã thông báo đó ra khỏi tiêu đề và gọi SDK của Firebase dành cho quản trị viên để xác thực mã thông báo. Nếu đó là một 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. - Sau đó, kết quả Boolean sẽ được trả về mẫu dưới dạng 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
Bạn cần có tất cả mã từ cả hai hàm để sao chép chức năng có trong dịch vụ Người dùng, cụ thể là hàm is_current_user_admin() của dịch vụ này. Lệnh gọi hàm này trong Mô-đun 20 đã thực hiện tất cả các thao tác nặng, không giống như Mô-đun 21, nơi chúng ta triển khai một giải pháp thay thế. Tin vui là ứng dụng này không còn phụ thuộc vào dịch vụ chỉ dành cho App Engine nữa, tức là bạn có thể di chuyển các ứ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 các ứng dụng của riêng mình chỉ bằng cách chuyển sang các vai trò mong muốn trong _TARGETS, trong khi dịch vụ Người dùng được mã hoá cứng cho các vai trò quản trị App Engine.
Khởi động tính năng Xác thực Firebase và lưu vào bộ nhớ đệm người dùng quản trị App Engine
Chúng ta có thể đã khởi động Firebase Auth ở trên cùng, gần cùng vị trí mà ứng dụng Flask được khởi động và ứng dụng Cloud NDB API được tạo, nhưng không cần thiết cho đến khi tất cả mã quản trị được xác định, đó là vị trí hiện tại của chúng ta. Tương tự, giờ đây khi _get_gae_admins() được xác định, hãy gọi hàm này để lưu vào bộ nhớ đệm danh sách người dùng quản trị.
Thêm các dòng này ngay bên dưới phần nội dung hàm của is_admin():
# initialize Firebase and fetch set of App Engine admins
initialize_app()
_ADMINS = _get_gae_admins()
Truy cập vào phần cập nhật mô hình dữ liệu
Mô hình dữ liệu Visit không thay đổi. Để truy cập vào Datastore, bạn cần sử dụng rõ ràng trình quản lý bối cảnh ứng dụng Cloud NDB API, ds_client.context(). Trong mã, điều này có nghĩa là bạn sẽ bao bọc các lệnh gọi Datastore trong cả store_visit() và fetch_visits() bên trong các khối with của Python. Bản cập nhật này giống hệt với Mô-đun 2. Thực hiện các thay đổi như sau:
TRƯỚC:
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:
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 Firebase Auth và Cloud Identity Platform chủ yếu là 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 sẽ chuyển sang mẫu web Mô-đun 21.
Trong main.py, bối cảnh web sẽ truyền 5 phần dữ liệu thiết yếu đến mẫu. 4 phần đầu tiên được liệt kê sẽ liên kết với tính nă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 user nếu khôngadmin– Huy hiệu (quản trị viên) nếu người dùng đã đăng nhập là quản trị viênsign– hiện nút Đăng nhập hoặc Đăng xuấtlink– đường liên kết đăng nhập hoặc đăng xuất khi nhấp vào nútvisits– lượt truy cập gần đây nhất
TRƯỚC:
@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)
Tất cả 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 trạng thái ban đầu trong ứng dụng Mô-đun 1:
SAU:
@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 trang web
Tất cả nội dung cập nhật trong phần trước sẽ xuất hiện như thế nào trong mẫu? Chủ yếu là di chuyển tính năng quản lý người dùng từ ứng dụng sang Firebase Auth chạy trong mẫu và một phần của tất cả mã đó mà chúng tôi đã di chuyển vào JavaScript. Chúng tôi nhận thấy main.py giảm khá nhiều, vì vậy, hãy kỳ vọng templates/index.html cũng sẽ có mức tăng trưởng tương tự.
TRƯỚC:
<!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 bên dưới:
SAU:
<!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 xem xét từng thành phần một.
Nhập Firebase
Trong khi vẫn ở tiêu đề của tài liệu HTML, sau khi vượt 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 Firebase hiện được chia thành nhiều mô-đun để tăng 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 hàm quản lý tính năng xác thực Firebase, Google làm nhà cung cấp dịch vụ xác thực, đăng nhập và đăng xuất, cũng như "lệnh gọi lại" thay đổi trạng thái xác thực đề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 apiKey và authDomain từ hộp thoại Application Setup Details (Thông tin thiết lập ứng dụng). Thêm các giá trị đó vào biến firebaseConfig trong phần tiếp theo này. Đường liên kết đến hướng dẫn chi tiết hơn được cung cấp 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 động 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'});
Thao tác này thiết lập khả năng sử dụng Google làm nhà cung cấp dịch vụ uỷ quyền và cung cấp một lựa chọn được chú thích để hiện 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ẽ thấy "trình chọn tài khoản" này như mong đợi:
Tuy nhiên, nếu chỉ có một người dùng trong phiên, thì quy trình đăng nhập sẽ tự động hoàn tất mà không cần người dùng tương tác. (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 xuất hiện cho một người dùng (thay vì đăng nhập ngay vào ứng dụng) bằng cách bỏ chú thích dòng tham số tuỳ chỉnh. Nếu được bật, ngay cả khi đăng nhập một người dùng, trình chọn tài khoản cũng sẽ xuất hiện: 
Hàm đăng nhập và đăng xuất
Các dòng mã tiếp theo tạo thành các hàm cho lượt 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);
};
Hành động đă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 về hoạt động 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ó nên gửi ngữ cảnh mẫu "người dùng đã đăng nhập" so với ngữ cảnh "người dùng đã đăng xuất" hay không sẽ được chuyển đổi tại đây. Điều kiện ở trên cùng sẽ dẫn đến true nếu người dùng đăng nhập thành công, kích hoạt các thao tác sau:
- Địa chỉ email của người dùng được đặt để hiển thị.
- Nút Đăng nhập sẽ chuyển thành Đăng xuất.
- Lệnh gọi theo kiểu Ajax đến
/is_adminđược thực hiện để xác định xem có nên hiện 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:
- Tên người dùng được đặt thành user
- Đã xoá mọi huy hiệu quản trị viên
- Nút Đăng xuất đã thay đổ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 sẽ bắt đầu bằng các biến mẫu được thay thế bằng các phần tử HTML thay đổi khi cần thiết:
- Tên người dùng hiển thị
- Huy hiệu quản trị viên
(admin)(nếu có) - 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 các biến cho những phần tử HTML thay đổi khi đăng nhập và đăng xuất được liệt kê ngay ở 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>
Đây là những thay đổi cần thiết trong ứng dụng và mẫu web để chuyển từ App Engine NDB và Users API sang Cloud NDB và Identity Platform, cũng như nâng cấp lên Python 3. Chúc mừng bạn đã đến được ứng dụng mẫu mới của Mô-đun 21! Bạn có thể xem phiên bản của chúng tôi trong thư mục repo của Mô-đun 21b.
Phần tiếp theo của lớp học lập trình là không bắt buộc (*) và chỉ dành cho những người dùng có ứng dụng phải duy trì trên Python 2, hướng dẫn bạn các bước cần thiết để có được một ứng dụng Module 21 Python 2 đang hoạt động.
6. *Bản chuyển ngược Python 2
Đây là phần không bắt buộc dành cho những nhà phát triển đang thực hiện quy trình di chuyển Identity Platform nhưng phải tiếp tục chạy trên 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 hoạt động của ứng dụng Module 21 bằng Python 2, bạn cần có những thông tin sau:
- Yêu cầu về thời gian chạy: Tệp cấu hình hỗ trợ Python 2 và các thay đổi bắt buộc trong ứng dụng chính để tránh các vấn đề không tương thích với Python 3
- Thay đổi nhỏ về thư viện: Python 2 đã ngừng hoạt động 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 Trình quản lý tài nguyên. Do đó, bạn cần một cách khác để truy cập vào chức năng bị thiếu đó.
Hãy thực hiện các bước đó ngay bây giờ, bắt đầu từ bước định cấu hình.
Khôi phục appengine_config.py
Trước đó trong hướng dẫn này, bạn đã được hướng dẫn xoá appengine_config.py vì thời gian chạy App Engine Python 3 không dùng đến. Đối với Python 2, không chỉ cần duy trì mà còn phải cập nhật Phụ lục 20 appengine_config.py để hỗ trợ việc sử dụng các thư viện tích hợp của bên thứ ba, cụ thể là grpcio và setuptools. Bạn phải có những 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ư thư viện cho Cloud NDB và Cloud Resource Manager.
Bạn sẽ thêm những 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 đó, bạn phải gọi hàm pkg_resources.working_set.add_entry() từ setuptools. Điều này cho phép các thư viện bên thứ ba đã sao chép (tự đóng gói hoặc đượ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.
Hãy triển khai các nội dung cập nhật sau đây cho tệp appengine_config.py để áp dụng những thay đổi này:
TRƯỚC:
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ỉ mã này thôi là chưa đủ để hỗ trợ việc sử dụng setuptools và grpcio. Bạn cần thêm một vài dòng nữa, vì vậy hãy cập nhật appengine_config.py để có dạng như sau:
SAU:
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 chi tiết về những thay đổi cần thiết để hỗ trợ các 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, bạn phải khôi phục tệp app.yaml về một tệp hỗ trợ Python 2. Hãy bắt đầu với Mô-đun 20 ban đầu app.yaml:
TRƯỚC:
runtime: python27
threadsafe: yes
api_version: 1
handlers:
- url: /.*
script: main.app
Ngoài setuptools và grpcio như đã đề cập trước đó, còn có một phần phụ thuộc (không liên quan rõ ràng đến quá trình di chuyển Identity Platform) yêu cầu sử dụng thư viện ứng dụng Cloud Storage và phần phụ thuộc này cần một gói tích hợp sẵn khác của bên thứ ba, ssl. Thêm cả 3 gói này vào một phần libraries mới, chọn phiên bản "mới nhất" của các gói đó, rồi app.yaml:
SAU:
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 Google Auth, Cloud NDB, Cloud Resource Manager và SDK của Firebase dành cho quản trị viên vào requirements.txt Python 3. Tình hình đối với Python 2 phức tạp hơn:
- API Resource Manager 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 là 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. (Tính năng này chỉ có trong phiên bản Python 3.)
- Do đó, bạn cần có một cách khác để truy cập vào tính năng này thông qua API. Giải pháp là sử dụng thư viện ứng dụng API Google cấp thấp để giao tiếp với API. Để chuyển sang thư viện ứng dụng này, hãy thay thế
google-cloud-resource-managerbằng góigoogle-api-python-clientcấp thấp. - Vì Python 2 đã ngừng hoạt động, nên biểu đồ phần phụ thuộc hỗ trợ Mô-đun 21 yêu cầu khoá một số gói ở các phiên bản cụ thể. Bạn phải gọi một số gói ngay cả khi chúng không được chỉ định trong
app.yamlcủa Python 3.
TRƯỚC:
flask
Bắt đầu với Mô-đun 20 requirements.txt, hãy cập nhật mô-đun này thành mô-đun sau đây để có một ứng dụng Mô-đun 21 hoạt động:
SAU:
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à 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 đủ cho một ứng dụng 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 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 đưa ra lệnh quen thuộc này để cài đặt các yêu cầu này 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ể cần dùng pip2 thay vì pip.
Sửa đổi mã xử lý ứng dụng
May mắn thay, hầu hết các thay đổi bắt buộc đều nằm trong 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 thay vì thư viện ứng dụng Resource Manager để truy cập vào API. Bạn không cần cập nhật mẫu web templates/index.html.
Cập nhật các tệp nhập và quá trình khởi tạo
Thay thế thư viện ứng dụng Resource Manager (google.cloud.resourcemanager) bằng thư viện ứng dụng Google APIs (googleapiclient.discovery), như minh hoạ dưới đây:
TRƯỚC:
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:
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
Hỗ trợ người dùng quản trị App Engine
Bạn cần thực hiện một số thay đổi trong _get_gae_admins() để hỗ trợ việc sử dụng thư viện ứng dụng cấp thấp. Trước tiên, hãy thảo luận về những thay đổi, sau đó cung cấp cho bạn tất cả mã cần cập nhật.
Mã Python 2 yêu cầu sử dụng cả thông tin đăng nhập và mã dự án được trả về từ google.auth.default(). Thông tin đăng nhập không được dùng trong Python 3, vì vậy, thông tin này được chỉ định cho một biến giả dấu 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 một ứng dụng API Resource Manager, bạn sẽ tạo một điểm cuối dịch vụ API (tương tự như ứng dụng API) nên chúng ta sẽ giữ nguyên tên biến (rm_client). Một điểm khác biệt là việc khởi tạo một điểm cuối dịch vụ yêu cầu thông tin đăng nhập (CREDS).
Những thay đổi này được phản ánh trong đoạn mã dưới đây:
TRƯỚC:
_, PROJ_ID = default( # Application Default Credentials and project ID
['https://www.googleapis.com/auth/cloudplatformprojects.readonly'])
rm_client = resourcemanager.ProjectsClient()
SAU:
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 Resource Manager trả về các đối tượng allow-policy sử dụng ký hiệu thuộc tính có dấu chấm, trong khi thư viện ứng dụng cấp thấp hơn trả về các từ điển Python sử dụng dấu ngoặc vuông ( [ ]), ví dụ: sử dụng binding.role cho thư viện ứng dụng Resource Manager so với binding['role'] cho thư viện cấp thấp hơn. Thư viện trước đây cũng sử dụng tên "underscore_separated" so với thư viện cấp thấp hơn, ưu tiên tên "CamelCased" cùng với một cách truyền tham số API hơi khác.
Dưới đây là những điểm khác biệt về cách sử dụng:
TRƯỚC:
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:
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'])
Sau khi thực hiện tất cả những thay đổi này, hãy 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
Bạn không cần cập nhật hàm is_admin() vì hàm này dựa vào _get_gae_admins() và hàm này đã được cập nhật.
Đến đây là kết thúc những thay đổi cần thiết để chuyển ngược ứng dụng Python 3 Module 21 sang Python 2. Chúc mừng bạn đã đến được ứng dụng mẫu cập nhật của Mô-đun 21! 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
Các bước cuối cùng trong lớp học lập trình là đảm bảo các thực thể (người dùng hoặc tài khoản dịch vụ) chạy ứng dụng này có quyền thích hợp để làm như vậy, sau đó triển khai ứng dụng để xác nhận rằng ứng dụng hoạt động như dự kiến và các thay đổi được phản ánh trong đầu ra.
Có thể đọc chính sách cho phép của IAM
Trước đây, chúng tôi đã giới thiệu cho bạn 4 vai trò cần thiết để được công nhận là người dùng quản trị App Engine, nhưng hiện tại có thêm một vai trò thứ năm mà bạn cần làm quen:
roles/viewerroles/editorroles/ownerroles/appengine.appAdminroles/resourcemanager.projectIamAdmin(đối với các thực thể truy cập vào chính sách cho phép IAM)
Vai trò roles/resourcemanager.projectIamAdmin cho phép các thực thể 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ị nào của App Engine hay không. Nếu không có tư cách thành viên trong roles/resourcemanager.projectIamAdmin, các lệnh gọi đến Cloud Resource Manager API để lấy chính sách cho phép sẽ không thành công.
Bạn không cần thực hiện bất kỳ thao tác rõ ràng nào ở đâ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 quyền 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 một tài khoản dịch vụ do người dùng quản lý với 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 quyền thành viên cho tài khoản dịch vụ như vậy, 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 đám mây 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 xuất chính sách IAM đã cập nhật cho dự án của bạn, trong đó bạn có thể xác nhận rằng 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. Xin nhắc lại, bạn không cần phải đư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á các ứ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 như giống hệt với ứng dụng Module 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à Firebase Auth) để quản lý người dùng:

Một điểm khác biệt mà bạn sẽ nhận thấy so với Mô-đun 20 là việc nhấp vào nút Đăng nhập sẽ dẫn đến một cửa sổ bật lên thay vì một lệnh chuyển hướng, được ghi lại trong một số ảnh chụp màn hình bên dưới. Giống như Mô-đun 20, tuy nhiên, hành vi này có khác biệt đôi 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 không có người dùng nào đăng ký bằng trình duyệt hoặc chỉ có một người dùng chưa đăng nhập, thì một cửa sổ bật lên chung của Google Sign-in sẽ xuất hiện:

Nếu một người dùng được đăng ký bằng trình duyệt của bạn nhưng đă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 sẽ bật lên rồi đó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 của 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 một bộ chọn tài khoản, ngay cả đối với một người dùng duy nhất:

Để triển khai việc này, hãy bỏ chú thích 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 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 đăng nhập. 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.

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

Điều này cũng áp dụng khi người dùng quản trị đã đăng nhập:

Không giống như Mô-đun 21, Mô-đun 20 luôn truy cập vào logic cho nội dung mẫu web từ ứng dụng (mã phía máy chủ). Một điểm yếu của Module 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). Không cần chuyến đi bắt buộc 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à lệnh 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à các lượt đăng nhập và đăng xuất không được tính là lượt truy cập bổ sung, vì vậy, danh sách lượt truy cập gần đây nhất vẫn giữ nguyên cho các thao tác quản lý người dùng. Xin lưu ý rằng ảnh chụp màn hình ở trên cho thấy cùng một nhóm gồ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" ở phần đầu của lớp học lập trình này. Nhật ký riêng biệt về lượt truy cập sẽ xuất hiện 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ần truy cập gần đây nhất cho từng ảnh chụp màn hình để xem thứ tự thời gian.
Dọn dẹp
Giải pháp chung
Nếu đã hoàn tất, bạn nên tắt ứng dụng App Engine để tránh bị tính phí. Tuy nhiên, nếu muốn kiểm thử hoặc thử nghiệm thêm, nền tảng App Engine có một hạn mức miễn phí. Vì vậy, miễn là không vượt quá cấp sử dụng đó, bạn sẽ không bị tính phí. Đó là mức phí cho hoạt động tính toán, nhưng cũng có thể có các khoản phí cho các dịch vụ App Engine có liên quan. Vì vậy, hãy xem trang định 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ụ đám mây khác, thì các 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 "Cụ thể cho lớp học lập trình này" bên dưới.
Để công bố đầy đủ, việc triển khai trên một nền tảng điện toán không máy chủ của Google Cloud như App Engine sẽ phát sinh một khoản chi phí nhỏ cho việc tạo và lưu trữ. Cloud Build 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ẽ chiếm một phần hạn mức. Tuy nhiên, có thể bạn sinh sống ở một khu vực không có gói miễn phí như vậy, vì vậy, hãy lưu ý đến mức sử dụng bộ nhớ để giảm thiểu chi phí phát sinh. Bạn nên xem xét các "thư mục" cụ thể trên Cloud Storage, bao gồm:
console.cloud.google.com/storage/browser/LOC.artifacts.PROJECT_ID.appspot.com/containers/imagesconsole.cloud.google.com/storage/browser/staging.PROJECT_ID.appspot.com- Các đường liên kết đến bộ nhớ ở trên phụ thuộc vào
PROJECT_IDvàLOCcủa bạn, ví dụ: "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 tiếp tục với ứng dụng này hoặc các lớp học lập trình di chuyển có liên quan khác và muốn xoá hoàn toàn mọi thứ, hãy tắt dự án của bạn.
Cụ thể đối với lớp học lập trình này
Các dịch vụ trong danh sách dưới đây là riêng biệt đối với 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, dịch vụ này cũng có một cấp miễn phí; hãy xem trang định giá của dịch vụ này để biết thêm thông tin.
- Việc sử dụng Cloud Identity Platform có một mức "miễn phí" nhất định, tuỳ thuộc vào dịch vụ bạn sử dụng. Hãy xem trang thông tin về giá của công cụ này để biết thêm thông tin.
- Hầu hết các hoạt động sử dụng Cloud Resource Manager API đều miễn phí theo trang định giá của API này.
Các bước tiếp theo
Ngoài hướng dẫn này, bạn nên cân nhắc các mô-đun di chuyển khác tập trung vào việc chuyển từ các dịch vụ cũ đi kèm, bao gồm:
- Mô-đun 2: di chuyển từ
ndbApp Engine sang Cloud NDB - Các mô-đun 7-9: di chuyển từ Hàng đợi tác vụ App Engine (đẩy tác vụ) sang Cloud Tasks
- Các mô-đun 12 – 13: di chuyển từ App Engine Memcache sang Cloud Memorystore
- Các mô-đun 15-16: di chuyển từ Blobstore của App Engine sang Cloud Storage
- Các mô-đun 18-19: di chuyển từ Hàng đợi tác vụ App Engine (kéo tác vụ) sang Cloud Pub/Sub
App Engine không còn là nền tảng duy nhất không cần máy chủ 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 vi dịch vụ độ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ể dùng lại, thì đây là những lý do chính đáng để cân nhắc việc chuyển sang Cloud Functions. Nếu việc tạo vùng chứa đã trở thành một phần trong quy trình phát triển ứng dụng của bạn, đặc biệt là nếu quy trình này bao gồm một quy trình CI/CD (tích hợp liên tục/phân phối liên tục hoặc triển khai liên tục), hãy cân nhắc việc di chuyển sang Cloud Run. Các 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 Mô-đun 11
- Di chuyển từ App Engine sang Cloud Run: xem Mô-đun 4 để đóng gói ứng dụng của bạn bằng Docker hoặc Mô-đùn 5 để thực hiện việc này mà không cần vùng chứa, kiến thức về Docker hoặc
Dockerfile
Bạn không bắt buộc phải chuyển sang một nền tảng không máy chủ khác. Bạn nên cân nhắc những 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 thực hiện bất kỳ thay đổi nào.
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 Serverless Migration Station (lớp học lập trình, video, mã nguồn [nếu có]) tại kho lưu trữ nguồn mở. README của kho lưu trữ này cũng cung cấp hướng dẫn về những hoạt động di chuyển cần cân nhắc và "thứ tự" liên quan của các Mô-đun di chuyển.
8. Tài nguyên khác
Dưới đây là các tài nguyên bổ sung dành cho những nhà phát triển muốn tìm hiểu thêm về mô-đun di chuyển 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, tìm đường liên kết đến mã và nhiều phần tài liệu mà bạn có thể thấy hữu ích ở bên dưới.
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 kiếm vấn đề của bạn trước khi báo cáo. Đường liên kết để tìm kiếm và tạo vấn đề mới:
Tài nguyên di chuyển
Bạn có thể tìm thấy đường liên kết đến các thư mục repo cho Mô-đun 20 (BẮT ĐẦU) và Mô-đun 21 (KẾT THÚC) trong bảng bên dưới.
Lớp học lập trình | Python 2 | Python 3 |
(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 liên quan đến hướng dẫn này:
Cloud Identity Platform và Cloud Marketplace
- Trang sản phẩm Identity Platform
- Xác thực Firebase
- Trang so sánh sản phẩm Identity Platform và Firebase Auth
- Thông tin về giá của Identity Platform
- Hạn mức Identity Platform (và mức sử dụng không có công cụ)
- Thiết lập nhà cung cấp Identity Platform
- Trang sản phẩm trên Cloud Marketplace
- Trang Identity Platform trong Marketplace
Cloud Resource Manager, Cloud IAM, Firebase Admin SDK
- Trang sản phẩm Resource Manager
- Thông tin về giá của Resource Manager
- Thư viện ứng dụng Resource Manager
- Tổng quan về Cloud IAM (vai trò, chính sách cho phép, v.v.)
- Firebase Admin SDK (Python)
Người dùng App Engine, App Engine NDB, Cloud NDB, Cloud Datastore
- Tổng quan về người dùng App Engine
- Tài liệu về NDB của App Engine
- Kho lưu trữ NDB của App Engine
- Thư viện ứng dụng Cloud NDB
- Kho lưu trữ Cloud NDB
- Trang sản phẩm Cloud Datastore
- Thông tin về giá của Cloud Datastore
Thông tin tham khảo về Mô-đun di chuyển khác
- Giới thiệu về Mô-đun di chuyển
- Tất cả tài nguyên của "Serverless Migration Station"
- Tài liệu về việc di chuyển sang Python 3
- Lớp học lập trình về Di chuyển mô-đun 17 "Sử dụng các dịch vụ đi kèm trong thời gian chạy thế hệ thứ 2"
- Lớp học lập trình Di chuyển mô-đun 20 "Thêm dịch vụ Người dùng App Engine vào các ứng dụng Flask"
Di chuyển App Engine
- Sử dụng các thư viện của bên thứ ba trong ứng dụng Python 2
- Các thay đổi đối với
app.yamltrong thời gian chạy thế hệ thứ 2 (Python 3) - Hướng dẫn di chuyển Cloud NDB
- Nội dung về việc di chuyển NDB lên đám mây
Nền tảng App Engine
- Tài liệu về App Engine
- Thời gian chạy Python 2 App Engine (môi trường tiêu chuẩn)
- Sử dụng các thư viện tích hợp sẵn của App Engine trên App Engine Python 2
- Thời gian chạy Python 3 App Engine (môi trường tiêu chuẩn)
- Sự khác biệt giữa thời gian chạy Python 2 và 3 App Engine (môi trường tiêu chuẩn)
- Hướng dẫn di chuyển ứng dụng từ Python 2 sang Python 3 trên App Engine (môi trường tiêu chuẩn)
- Thông tin về giá và hạn mức của App Engine
- Ra mắt nền tảng App Engine thế hệ thứ hai (2018)
- So sánh nền tảng thế hệ thứ nhất và thứ hai
- Hỗ trợ dài hạn cho các thời gian chạy cũ
- Các mẫu di chuyển tài liệu
- Các mẫu di chuyển do cộng đồng đóng góp
SDK đám mây
- SDK Google Cloud
- Công cụ dòng lệnh
gcloudCloud SDK - Bật (và tắt) API của Google
- Trình quản lý API của bảng điều khiển Cloud (bật/tắt API)
- Bật các API của Google bằng
gcloud - Liệt kê các API của Google bằng
gcloud
Thông tin khác về Cloud
- Python trên Google Cloud
- Tài liệu về thư viện ứng dụng Python
- Kho lưu trữ thư viện ứng dụng Python
- Cấp"Luôn miễn phí"
- Cloud SDK
- Công cụ dòng lệnh
gcloudCloud SDK - Tất cả tài liệu của Google Cloud
Video
- Serverless Migration Station
- Expeditions không cần máy chủ
- Đăng ký theo dõi Google Cloud Tech
- Đăng ký theo dõi Google Developers
Giấy phép
Tác phẩm này được cấp phép theo giấy phép Ghi công theo Creative Commons 2.0 Chung.