ย้ายข้อมูลจากบริการผู้ใช้ App Engine ไปยัง Cloud Identity Platform (โมดูล 21)

1. ภาพรวม

ชุด Codelab ของ Serverless Migration Station (บทแนะนำแบบลงมือปฏิบัติจริงที่ทำตามได้ด้วยตนเอง) และวิดีโอที่เกี่ยวข้องมีจุดมุ่งหมายเพื่อช่วยให้นักพัฒนาแอป Google Cloud แบบไร้เซิร์ฟเวอร์ปรับปรุงแอปพลิเคชันให้ทันสมัยโดยแนะนำการย้ายข้อมูลอย่างน้อย 1 รายการ ซึ่งส่วนใหญ่เป็นการย้ายข้อมูลออกจากบริการเดิม การทำเช่นนี้จะทำให้แอปของคุณพกพาได้มากขึ้น และช่วยให้คุณมีตัวเลือกและความยืดหยุ่นมากขึ้น ซึ่งจะช่วยให้คุณผสานรวมและเข้าถึงผลิตภัณฑ์ระบบคลาวด์ที่หลากหลายยิ่งขึ้น รวมถึงอัปเกรดเป็นภาษาเวอร์ชันใหม่ๆ ได้ง่ายขึ้น แม้ว่าในตอนแรกจะมุ่งเน้นไปที่ผู้ใช้ Cloud รุ่นแรกๆ ซึ่งส่วนใหญ่เป็นนักพัฒนาซอฟต์แวร์ App Engine (สภาพแวดล้อมมาตรฐาน) แต่ชุดข้อมูลนี้ก็ครอบคลุมแพลตฟอร์มแบบไร้เซิร์ฟเวอร์อื่นๆ เช่น Cloud Functions และ Cloud Run หรือที่อื่นๆ หากเกี่ยวข้อง

วัตถุประสงค์ของ Codelab นี้คือการแสดงให้นักพัฒนาแอป App Engine ที่ใช้ Python 2 ทราบวิธีย้ายข้อมูลจาก App Engine Users API/service ไปยัง Cloud Identity Platform (GCIP) นอกจากนี้ ยังมีการย้ายข้อมูลโดยนัยจาก App Engine NDB ไปยัง Cloud NDB สำหรับการเข้าถึง Datastore (ซึ่งครอบคลุมอยู่ในโมดูลการย้ายข้อมูล 2 เป็นหลัก) รวมถึงการอัปเกรดเป็น Python 3

โมดูลที่ 20 จะครอบคลุมวิธีเพิ่มการใช้ Users API ลงในแอปตัวอย่างของโมดูลที่ 1 ในโมดูลนี้ คุณจะได้ใช้แอปโมดูลที่ 20 ที่เสร็จสมบูรณ์แล้วและย้ายข้อมูลการใช้งานไปยัง Cloud Identity Platform

คุณจะได้เรียนรู้วิธีต่อไปนี้

  • แทนที่การใช้บริการผู้ใช้ App Engine ด้วย Cloud Identity Platform
  • แทนที่การใช้ App Engine NDB ด้วย Cloud NDB (ดูโมดูลที่ 2 ด้วย)
  • ตั้งค่าผู้ให้บริการข้อมูลประจำตัวสำหรับการตรวจสอบสิทธิ์ที่แตกต่างกันโดยใช้ Firebase Auth
  • ใช้ Cloud Resource Manager API เพื่อรับข้อมูล IAM ของโปรเจ็กต์
  • ใช้ Firebase Admin SDK เพื่อรับข้อมูลผู้ใช้
  • พอร์ตแอปพลิเคชันตัวอย่างไปยัง Python 3

สิ่งที่คุณต้องมี

แบบสำรวจ

คุณจะใช้บทแนะนำนี้อย่างไร

อ่านอย่างเดียว อ่านและทำแบบฝึกหัดให้เสร็จ

คุณจะให้คะแนนประสบการณ์การใช้งาน Python เท่าใด

ผู้ฝึกหัด ขั้นกลาง ผู้ชำนาญ

คุณจะให้คะแนนประสบการณ์การใช้บริการ Google Cloud เท่าใด

ผู้ฝึกหัด ขั้นกลาง ผู้ชำนาญ

2. ฉากหลัง

บริการผู้ใช้ App Engine เป็นระบบการตรวจสอบสิทธิ์ผู้ใช้สำหรับแอป App Engine โดยจะให้บริการลงชื่อเข้าใช้ด้วย Google เป็นผู้ให้บริการข้อมูลประจำตัว ให้ลิงก์เข้าสู่ระบบและออกจากระบบที่สะดวกสำหรับใช้ในแอป และรองรับแนวคิดของผู้ใช้ที่ดูแลระบบและฟังก์ชันการทำงานสำหรับผู้ดูแลระบบเท่านั้น Google Cloud ขอแนะนำให้ย้ายข้อมูลจากบริการ App Engine แบบรวมรุ่นเดิมไปยังบริการแบบสแตนด์อโลนของ Cloud เช่น จากบริการผู้ใช้ไปยัง Cloud Identity Platform และอื่นๆ เพื่อปรับปรุงความสามารถในการพกพาของแอปพลิเคชัน

Identity Platform สร้างขึ้นโดยอิงตาม Firebase Authentication และเพิ่มฟีเจอร์ระดับองค์กรหลายอย่าง เช่น การตรวจสอบสิทธิ์แบบหลายปัจจัย, การรองรับ SSO ของ OIDC และ SAML, กลุ่มผู้ใช้หลายกลุ่ม, SLA 99.95% และอื่นๆ ความแตกต่างเหล่านี้ยังไฮไลต์ไว้ในหน้าเปรียบเทียบผลิตภัณฑ์ Identity Platform และการตรวจสอบสิทธิ์ Firebase ด้วย ทั้ง 2 ผลิตภัณฑ์มีฟีเจอร์มากกว่าฟังก์ชันการทำงานที่บริการผู้ใช้มีอย่างมาก

Codelab โมดูลที่ 21 นี้แสดงให้เห็นถึงการเปลี่ยนการตรวจสอบสิทธิ์ผู้ใช้ของแอปจากบริการผู้ใช้ไปยังฟีเจอร์ของ Identity Platform ที่สะท้อนฟังก์ชันการทำงานที่แสดงในโมดูลที่ 20 ได้ใกล้เคียงที่สุด โมดูลที่ 21 ยังมีการย้ายข้อมูลจาก App Engine NDB ไปยัง Cloud NDB สำหรับการเข้าถึง Datastore ซึ่งเป็นการย้ำการย้ายข้อมูลโมดูลที่ 2

แม้ว่าโค้ดของโมดูล 20 จะ "โฆษณา" ว่าเป็นแอปตัวอย่าง Python 2 แต่แหล่งที่มานั้นเข้ากันได้กับ Python 2 และ 3 และยังคงเป็นเช่นนั้นแม้หลังจากย้ายข้อมูลไปยัง Identity Platform (และ Cloud NDB) ในโมดูล 21 คุณสามารถใช้บริการผู้ใช้ต่อไปได้ในขณะที่อัปเกรดเป็น Python 3 เนื่องจากคุณเลือกได้ว่าจะย้ายข้อมูลไปยัง Identity Platform หรือไม่ ดูโค้ดแล็บโมดูล 17 และวิดีโอเพื่อดูวิธีใช้บริการที่รวมไว้ต่อไปขณะอัปเกรดเป็นรันไทม์รุ่นที่ 2 เช่น Python 3

บทแนะนำนี้มีขั้นตอนต่อไปนี้

  1. การตั้งค่า/การเตรียมการ
  2. อัปเดตการกำหนดค่า
  3. แก้ไขโค้ดแอปพลิเคชัน

3. การตั้งค่า/การเตรียมการ

ส่วนนี้จะอธิบายวิธี

  1. ตั้งค่าโปรเจ็กต์ Cloud
  2. รับแอปตัวอย่างพื้นฐาน
  3. (อีกครั้ง) นำไปใช้งานและตรวจสอบแอปพื้นฐาน
  4. เปิดใช้บริการ/API ใหม่ของ Google Cloud

ขั้นตอนเหล่านี้ช่วยให้คุณเริ่มต้นด้วยโค้ดที่ใช้งานได้ซึ่งพร้อมสำหรับการย้ายข้อมูลไปยังบริการ Cloud แบบสแตนด์อโลน

1. ตั้งค่าโปรเจ็กต์

หากคุณทำ Codelab โมดูลที่ 20 เสร็จแล้ว ให้ใช้โปรเจ็กต์ (และโค้ด) เดียวกันนั้นซ้ำ หรือจะสร้างโปรเจ็กต์ใหม่หรือนำโปรเจ็กต์อื่นที่มีอยู่มาใช้ซ้ำก็ได้ ตรวจสอบว่าโปรเจ็กต์มีบัญชีสำหรับการเรียกเก็บเงินที่ใช้งานอยู่และแอป App Engine ที่เปิดใช้ ค้นหารหัสโปรเจ็กต์และเตรียมไว้ให้พร้อมในระหว่างการทำ Codelab นี้ และใช้รหัสโปรเจ็กต์เมื่อใดก็ตามที่คุณพบตัวแปร PROJ_ID

2. รับแอปตัวอย่างพื้นฐาน

ข้อกำหนดเบื้องต้นอย่างหนึ่งคือแอป App Engine ของโมดูล 20 ที่ใช้งานได้ ดังนั้นให้ทำตาม Codelab (แนะนำ ลิงก์ด้านบน) หรือคัดลอกโค้ดโมดูล 20 จากที่เก็บ ไม่ว่าคุณจะใช้ของคุณเองหรือของเรา เราจะเริ่มจากตรงนี้ ("START") Codelab นี้จะแนะนำขั้นตอนการย้ายข้อมูลให้คุณ โดยจะจบด้วยโค้ดที่คล้ายกับโค้ดในโฟลเดอร์ที่เก็บข้อมูลของโมดูล 21 ("FINISH")

คัดลอกโฟลเดอร์ที่เก็บข้อมูลของโมดูล 20 โดยควรมีลักษณะเหมือนเอาต์พุตด้านล่าง และอาจมีโฟลเดอร์ lib หากคุณทำ Codelab โมดูลที่ 20

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

3. (อีกครั้ง) นำไปใช้งานและตรวจสอบแอปพื้นฐาน

ทำตามขั้นตอนต่อไปนี้เพื่อติดตั้งใช้งานแอป Module 20

  1. ลบโฟลเดอร์ lib หากมี แล้วเรียกใช้ pip install -t lib -r requirements.txt เพื่อสร้างโฟลเดอร์ใหม่ คุณอาจต้องใช้ pip2 หากติดตั้งทั้ง Python 2 และ 3
  2. ตรวจสอบว่าคุณได้ติดตั้งและเริ่มต้นเครื่องมือบรรทัดคำสั่ง gcloud รวมถึงได้ตรวจสอบการใช้งานแล้ว
  3. หากไม่ต้องการป้อน PROJ_ID ทุกครั้งที่ออกคำสั่ง gcloud ให้ตั้งค่าโปรเจ็กต์ Cloud ด้วย gcloud config set project PROJ_ID ก่อน
  4. ทำให้แอปตัวอย่างใช้งานได้ด้วย gcloud app deploy
  5. ยืนยันว่าแอปทำงานได้ตามที่คาดไว้โดยไม่มีข้อผิดพลาด หากคุณทำ Codelab โมดูลที่ 20 เสร็จแล้ว แอปจะแสดงข้อมูลการเข้าสู่ระบบของผู้ใช้ (อีเมลผู้ใช้ "ป้ายผู้ดูแลระบบ" ที่เป็นไปได้ และปุ่มเข้าสู่ระบบ/ออกจากระบบ) ที่ด้านบนพร้อมกับการเข้าชมล่าสุด (ดังภาพด้านล่าง)

907e64c19ef964f8.png

การลงชื่อเข้าใช้ในฐานะผู้ใช้ทั่วไปจะทําให้อีเมลของผู้ใช้ปรากฏขึ้น และปุ่ม "เข้าสู่ระบบ" จะเปลี่ยนเป็นปุ่ม "ออกจากระบบ"

ad7b59916b69a035.png

การลงชื่อเข้าใช้ในฐานะผู้ใช้ที่เป็นผู้ดูแลระบบจะทำให้อีเมลของผู้ใช้แสดงพร้อมกับ "(ผู้ดูแลระบบ)" ข้างอีเมล ดังนี้

867bcb3334149e4.png

4. เปิดใช้ Google Cloud APIs/บริการใหม่

บทนำ

แอป Module 20 ใช้ App Engine NDB และ Users API ซึ่งเป็นบริการแบบรวมที่ไม่ต้องตั้งค่าเพิ่มเติม แต่บริการ Cloud แบบสแตนด์อโลนต้องตั้งค่า และแอปที่อัปเดตจะใช้ทั้ง Cloud Identity Platform และ Cloud Datastore (ผ่านไลบรารีของไคลเอ็นต์ Cloud NDB) นอกจากนี้ ความจำเป็นในการระบุผู้ใช้ที่เป็นผู้ดูแลระบบ App Engine ยังกำหนดให้ต้องใช้ Cloud Resource Manager API ด้วย

ค่าใช้จ่าย

  • App Engine และ Cloud Datastore มีโควต้าระดับ "ฟรีตลอดเวลา" และตราบใดที่คุณยังคงอยู่ภายใต้ขีดจำกัดเหล่านั้น คุณก็จะไม่ถูกเรียกเก็บเงินเมื่อทำตามบทแนะนำนี้จนเสร็จ ดูรายละเอียดเพิ่มเติมได้ที่หน้าราคา App Engine และหน้าราคา Cloud Datastore
  • ระบบจะเรียกเก็บเงินสำหรับการใช้แพลตฟอร์ม Cloud Identity โดยขึ้นอยู่กับจำนวนผู้ใช้ที่ใช้งานอยู่รายเดือน (MAU) หรือการยืนยันการตรวจสอบสิทธิ์ และมีเวอร์ชัน "ฟรี" สำหรับรูปแบบการใช้งานแต่ละรูปแบบ ดูรายละเอียดเพิ่มเติมได้ในหน้าราคา นอกจากนี้ แม้ว่า App Engine และ Cloud Datastore จะต้องมีการเรียกเก็บเงิน แต่การใช้ GCIP เพียงอย่างเดียวไม่จำเป็นต้องเปิดใช้การเรียกเก็บเงินตราบใดที่คุณไม่เกินโควต้าประจำวันแบบไม่ต้องใช้เครื่องมือ ดังนั้นโปรดพิจารณาเรื่องนี้สำหรับโปรเจ็กต์ Cloud ที่ไม่ได้เกี่ยวข้องกับ Cloud API/บริการที่ต้องมีการเรียกเก็บเงิน
  • การใช้ Cloud Resource Manager API ส่วนใหญ่ไม่มีค่าใช้จ่ายตามหน้าการกำหนดราคา

ผู้ใช้จะเปิดใช้ Cloud API ได้จากคอนโซล Cloud หรือจากบรรทัดคำสั่ง (ผ่านคำสั่ง gcloud ซึ่งเป็นส่วนหนึ่งของ Cloud SDK) ทั้งนี้ขึ้นอยู่กับความต้องการของคุณ มาเริ่มกันที่ Cloud Datastore และ Cloud Resource Manager API

จาก Cloud Console

ไปที่หน้าคลังของ API Manager (สำหรับโปรเจ็กต์ที่ถูกต้อง) ใน Cloud Console แล้วค้นหา API โดยใช้แถบค้นหา c7a740304e9d35b.png

เปิดใช้ API ต่อไปนี้

ค้นหาและคลิกปุ่มเปิดใช้สำหรับแต่ละ API แยกกัน ระบบอาจแจ้งให้คุณระบุข้อมูลการเรียกเก็บเงิน ตัวอย่างเช่น นี่คือหน้าสำหรับ Resource Manager API

fc7bd8f4c49d12e5.png

ปุ่มจะเปลี่ยนเป็น "จัดการ" เมื่อเปิดใช้แล้ว (โดยปกติจะใช้เวลา 2-3 วินาที)

8eca12d6cc7b45b0.png

เปิดใช้ Cloud Datastore ในลักษณะเดียวกัน

83811599b110e46b.png

จากบรรทัดคำสั่ง

แม้ว่าการเปิดใช้ API จากคอนโซลจะให้ข้อมูลที่มองเห็นได้ แต่บางคนก็ชอบใช้บรรทัดคำสั่งมากกว่า นอกจากนี้ คุณยังเปิดใช้ API ได้พร้อมกันหลายรายการอีกด้วย ออกคำสั่งนี้เพื่อเปิดใช้ทั้ง Cloud Datastore และ Cloud Resource Manager API แล้วรอให้การดำเนินการเสร็จสมบูรณ์ ดังที่แสดงไว้ที่นี่

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

ระบบอาจแจ้งให้คุณระบุข้อมูลสำหรับการเรียกเก็บเงิน

"URL" สำหรับ API แต่ละรายการที่ใช้ในคำสั่งด้านบนเรียกว่าชื่อบริการของ API และจะอยู่ที่ด้านล่างของหน้าไลบรารีสำหรับ API แต่ละรายการ หากต้องการเปิดใช้ Cloud API อื่นๆ สำหรับแอปของคุณเอง คุณสามารถดูชื่อบริการที่เกี่ยวข้องได้ในหน้า API ที่เกี่ยวข้อง คำสั่งนี้จะแสดงชื่อบริการทั้งหมดสำหรับ API ที่คุณเปิดใช้ได้

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

ไม่ว่าจะอยู่ใน Cloud Console หรือในบรรทัดคำสั่ง เมื่อทำตามขั้นตอนข้างต้นเสร็จแล้ว ตัวอย่างของเราจะเข้าถึง API เหล่านั้นได้ ขั้นตอนถัดไปคือการเปิดใช้แพลตฟอร์ม Cloud Identity และทำการเปลี่ยนแปลงโค้ดที่จำเป็น

เปิดใช้และตั้งค่า Cloud Identity Platform (คอนโซลระบบคลาวด์เท่านั้น)

Cloud Identity Platform เป็นบริการใน Marketplace เนื่องจากเชื่อมต่อหรือขึ้นอยู่กับทรัพยากรภายนอก Google Cloud เช่น การตรวจสอบสิทธิ์ Firebase ปัจจุบันคุณเปิดใช้บริการ Marketplace ได้จาก Cloud Console เท่านั้น โดยทำตามขั้นตอนต่อไปนี้

  1. ไปที่หน้าแพลตฟอร์ม Cloud Identity ใน Cloud Marketplace แล้วคลิกปุ่มเปิดใช้ อัปเกรดจาก Firebase Authentication หากได้รับแจ้ง ซึ่งจะช่วยให้คุณใช้ฟีเจอร์เพิ่มเติมได้ เช่น ฟีเจอร์ที่อธิบายไว้ก่อนหน้านี้ในส่วนข้อมูลพื้นฐาน นี่คือหน้า Marketplace ที่ไฮไลต์ปุ่มเปิดใช้ 28475f1c9b29de69.png
  2. เมื่อเปิดใช้ Identity Platform แล้ว ระบบอาจนำคุณไปยังหน้าผู้ให้บริการข้อมูลประจำตัวโดยอัตโนมัติ หากไม่พบ ให้ใช้ลิงก์ที่สะดวกนี้เพื่อไปยังหน้าดังกล่าว fc2d92d42a5d1dd7.png
  3. เปิดใช้ผู้ให้บริการ Google Auth หากยังไม่ได้ตั้งค่าผู้ให้บริการ ให้คลิกเพิ่มผู้ให้บริการ แล้วเลือก Google เมื่อกลับมาที่หน้าจอนี้ คุณควรเปิดใช้รายการ Google Google เป็นผู้ให้บริการตรวจสอบสิทธิ์เพียงรายเดียวที่เราใช้ในบทแนะนำนี้เพื่อจำลองบริการผู้ใช้ App Engine เป็นบริการลงชื่อเข้าใช้ด้วย Google แบบเบา ในแอปของคุณเอง คุณสามารถเปิดใช้ผู้ให้บริการการตรวจสอบสิทธิ์เพิ่มเติมได้
  4. เมื่อเลือกและตั้งค่า Google และผู้ให้บริการตรวจสอบสิทธิ์อื่นๆ ที่ต้องการแล้ว ให้คลิกรายละเอียดการตั้งค่าแอปพลิเคชัน จากนั้นคัดลอก apiKey และ authDomain ในออบเจ็กต์ config ในแท็บเว็บจากหน้าต่างกล่องโต้ตอบการตรวจสอบ แล้วบันทึกทั้ง 2 รายการไว้ในที่ที่ปลอดภัย ทำไมไม่คัดลอกทั้งหมด ข้อมูลโค้ดในกล่องโต้ตอบนี้มีการฮาร์ดโค้ดและระบุวันที่ไว้ ดังนั้นเพียงบันทึกส่วนที่สำคัญที่สุดและใช้ในโค้ดของเราพร้อมกับการใช้งาน Firebase Auth แบบพร้อมกันมากขึ้น เมื่อคัดลอกค่าและบันทึกไว้ในที่ที่ปลอดภัยแล้ว ให้คลิกปุ่มปิดเพื่อตั้งค่าที่จำเป็นทั้งหมดให้เสร็จสมบูรณ์ bbb09dcdd9be538e.png

4. อัปเดตการกำหนดค่า

การอัปเดตในการกำหนดค่ารวมถึงการเปลี่ยนแปลงไฟล์การกำหนดค่าต่างๆ รวมถึงการสร้างสิ่งที่เทียบเท่ากับ App Engine แต่ภายในระบบนิเวศของ Cloud Identity Platform

appengine_config.py

  • หากอัปเกรดเป็น Python 3 ให้ลบ appengine_config.py
  • หากวางแผนที่จะปรับปรุงเป็น Identity Platform แต่ยังคงใช้ Python 2 อยู่ อย่าลบไฟล์ แต่เราจะอัปเดตในภายหลังระหว่างการย้อนพอร์ต Python 2

requirements.txt

ไฟล์ requirements.txt ของโมดูล 20 แสดงเฉพาะ Flask สำหรับโมดูล 21 ให้เพิ่มแพ็กเกจต่อไปนี้

ตอนนี้เนื้อหาของ requirements.txt ควรมีลักษณะดังนี้

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

app.yaml

  • การอัปเกรดเป็น Python 3 หมายถึงการทำให้ไฟล์ app.yaml ง่ายขึ้น นำทุกอย่างออก ยกเว้นคำสั่งรันไทม์ แล้วตั้งค่าเป็น Python 3 เวอร์ชันที่รองรับในปัจจุบัน ปัจจุบันตัวอย่างใช้เวอร์ชัน 3.10
  • หากคุณยังคงใช้ Python 2 อยู่ โปรดอย่าดำเนินการใดๆ ที่นี่

ก่อน:

runtime: python27
threadsafe: yes
api_version: 1

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

แอปตัวอย่างของโมดูล 20 ไม่มีตัวแฮนเดิลไฟล์แบบคงที่ หากแอปของคุณทำเช่นนั้น ให้ปล่อยไว้ตามเดิม คุณสามารถนำตัวแฮนเดิลสคริปต์ทั้งหมดออกได้หากต้องการ หรือจะปล่อยไว้เพื่อใช้อ้างอิงก็ได้ ตราบใดที่คุณเปลี่ยนแฮนเดิลเป็น auto ตามที่อธิบายไว้ในapp.yaml คู่มือการย้ายข้อมูล การเปลี่ยนแปลงเหล่านี้จะช่วยให้app.yaml ที่อัปเดตแล้วสำหรับ Python 3 ง่ายขึ้น ดังนี้

หลังจากนั้น

runtime: python310

การอัปเดตการกำหนดค่าอื่นๆ

ไม่ว่าจะใช้ Python 2 ต่อไปหรือพอร์ตเป็น Python 3 หากคุณมีโฟลเดอร์ lib ให้ลบโฟลเดอร์ดังกล่าว

5. แก้ไขโค้ดแอปพลิเคชัน

ส่วนนี้มีการอัปเดตไฟล์แอปพลิเคชันหลัก main.py โดยแทนที่การใช้บริการผู้ใช้ App Engine ด้วย Cloud Identity Platform หลังจากอัปเดตแอปพลิเคชันหลักแล้ว คุณจะต้องอัปเดตเทมเพลตเว็บ templates/index.html

อัปเดตการนำเข้าและการเริ่มต้น

ทำตามขั้นตอนด้านล่างเพื่ออัปเดตการนำเข้าและเริ่มต้นทรัพยากรของแอปพลิเคชัน

  1. สำหรับการนำเข้า ให้แทนที่ App Engine NDB ด้วย Cloud NDB
  2. นอกจาก Cloud NDB แล้ว ให้นำเข้า Cloud Resource Manager ด้วย
  3. Identity Platform สร้างขึ้นจาก Firebase Auth ดังนั้นให้คุณนําเข้า Firebase Admin SDK
  4. Cloud API กำหนดให้ใช้ไคลเอ็นต์ API ดังนั้นให้เริ่มต้นใช้งานสำหรับ Cloud NDB ที่ด้านล่างการเริ่มต้น Flask

แม้ว่าจะนำเข้าแพ็กเกจ Cloud Resource Manager ที่นี่ แต่เราจะใช้แพ็กเกจนี้ในขั้นตอนต่อๆ ไปในการเริ่มต้นแอป ด้านล่างนี้คือการนำเข้าและการเริ่มต้นจากโมดูล 20 ตามด้วยลักษณะที่ส่วนต่างๆ ควรเป็นหลังจากใช้การเปลี่ยนแปลงข้างต้น

ก่อน:

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

app = Flask(__name__)

หลังจากนั้น

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

การสนับสนุนสำหรับผู้ใช้ที่เป็นผู้ดูแลระบบ App Engine

มี 2 องค์ประกอบที่ต้องเพิ่มลงในแอปที่รองรับการจดจำผู้ใช้ที่เป็นผู้ดูแลระบบ

  • _get_gae_admins() — รวบรวมชุดผู้ใช้ที่เป็นผู้ดูแลระบบ เรียกใช้ครั้งเดียวและบันทึก
  • is_admin() — ตรวจสอบว่าผู้ใช้ที่ลงชื่อเข้าใช้เป็นผู้ใช้ที่เป็นผู้ดูแลระบบหรือไม่ โดยจะเรียกใช้เมื่อผู้ใช้เข้าสู่ระบบ

ฟังก์ชันยูทิลิตี _get_gae_admins() จะเรียกใช้ Resource Manager API เพื่อดึงข้อมูลนโยบายอนุญาตของ Cloud IAM ปัจจุบัน นโยบายอนุญาตจะกำหนดและบังคับใช้บทบาทที่มอบให้กับผู้ใช้หลัก (ผู้ใช้ที่เป็นบุคคล บัญชีบริการ ฯลฯ) การตั้งค่าประกอบด้วยรายการต่อไปนี้

  • การดึงข้อมูลรหัสโปรเจ็กต์ที่อยู่ในระบบคลาวด์ (PROJ_ID)
  • การสร้างไคลเอ็นต์ Resource Manager API (rm_client)
  • การสร้างชุดบทบาทผู้ดูแลระบบ App Engine (อ่านอย่างเดียว) (_TARGETS)

Resource Manager ต้องใช้รหัสโปรเจ็กต์ Cloud ดังนั้นให้นำเข้า google.auth.default() และเรียกใช้ฟังก์ชันดังกล่าวเพื่อรับรหัสโปรเจ็กต์ การเรียกนั้นมีพารามิเตอร์ที่ดูเหมือน URL แต่เป็นขอบเขตสิทธิ์ OAuth2 เมื่อเรียกใช้แอปในระบบคลาวด์ เช่น ใน VM ของ Compute Engine หรือแอป App Engine ระบบจะจัดเตรียมบัญชีบริการเริ่มต้นที่มีสิทธิ์ในวงกว้างให้ เราขอแนะนำให้สร้างบัญชีบริการที่ผู้ใช้จัดการของคุณเองตามแนวทางปฏิบัติที่ดีที่สุดในการให้สิทธิ์ขั้นต่ำ

สําหรับการเรียก API คุณควรลดขอบเขตของแอปให้เหลือระดับต่ำสุดที่จําเป็นต่อการทํางานอย่างถูกต้อง การเรียก Resource Manager API ที่เราจะทำคือ get_iam_policy() ซึ่งต้องมีขอบเขตอย่างใดอย่างหนึ่งต่อไปนี้จึงจะดำเนินการได้

  • 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

แอปตัวอย่างต้องการสิทธิ์การเข้าถึงแบบอ่านอย่างเดียวสำหรับนโยบายการอนุญาตเท่านั้น โดยจะไม่แก้ไขนโยบายและไม่จำเป็นต้องมีสิทธิ์เข้าถึงทั้งโปรเจ็กต์ ซึ่งหมายความว่าแอปไม่จำเป็นต้องใช้สิทธิ์ 3 รายการแรก โดยเราจะใช้เฉพาะตัวเลือกสุดท้าย ซึ่งเป็นตัวเลือกที่เราจะใช้กับแอปตัวอย่าง

ส่วนหลักของฟังก์ชันจะสร้างชุดผู้ใช้ดูแลระบบที่ว่างเปล่า (admins) ดึงข้อมูล allow_policy ผ่าน get_iam_policy() และวนซ้ำผ่านการเชื่อมโยงทั้งหมดเพื่อค้นหาบทบาทผู้ดูแลระบบ App Engine โดยเฉพาะ

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

สำหรับแต่ละบทบาทเป้าหมายที่พบ ระบบจะรวบรวมผู้ใช้ที่อยู่ในบทบาทนั้นๆ และเพิ่มผู้ใช้เหล่านั้นลงในชุดผู้ใช้ดูแลระบบโดยรวม โดยจะสิ้นสุดด้วยการแสดงผู้ใช้ที่เป็นผู้ดูแลระบบทั้งหมดที่พบและแคชไว้เป็นค่าคงที่ (_ADMINS) ตลอดอายุของอินสแตนซ์ App Engine นี้ เราจะเห็นการโทรนั้นในอีกสักครู่

เพิ่ม_get_gae_admins()คำจำกัดความฟังก์ชันต่อไปนี้ลงใน main.py ใต้การเริ่มต้นไคลเอ็นต์ 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

เมื่อผู้ใช้เข้าสู่ระบบแอป ระบบจะดำเนินการต่อไปนี้

  1. ระบบจะตรวจสอบอย่างรวดเร็วจากเทมเพลตเว็บหลังจากที่ผู้ใช้ลงชื่อเข้าใช้ Firebase
  2. เมื่อสถานะการให้สิทธิ์เปลี่ยนแปลงในเทมเพลต ระบบจะเรียกใช้ fetch() รูปแบบ Ajax ไปยัง /is_admin ซึ่งแฮนเดิลเลอร์คือฟังก์ชันถัดไป is_admin()
  3. ระบบจะส่งโทเค็นรหัส Firebase ในเนื้อหา POST ไปยัง is_admin() ซึ่งจะดึงโทเค็นออกจากส่วนหัวและเรียกใช้ Firebase Admin SDK เพื่อตรวจสอบความถูกต้อง หากเป็นผู้ใช้ที่ถูกต้อง ให้ดึงอีเมลของผู้ใช้และตรวจสอบว่าเป็นผู้ใช้ที่ดูแลระบบหรือไม่
  4. จากนั้นระบบจะส่งผลลัพธ์บูลีนกลับไปยังเทมเพลตเป็น 200 ที่สำเร็จ

เพิ่ม is_admin() ลงใน main.py หลัง _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

โค้ดทั้งหมดจากทั้ง 2 ฟังก์ชันจำเป็นต่อการจำลองฟังก์ชันการทำงานที่มีให้บริการจากบริการ Users โดยเฉพาะฟังก์ชัน is_current_user_admin() การเรียกใช้ฟังก์ชันนี้ในโมดูล 20 ทำงานหนักทั้งหมด ซึ่งแตกต่างจากโมดูล 21 ที่เราใช้โซลูชันการแทนที่ ข่าวดีคือตอนนี้แอปไม่ได้ขึ้นอยู่กับบริการ App Engine อย่างเดียวแล้ว ซึ่งหมายความว่าคุณสามารถย้ายแอปไปยัง Cloud Run หรือบริการอื่นๆ ได้ นอกจากนี้ คุณยังเปลี่ยนคำจำกัดความของ "ผู้ใช้ที่เป็นผู้ดูแลระบบ" สำหรับแอปของคุณเองได้เพียงแค่เปลี่ยนไปใช้บทบาทที่ต้องการใน _TARGETS ในขณะที่บริการผู้ใช้จะมีการฮาร์ดโค้ดสำหรับบทบาทผู้ดูแลระบบ App Engine

เริ่มต้น Firebase Auth และแคชผู้ใช้ที่ดูแลระบบ App Engine

เราสามารถเริ่มต้น Firebase Auth ที่ด้านบนใกล้กับจุดเดียวกับที่เริ่มต้นแอป Flask และสร้างไคลเอ็นต์ Cloud NDB API ได้ แต่ไม่จำเป็นต้องทำจนกว่าจะมีการกำหนดโค้ดผู้ดูแลระบบทั้งหมด ซึ่งเป็นจุดที่เราอยู่ตอนนี้ ในทำนองเดียวกัน เมื่อกำหนด _get_gae_admins() แล้ว ให้เรียกใช้เพื่อแคชรายชื่อผู้ใช้ที่เป็นผู้ดูแลระบบ

เพิ่มบรรทัดต่อไปนี้ใต้ส่วนเนื้อหาของฟังก์ชัน is_admin()

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

การอัปเดตโมเดลข้อมูลการเข้าชม

โมเดลข้อมูล Visit จะไม่เปลี่ยนแปลง การเข้าถึง Datastore ต้องใช้เครื่องมือจัดการบริบทไคลเอ็นต์ Cloud NDB API อย่างชัดเจน ds_client.context() ในโค้ด หมายความว่าคุณต้องรวมการเรียก Datastore ไว้ทั้งใน store_visit() และ fetch_visits() ภายในบล็อก with ของ Python การอัปเดตนี้เหมือนกับโมดูลที่ 2 ทำการเปลี่ยนแปลงดังนี้

ก่อน:

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)

หลังจากนั้น

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)

ย้ายตรรกะการเข้าสู่ระบบของผู้ใช้ไปยังเทมเพลตเว็บ

บริการผู้ใช้ App Engine อยู่ฝั่งเซิร์ฟเวอร์ ในขณะที่ Firebase Auth และ Cloud Identity Platform ส่วนใหญ่อยู่ฝั่งไคลเอ็นต์ ด้วยเหตุนี้ โค้ดการจัดการผู้ใช้ส่วนใหญ่ในแอปโมดูล 20 จึงย้ายไปอยู่ในเทมเพลตเว็บของโมดูล 21

ใน main.py บริบทเว็บจะส่งข้อมูลที่จำเป็น 5 รายการไปยังเทมเพลต โดย 4 รายการแรกที่ระบุไว้จะเชื่อมโยงกับการจัดการผู้ใช้และจะแตกต่างกันไปขึ้นอยู่กับว่าผู้ใช้ลงชื่อเข้าใช้หรือไม่

  • who - อีเมลของผู้ใช้หากลงชื่อเข้าใช้ หรือ user ในกรณีอื่นๆ
  • admin — ป้าย(ผู้ดูแลระบบ) หากผู้ใช้ที่ลงชื่อเข้าใช้เป็นผู้ดูแลระบบ
  • sign — แสดงปุ่มเข้าสู่ระบบหรือออกจากระบบ
  • link — ลิงก์ลงชื่อเข้าใช้หรือออกจากระบบเมื่อคลิกปุ่ม
  • visits - การเข้าชมล่าสุด

ก่อน:

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

การจัดการผู้ใช้ทั้งหมดจะย้ายไปอยู่ที่เทมเพลตเว็บ ดังนั้นเราจึงเหลือเพียงการเข้าชม ซึ่งจะนำตัวแฮนเดิลหลักกลับไปเป็นสิ่งที่เรามีในแอปโมดูลที่ 1

หลังจากนั้น

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

อัปเดตเทมเพลตเว็บ

การอัปเดตทั้งหมดจากส่วนก่อนหน้ามีลักษณะอย่างไรในเทมเพลต โดยหลักๆ แล้วคือการย้ายการจัดการผู้ใช้จากแอปไปยัง Firebase Auth ที่ทำงานในเทมเพลต และการพอร์ตโค้ดทั้งหมดที่เราย้ายไปยัง JavaScript บางส่วน เราเห็นว่า main.py ลดลงค่อนข้างมาก ดังนั้นคาดว่า templates/index.html จะมีการเติบโตที่คล้ายกัน

ก่อน:

<!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>

แทนที่เทมเพลตเว็บทั้งหมดด้วยเนื้อหาด้านล่าง

หลังจากนั้น

<!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>

เนื้อหา HTML นี้มีคอมโพเนนต์หลายอย่าง ดังนั้นเรามาดูทีละส่วนกัน

การนำเข้า Firebase

ขณะที่ยังอยู่ในส่วนหัวของเอกสาร HTML ให้นำเข้าคอมโพเนนต์ Firebase ที่จำเป็นเมื่อผ่านชื่อหน้า ตอนนี้เราได้แบ่งคอมโพเนนต์ของ Firebase ออกเป็นหลายโมดูลเพื่อประสิทธิภาพ โค้ดเพื่อเริ่มต้น Firebase จะนำเข้าจากโมดูลแอป Firebase หลัก ขณะที่ฟังก์ชันที่จัดการการตรวจสอบสิทธิ์ Firebase, Google ในฐานะผู้ให้บริการตรวจสอบสิทธิ์, การลงชื่อเข้าใช้และออกจากระบบ รวมถึง "การเรียกกลับ" การเปลี่ยนแปลงสถานะการตรวจสอบสิทธิ์จะนำเข้าจากโมดูล Firebase Auth ทั้งหมด

<!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

ก่อนหน้านี้ในส่วนการตั้งค่า Identity Platform ของบทแนะนำนี้ คุณได้บันทึก apiKey และ authDomain จากกล่องโต้ตอบรายละเอียดการตั้งค่าแอปพลิเคชัน เพิ่มค่าเหล่านั้นลงในตัวแปร firebaseConfig ในส่วนถัดไปนี้ ลิงก์ไปยังวิธีการโดยละเอียดเพิ่มเติมจะอยู่ในความคิดเห็น

// 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",
};

การเริ่มต้น Firebase

ส่วนถัดไปจะเริ่มต้น Firebase ด้วยข้อมูลการกำหนดค่านี้

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

ซึ่งจะกำหนดความสามารถในการใช้ Google เป็นผู้ให้บริการตรวจสอบสิทธิ์ และมีตัวเลือกที่ถูกแสดงความคิดเห็นไว้สำหรับการแสดงตัวเลือกบัญชีแม้ว่าจะมีบัญชี Google ที่ลงทะเบียนไว้ในเซสชันเบราว์เซอร์เพียงบัญชีเดียวก็ตาม กล่าวคือ เมื่อมีหลายบัญชี คุณจะเห็น "ตัวเลือกบัญชี" นี้ตามที่คาดไว้ a38369389b7c4c7e.png อย่างไรก็ตาม หากมีผู้ใช้เพียงคนเดียวในเซสชัน กระบวนการเข้าสู่ระบบจะเสร็จสมบูรณ์โดยอัตโนมัติโดยไม่ต้องมีการโต้ตอบจากผู้ใช้ (ป๊อปอัปจะปรากฏขึ้นแล้วหายไป) คุณสามารถบังคับให้กล่องโต้ตอบตัวเลือกบัญชีแสดงต่อผู้ใช้ 1 ราย (เทียบกับการเข้าสู่ระบบแอปทันที) ได้โดยยกเลิกการแสดงความคิดเห็นในบรรทัดพารามิเตอร์ที่กำหนดเอง หากเปิดใช้ แม้แต่การเข้าสู่ระบบของผู้ใช้คนเดียวก็จะแสดงตัวเลือกบัญชี: b75624cb68d94557.png

ฟังก์ชันเข้าสู่ระบบและออกจากระบบ

บรรทัดโค้ดถัดไปจะสร้างฟังก์ชันสำหรับการคลิกปุ่มเข้าสู่ระบบหรือออกจากระบบ

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

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

การดำเนินการลงชื่อเข้าใช้และออกจากระบบ

ส่วนหลักสุดท้ายในบล็อก <script> นี้คือฟังก์ชันที่เรียกใช้สำหรับการเปลี่ยนแปลงการให้สิทธิ์ทุกครั้ง (ลงชื่อเข้าใช้หรือลงชื่อออก)

// 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>

โค้ดในโมดูล 20 ที่กำหนดว่าจะส่งบริบทเทมเพลต "ผู้ใช้เข้าสู่ระบบแล้ว" หรือบริบท "ผู้ใช้ออกจากระบบแล้ว" จะได้รับการเปลี่ยนที่นี่ เงื่อนไขที่ด้านบนจะส่งผลให้เป็น true หากผู้ใช้เข้าสู่ระบบสำเร็จ ซึ่งจะทริกเกอร์การดำเนินการต่อไปนี้

  1. ระบบจะตั้งค่าอีเมลของผู้ใช้ให้แสดง
  2. ปุ่มเข้าสู่ระบบจะเปลี่ยนเป็นออกจากระบบ
  3. ระบบจะเรียกใช้ /is_admin ในรูปแบบ Ajax เพื่อพิจารณาว่าจะแสดงป้ายผู้ใช้ดูแลระบบ (admin) หรือไม่

เมื่อผู้ใช้ออกจากระบบ ระบบจะเรียกใช้ข้อกำหนด else เพื่อรีเซ็ตข้อมูลผู้ใช้ทั้งหมด

  1. ตั้งชื่อผู้ใช้เป็น user
  2. นำป้ายผู้ดูแลระบบออก
  3. เปลี่ยนปุ่มออกจากระบบกลับเป็นเข้าสู่ระบบ

ตัวแปรเทมเพลต

หลังจากส่วนหัวสิ้นสุด เนื้อหาหลักจะเริ่มต้นด้วยตัวแปรเทมเพลตที่แทนที่ด้วยองค์ประกอบ HTML ซึ่งจะเปลี่ยนแปลงตามความจำเป็น

  1. ชื่อผู้ใช้ที่แสดง
  2. ป้ายผู้ดูแลระบบ (admin) (หากมี)
  3. ปุ่มเข้าสู่ระบบหรือออกจากระบบ
<body>
<p>
Welcome, <span id="who"></span> <span id="admin"><code>(admin)</code></span>
<button id="logbtn"></button>
</p><hr>

การเข้าชมล่าสุดและตัวแปรองค์ประกอบ HTML

โค้ดการเข้าชมล่าสุดจะไม่เปลี่ยนแปลง และบล็อกสุดท้าย <script> จะตั้งค่าตัวแปรสำหรับองค์ประกอบ HTML ที่เปลี่ยนแปลงสำหรับการลงชื่อเข้าใช้และลงชื่อออกที่แสดงไว้ด้านบน

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

การดำเนินการนี้เป็นการสิ้นสุดการเปลี่ยนแปลงที่จำเป็นในแอปพลิเคชันและเทมเพลตเว็บเพื่อเปลี่ยนจาก App Engine NDB และ Users API ไปเป็น Cloud NDB และ Identity Platform รวมถึงอัปเกรดเป็น Python 3 ขอแสดงความยินดีที่มาถึงแอปตัวอย่างโมดูล 21 ใหม่ คุณสามารถดูเวอร์ชันของเราได้ในโฟลเดอร์ที่เก็บโมดูล 21b

ส่วนถัดไปของโค้ดแล็บเป็นส่วนที่ไม่บังคับ (*) และมีไว้สำหรับผู้ใช้ที่แอปต้องยังคงใช้ Python 2 เท่านั้น โดยจะแนะนำขั้นตอนที่จำเป็นเพื่อให้ได้แอป Python 2 Module 21 ที่ใช้งานได้

6. *การพอร์ตย้อนกลับของ Python 2

ส่วนนี้เป็นส่วนที่ไม่บังคับสำหรับนักพัฒนาแอปที่ทำการย้ายข้อมูล Identity Platform แต่ต้องเรียกใช้ในรันไทม์ Python 2 ต่อไป หากคุณไม่กังวลเรื่องนี้ ให้ข้ามส่วนนี้

หากต้องการสร้างแอปโมดูล 21 เวอร์ชัน Python 2 ที่ใช้งานได้ คุณต้องมีสิ่งต่อไปนี้

  1. ข้อกำหนดของรันไทม์: ไฟล์การกำหนดค่าที่รองรับ Python 2 และการเปลี่ยนแปลงที่จำเป็นในแอปพลิเคชันหลักเพื่อหลีกเลี่ยงปัญหาความไม่เข้ากันของ Python 3
  2. การเปลี่ยนแปลงไลบรารีย่อย: เราเลิกใช้งาน Python 2 ก่อนที่จะเพิ่มฟีเจอร์ที่จำเป็นบางอย่างลงในไลบรารีของไคลเอ็นต์ Resource Manager ด้วยเหตุนี้ คุณจึงต้องมีวิธีอื่นในการเข้าถึงฟังก์ชันการทำงานที่ขาดหายไป

มาทำตามขั้นตอนเหล่านั้นกันเลย โดยเริ่มจากการกำหนดค่า

กู้คืน appengine_config.py

ก่อนหน้านี้ในบทแนะนำนี้ เราได้แนะนำให้คุณลบ appengine_config.py เนื่องจากรันไทม์ Python 3 App Engine ไม่ได้ใช้ สำหรับ Python 2 ไม่เพียงแต่ต้องเก็บรักษาไว้เท่านั้น แต่ยังต้องอัปเดตโมดูล 20 appengine_config.py เพื่อรองรับการใช้ไลบรารีของบุคคลที่สามในตัว ซึ่งได้แก่ grpcio และ setuptools คุณต้องใช้แพ็กเกจเหล่านั้นทุกครั้งที่แอป App Engine ใช้ไลบรารีของไคลเอ็นต์ Cloud เช่น ไลบรารีสำหรับ Cloud NDB และ Cloud Resource Manager

คุณจะเพิ่มแพ็กเกจเหล่านั้นลงใน app.yaml ในอีกสักครู่ แต่หากต้องการให้แอปเข้าถึงแพ็กเกจเหล่านั้นได้ คุณต้องเรียกใช้ฟังก์ชัน pkg_resources.working_set.add_entry() จาก setuptools ซึ่งจะช่วยให้ไลบรารีของบุคคลที่สามที่คัดลอก (รวมไว้เองหรือจัดจำหน่าย) ซึ่งติดตั้งไว้ในโฟลเดอร์ lib สื่อสารกับไลบรารีในตัวได้

ใช้การอัปเดตต่อไปนี้กับไฟล์ appengine_config.py เพื่อให้การเปลี่ยนแปลงเหล่านี้มีผล

ก่อน:

from google.appengine.ext import vendor

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

โค้ดนี้เพียงอย่างเดียวไม่เพียงพอที่จะรองรับการใช้งาน setuptools และ grpcio คุณต้องเพิ่มอีก 2-3 บรรทัด ดังนั้นให้อัปเดต appengine_config.py ให้มีลักษณะดังนี้

หลังจากนั้น

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)

ดูรายละเอียดเพิ่มเติมเกี่ยวกับการเปลี่ยนแปลงที่จำเป็นเพื่อรองรับไลบรารีของไคลเอ็นต์ Cloud ได้ในเอกสารประกอบเกี่ยวกับการย้ายข้อมูลบริการแบบแพ็กเกจ

app.yaml

เช่นเดียวกับ appengine_config.py คุณต้องเปลี่ยนไฟล์ app.yaml กลับไปเป็นไฟล์ที่รองรับ Python 2 มาเริ่มกันที่โมดูล 20 เดิม app.yaml

ก่อน:

runtime: python27
threadsafe: yes
api_version: 1

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

นอกเหนือจาก setuptools และ grpcio ที่กล่าวถึงก่อนหน้านี้แล้ว ยังมี Dependency (ไม่เกี่ยวข้องกับการย้ายข้อมูล Identity Platform โดยตรง) ที่กำหนดให้ใช้ไลบรารีไคลเอ็นต์ Cloud Storage และต้องใช้แพ็กเกจของบุคคลที่สามในตัวอีกแพ็กเกจหนึ่งคือ ssl เพิ่มทั้ง 3 รายการในlibrariesส่วนใหม่ โดยเลือกเวอร์ชัน "ล่าสุด" ของแพ็กเกจเหล่านั้นที่พร้อมใช้งาน เพื่อapp.yaml

หลังจากนั้น

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

สำหรับโมดูลที่ 21 เราได้เพิ่ม Google Auth, Cloud NDB, Cloud Resource Manager และ Firebase Admin SDK ลงใน Python 3 requirements.txt สถานการณ์สำหรับ Python 2 นั้นซับซ้อนกว่า

  • Resource Manager API มีฟังก์ชันการทำงานของนโยบายอนุญาตที่จำเป็นสำหรับแอปตัวอย่าง แต่Python 2 เวอร์ชันสุดท้ายของไลบรารีของไคลเอ็นต์ Cloud Resource Manager ยังไม่รองรับฟังก์ชันนี้ (ใช้ได้เฉพาะในเวอร์ชัน Python 3)
  • ด้วยเหตุนี้ คุณจึงต้องมีวิธีอื่นในการเข้าถึงฟีเจอร์นี้จาก API วิธีแก้ปัญหาคือใช้ไลบรารีของไคลเอ็นต์ Google API ระดับล่างเพื่อสื่อสารกับ API หากต้องการเปลี่ยนไปใช้ไลบรารีไคลเอ็นต์นี้ ให้แทนที่ google-cloud-resource-manager ด้วยแพ็กเกจ google-api-python-client ระดับล่าง
  • เนื่องจากหยุดให้บริการ Python 2 แล้ว กราฟการอ้างอิงที่รองรับโมดูล 21 จึงต้องล็อกแพ็กเกจบางอย่างไว้ในเวอร์ชันที่เฉพาะเจาะจง ต้องเรียกใช้แพ็กเกจบางอย่างแม้ว่าจะไม่ได้ระบุไว้ใน app.yaml ของ Python 3 ก็ตาม

ก่อน:

flask

เริ่มจากโมดูล 20 requirements.txt ให้อัปเดตเป็นข้อมูลต่อไปนี้เพื่อให้แอปโมดูล 21 ทำงานได้

หลังจากนั้น

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

ระบบจะอัปเดตหมายเลขแพ็กเกจและเวอร์ชันในที่เก็บเมื่อมีการเปลี่ยนแปลงการอ้างอิง แต่app.yamlนี้เพียงพอสำหรับแอปที่ทำงานได้ในขณะที่เขียน

การอัปเดตการกำหนดค่าอื่นๆ

หากยังไม่ได้ลบโฟลเดอร์ lib จากช่วงต้นของโค้ดแล็บนี้ ให้ลบเลย ด้วย requirements.txt ที่อัปเดตใหม่ ให้ใช้คำสั่งที่คุ้นเคยนี้เพื่อติดตั้งข้อกำหนดเหล่านี้ลงใน lib

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

หากติดตั้งทั้ง Python 2 และ 3 ในระบบพัฒนา คุณอาจต้องใช้ pip2 แทน pip

แก้ไขโค้ดแอปพลิเคชัน

โชคดีที่การเปลี่ยนแปลงที่จำเป็นส่วนใหญ่อยู่ในไฟล์การกำหนดค่า การเปลี่ยนแปลงเพียงอย่างเดียวที่จำเป็นในโค้ดแอปพลิเคชันคือการอัปเดตเล็กน้อยเพื่อใช้ไลบรารีของไคลเอ็นต์ Google API ระดับล่างแทนไลบรารีของไคลเอ็นต์ Resource Manager เพื่อเข้าถึง API ไม่จำเป็นต้องอัปเดตเทมเพลตเว็บ templates/index.html

อัปเดตการนำเข้าและการเริ่มต้น

แทนที่ไลบรารีของไคลเอ็นต์ Resource Manager (google.cloud.resourcemanager) ด้วยไลบรารีของไคลเอ็นต์ Google APIs (googleapiclient.discovery) ดังที่แสดงด้านล่าง

ก่อน:

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

หลังจากนั้น

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

การสนับสนุนสำหรับผู้ใช้ที่เป็นผู้ดูแลระบบ App Engine

คุณต้องทำการเปลี่ยนแปลงเล็กน้อยใน _get_gae_admins() เพื่อรองรับการใช้ไลบรารีไคลเอ็นต์ระดับล่าง เรามาพูดถึงสิ่งที่กำลังจะเปลี่ยนแปลงกันก่อน แล้วค่อยให้โค้ดทั้งหมดแก่คุณเพื่ออัปเดต

โค้ด Python 2 ต้องใช้ทั้งข้อมูลเข้าสู่ระบบและรหัสโปรเจ็กต์ที่ส่งคืนจาก google.auth.default() เนื่องจากไม่ได้ใช้ข้อมูลเข้าสู่ระบบใน Python 3 จึงมีการกำหนดให้เป็นตัวแปรดัมมี่ขีดล่างทั่วไป ( _ ) เนื่องจากจำเป็นสำหรับเวอร์ชัน Python 2 ให้เปลี่ยนขีดล่างเป็น CREDS นอกจากนี้ คุณจะสร้างปลายทางบริการ API แทนการสร้างไคลเอ็นต์ Resource Manager API ซึ่งมีแนวคิดคล้ายกับไคลเอ็นต์ API ดังนั้นเราจึงใช้ชื่อตัวแปรเดียวกัน (rm_client) ข้อแตกต่างอย่างหนึ่งคือการเริ่มต้นใช้งานปลายทางบริการต้องใช้ข้อมูลเข้าสู่ระบบ (CREDS)

การเปลี่ยนแปลงเหล่านี้แสดงอยู่ในโค้ดด้านล่าง

ก่อน:

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

หลังจากนั้น

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)

ความแตกต่างอีกอย่างคือไลบรารีของไคลเอ็นต์ Resource Manager จะแสดงออบเจ็กต์ allow-policy ที่ใช้สัญกรณ์แอตทริบิวต์แบบจุด ในขณะที่ไลบรารีของไคลเอ็นต์ระดับล่างจะแสดงพจนานุกรม Python ที่ใช้เครื่องหมายวงเล็บเหลี่ยม ( [ ] ) เช่น ใช้ binding.role สำหรับไลบรารีของไคลเอ็นต์ Resource Manager เทียบกับ binding['role'] สำหรับไลบรารีระดับล่าง นอกจากนี้ รูปแบบแรกยังใช้ชื่อ "underscore_separated" เทียบกับไลบรารีระดับล่างที่ต้องการชื่อ "CamelCased" รวมถึงวิธีที่แตกต่างกันเล็กน้อยในการส่งพารามิเตอร์ API

ความแตกต่างในการใช้งานเหล่านี้แสดงไว้ด้านล่าง

ก่อน:

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)

หลังจากนั้น

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'])

เมื่อรวมการเปลี่ยนแปลงทั้งหมดนี้เข้าด้วยกัน ให้แทนที่ Python 3 _get_gae_admins() ด้วยเวอร์ชัน Python 2 ที่เทียบเท่านี้

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

ฟังก์ชัน is_admin() ไม่จำเป็นต้องมีการอัปเดตเนื่องจากขึ้นอยู่กับ _get_gae_admins() ซึ่งได้รับการอัปเดตแล้ว

การดำเนินการนี้เป็นการสิ้นสุดการเปลี่ยนแปลงที่จำเป็นในการย้อนพอร์ตแอป Python 3 Module 21 ไปยัง Python 2 ยินดีด้วยที่มาถึงแอปตัวอย่างที่อัปเดตแล้วของโมดูล 21 คุณจะเห็นโค้ดทั้งหมดในโฟลเดอร์ repo ของโมดูล 21a

7. สรุป/ล้างข้อมูล

ขั้นตอนสุดท้ายในโค้ดแล็บคือการตรวจสอบว่าผู้ใช้หลัก (ผู้ใช้หรือบัญชีบริการ) ที่เรียกใช้แอปนี้มีสิทธิ์ที่เหมาะสมในการดำเนินการดังกล่าว จากนั้นให้ติดตั้งใช้งานแอปเพื่อยืนยันว่าแอปทำงานได้ตามที่ต้องการและการเปลี่ยนแปลงจะแสดงในเอาต์พุต

ความสามารถในการอ่านนโยบายอนุญาตของ IAM

ก่อนหน้านี้เราได้แนะนำบทบาท 4 อย่างที่จำเป็นต่อการได้รับการยอมรับว่าเป็นผู้ใช้ผู้ดูแลระบบ App Engine แต่ตอนนี้มีบทบาทที่ 5 ที่คุณควรทำความคุ้นเคย

  • roles/viewer
  • roles/editor
  • roles/owner
  • roles/appengine.appAdmin
  • roles/resourcemanager.projectIamAdmin (สำหรับหลักที่เข้าถึงนโยบายอนุญาตของ IAM)

roles/resourcemanager.projectIamAdminบทบาทนี้ช่วยให้ผู้ใช้หลักระบุได้ว่าผู้ใช้ปลายทางเป็นสมาชิกของบทบาทผู้ดูแลระบบ App Engine หรือไม่ หากไม่ได้เป็นสมาชิกใน roles/resourcemanager.projectIamAdmin การเรียกใช้ Cloud Resource Manager API เพื่อรับนโยบายการอนุญาตจะล้มเหลว

คุณไม่จำเป็นต้องดำเนินการใดๆ ที่นี่ เนื่องจากแอปจะทำงานภายใต้บัญชีบริการเริ่มต้นของ App Engine ซึ่งจะได้รับสิทธิ์เป็นสมาชิกในบทบาทนี้โดยอัตโนมัติ แม้ว่าคุณจะใช้บัญชีบริการเริ่มต้นในระหว่างขั้นตอนการพัฒนา แต่เราขอแนะนำอย่างยิ่งให้สร้างและใช้บัญชีบริการที่มีการจัดการโดยผู้ใช้ที่มีสิทธิ์ขั้นต่ำที่จำเป็นเพื่อให้แอปทำงานได้อย่างถูกต้อง หากต้องการให้สิทธิ์การเป็นสมาชิกแก่บัญชีบริการดังกล่าว ให้เรียกใช้คำสั่งต่อไปนี้

$ 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 คือรหัสโปรเจ็กต์ Cloud และ USR_MGD_SVC_ACCT@PROJ_ID.iam.gserviceaccount.com คือบัญชีบริการที่ผู้ใช้จัดการซึ่งคุณสร้างขึ้นสำหรับแอป คำสั่งนี้จะแสดงนโยบาย IAM ที่อัปเดตแล้วสำหรับโปรเจ็กต์ ซึ่งคุณสามารถยืนยันได้ว่าบัญชีบริการมีสิทธิ์เป็นสมาชิกใน roles/resourcemanager.projectIamAdmin ดูข้อมูลเพิ่มเติมได้ที่เอกสารอ้างอิง ขอย้ำอีกครั้งว่าคุณไม่จำเป็นต้องออกคำสั่งนั้นในโค้ดแล็บนี้ แต่ให้บันทึกคำสั่งนี้ไว้เป็นข้อมูลอ้างอิงสำหรับการปรับปรุงแอปของคุณเอง

ทำให้แอปพลิเคชันใช้งานได้และยืนยัน

อัปโหลดแอปไปยังระบบคลาวด์ด้วยคำสั่ง gcloud app deploy มาตรฐาน เมื่อติดตั้งใช้งานแล้ว คุณควรเห็นฟังก์ชันการทำงานที่เกือบเหมือนกับแอปในโมดูลที่ 20 ยกเว้นว่าคุณได้แทนที่บริการผู้ใช้ App Engine ด้วย Cloud Identity Platform (และ Firebase Auth) สำหรับการจัดการผู้ใช้เรียบร้อยแล้ว

3a83ae745121d70.png

ความแตกต่างอย่างหนึ่งที่คุณจะสังเกตเห็นเมื่อเทียบกับโมดูลที่ 20 คือการคลิกเข้าสู่ระบบจะทำให้เกิดป๊อปอัปแทนการเปลี่ยนเส้นทาง ซึ่งจะแสดงในภาพหน้าจอบางส่วนด้านล่าง อย่างไรก็ตาม ลักษณะการทำงานจะแตกต่างกันเล็กน้อยโดยขึ้นอยู่กับจำนวนบัญชี Google ที่ลงทะเบียนไว้กับเบราว์เซอร์

หากไม่มีผู้ใช้ที่ลงทะเบียนกับเบราว์เซอร์หรือมีผู้ใช้เพียงคนเดียวที่ยังไม่ได้ลงชื่อเข้าใช้ ป๊อปอัปการลงชื่อเข้าใช้ Google ทั่วไปจะปรากฏขึ้น

8437f5f3d489a942.png

หากผู้ใช้รายเดียวลงทะเบียนไว้ในเบราว์เซอร์ของคุณ แต่ลงชื่อเข้าใช้ที่อื่น จะไม่มีกล่องโต้ตอบปรากฏขึ้น (หรือกล่องโต้ตอบจะปรากฏขึ้นและปิดทันที) และแอปจะเข้าสู่สถานะที่ลงชื่อเข้าใช้แล้ว (แสดงอีเมลของผู้ใช้และปุ่มออกจากระบบ)

นักพัฒนาแอปบางรายอาจต้องการแสดงตัวเลือกบัญชีแม้ว่าจะมีผู้ใช้เพียงรายเดียวก็ตาม

b75624cb68d94557.png

หากต้องการใช้ฟีเจอร์นี้ ให้ยกเลิกการแสดงความคิดเห็นในบรรทัด provider.setCustomParameters({prompt: 'select_account'}); ในเทมเพลตเว็บตามที่อธิบายไว้ก่อนหน้านี้

หากมีผู้ใช้หลายคน กล่องโต้ตอบตัวเลือกบัญชีจะปรากฏขึ้น (ดูด้านล่าง) หากยังไม่ได้ลงชื่อเข้าใช้ ระบบจะแจ้งให้ผู้ใช้ลงชื่อเข้าใช้ หากลงชื่อเข้าใช้แล้ว ป๊อปอัปจะหายไปและแอปจะเข้าสู่สถานะลงชื่อเข้าใช้

c454455b6020d5e4.png

สถานะการลงชื่อเข้าใช้ของโมดูล 21 จะมีลักษณะเหมือนกับอินเทอร์เฟซผู้ใช้ของโมดูล 20 ดังนี้

49ebe4dcc1eff11f.png

เช่นเดียวกับกรณีที่ผู้ใช้ที่เป็นผู้ดูแลระบบลงชื่อเข้าใช้

44302f35b39856eb.png

โมดูล 20 จะเข้าถึงตรรกะสำหรับเนื้อหาเทมเพลตเว็บจากแอป (โค้ดฝั่งเซิร์ฟเวอร์) เสมอ ซึ่งแตกต่างจากโมดูล 21 ข้อบกพร่องของโมดูล 20 คือระบบจะบันทึกการเข้าชม 1 ครั้งเมื่อผู้ใช้ปลายทางเข้าชมแอปเป็นครั้งแรก และบันทึกอีกครั้งเมื่อผู้ใช้ลงชื่อเข้าใช้

สำหรับโมดูลที่ 21 ตรรกะการเข้าสู่ระบบจะอยู่ในเทมเพลตเว็บ (โค้ดฝั่งไคลเอ็นต์) เท่านั้น ไม่จำเป็นต้องมีการเดินทางฝั่งเซิร์ฟเวอร์เพื่อพิจารณาว่าจะแสดงเนื้อหาใด การเรียกไปยังเซิร์ฟเวอร์มีเพียงการตรวจสอบผู้ใช้ที่ดูแลระบบหลังจากที่ผู้ใช้ปลายทางลงชื่อเข้าใช้ ซึ่งหมายความว่าการเข้าสู่ระบบและการออกจากระบบจะไม่บันทึกการเข้าชมเพิ่มเติม ดังนั้นรายการการเข้าชมล่าสุดจึงคงที่สำหรับการดำเนินการจัดการผู้ใช้ โปรดสังเกตว่าภาพหน้าจอด้านบนแสดงชุดการเข้าชม 4 ครั้งชุดเดียวกันในการเข้าสู่ระบบของผู้ใช้หลายราย

ภาพหน้าจอของโมดูล 20 แสดงให้เห็น "ข้อบกพร่องการเข้าชมซ้ำ" ที่จุดเริ่มต้นของโค้ดแล็บนี้ ระบบจะแสดงบันทึกการเข้าชมแยกกันสำหรับการดำเนินการลงชื่อเข้าใช้หรือลงชื่อออกแต่ละครั้ง ตรวจสอบการประทับเวลาของการเข้าชมล่าสุดสำหรับภาพหน้าจอแต่ละภาพที่แสดงการเรียงลำดับตามเวลา

ล้างข้อมูล

ทั่วไป

หากคุณดำเนินการเสร็จแล้วในตอนนี้ เราขอแนะนำให้ปิดใช้แอป App Engine เพื่อไม่ให้มีการเรียกเก็บเงิน อย่างไรก็ตาม หากต้องการทดสอบหรือทดลองเพิ่มเติม แพลตฟอร์ม App Engine มีโควต้าฟรี ดังนั้นตราบใดที่คุณไม่เกินระดับการใช้งานดังกล่าว คุณก็จะไม่ถูกเรียกเก็บเงิน ซึ่งเป็นค่าบริการสำหรับการประมวลผล แต่อาจมีค่าบริการสำหรับบริการ App Engine ที่เกี่ยวข้องด้วย ดังนั้นโปรดดูข้อมูลเพิ่มเติมในหน้าการกำหนดราคา หากการย้ายข้อมูลนี้เกี่ยวข้องกับบริการอื่นๆ ของระบบคลาวด์ ระบบจะเรียกเก็บเงินสำหรับบริการเหล่านั้นแยกต่างหาก ไม่ว่าจะในกรณีใดก็ตาม หากมี ให้ดูส่วน "เฉพาะสำหรับ Codelab นี้" ด้านล่าง

เพื่อความโปร่งใสอย่างเต็มที่ การติดตั้งใช้งานแพลตฟอร์มการประมวลผลแบบ Serverless ของ Google Cloud เช่น App Engine จะทำให้เกิดค่าใช้จ่ายในการสร้างและพื้นที่เก็บข้อมูลเล็กน้อย Cloud Build มีโควต้าฟรีของตัวเอง เช่นเดียวกับ Cloud Storage การจัดเก็บรูปภาพนั้นจะใช้โควต้าบางส่วน อย่างไรก็ตาม คุณอาจอาศัยอยู่ในภูมิภาคที่ไม่มีระดับการใช้งานฟรีดังกล่าว ดังนั้นโปรดทราบการใช้พื้นที่เก็บข้อมูลเพื่อลดค่าใช้จ่ายที่อาจเกิดขึ้น "โฟลเดอร์" ของ Cloud Storage ที่คุณควรตรวจสอบ ได้แก่

  • 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
  • ลิงก์พื้นที่เก็บข้อมูลด้านบนจะขึ้นอยู่กับPROJECT_IDและLOCของคุณ เช่น "us" หากแอปโฮสต์อยู่ในสหรัฐอเมริกา

ในทางกลับกัน หากคุณจะไม่ดำเนินการต่อกับแอปพลิเคชันนี้หรือ Codelab การย้ายข้อมูลอื่นๆ ที่เกี่ยวข้อง และต้องการลบทุกอย่างออกทั้งหมด ให้ปิดโปรเจ็กต์

เฉพาะสำหรับ Codelab นี้

บริการที่ระบุไว้ด้านล่างเป็นบริการเฉพาะสำหรับโค้ดแล็บนี้ ดูข้อมูลเพิ่มเติมได้ในเอกสารประกอบของแต่ละผลิตภัณฑ์

  • บริการ App Engine Datastore ให้บริการโดย Cloud Datastore (Cloud Firestore ในโหมด Datastore) ซึ่งมีระดับฟรีด้วย ดูข้อมูลเพิ่มเติมได้ที่หน้าการกำหนดราคา
  • การใช้แพลตฟอร์ม Cloud Identity จะมีระดับ "ฟรี" บางอย่าง ขึ้นอยู่กับบริการที่คุณใช้ ดูรายละเอียดเพิ่มเติมได้ในหน้าราคา
  • การใช้ Cloud Resource Manager API ส่วนใหญ่ไม่มีค่าใช้จ่ายตามหน้าการกำหนดราคา

ขั้นตอนถัดไป

นอกเหนือจากบทแนะนำนี้ โมดูลการย้ายข้อมูลอื่นๆ ที่มุ่งเน้นการย้ายออกจากบริการแบบแพ็กเกจเดิมที่ควรพิจารณา ได้แก่

  • โมดูลที่ 2: ย้ายข้อมูลจาก App Engine ndb ไปยัง Cloud NDB
  • โมดูล 7-9: ย้ายข้อมูลจากคิวงานของ App Engine (งานแบบพุช) ไปยัง Cloud Tasks
  • โมดูล 12-13: ย้ายข้อมูลจาก Memcache ของ App Engine ไปยัง Cloud Memorystore
  • โมดูล 15-16: ย้ายข้อมูลจาก App Engine Blobstore ไปยัง Cloud Storage
  • โมดูล 18-19: ย้ายข้อมูลจากคิวงานของ App Engine (งานแบบดึง) ไปยัง Cloud Pub/Sub

App Engine ไม่ใช่แพลตฟอร์มแบบไร้เซิร์ฟเวอร์เพียงอย่างเดียวใน Google Cloud อีกต่อไป หากคุณมีแอป App Engine ขนาดเล็กหรือแอปที่มีฟังก์ชันการทำงานจำกัด และต้องการเปลี่ยนให้เป็นไมโครเซอร์วิสแบบสแตนด์อโลน หรือต้องการแยกแอปแบบ Monolithic ออกเป็นคอมโพเนนต์ที่นำกลับมาใช้ใหม่ได้หลายรายการ นี่เป็นเหตุผลที่ดีที่ควรพิจารณาเปลี่ยนไปใช้ Cloud Functions หากการใช้คอนเทนเนอร์กลายเป็นส่วนหนึ่งของเวิร์กโฟลว์การพัฒนาแอปพลิเคชัน โดยเฉพาะอย่างยิ่งหากประกอบด้วยไปป์ไลน์ CI/CD (การผสานรวมอย่างต่อเนื่อง/การนำส่งหรือการติดตั้งใช้งานอย่างต่อเนื่อง) ให้พิจารณาการย้ายข้อมูลไปยัง Cloud Run สถานการณ์เหล่านี้จะครอบคลุมในโมดูลต่อไปนี้

  • ย้ายข้อมูลจาก App Engine ไปยัง Cloud Functions: ดูโมดูลที่ 11
  • ย้ายข้อมูลจาก App Engine ไปยัง Cloud Run: ดูโมดูลที่ 4 เพื่อจัดคอนเทนเนอร์แอปด้วย Docker หรือโมดูลที่ 5 เพื่อดำเนินการโดยไม่ต้องใช้คอนเทนเนอร์ ความรู้เกี่ยวกับ Docker หรือ Dockerfile

การเปลี่ยนไปใช้แพลตฟอร์มแบบไม่ใช้เซิร์ฟเวอร์อื่นเป็นทางเลือก และเราขอแนะนำให้พิจารณาตัวเลือกที่ดีที่สุดสำหรับแอปและ Use Case ของคุณก่อนทำการเปลี่ยนแปลงใดๆ

ไม่ว่าคุณจะพิจารณาโมดูลการย้ายข้อมูลใดเป็นโมดูลถัดไป คุณจะเข้าถึงเนื้อหาของ Serverless Migration Station ทั้งหมด (Codelab, วิดีโอ, ซอร์สโค้ด [หากมี]) ได้ที่ที่เก็บแบบโอเพนซอร์ส README ของที่เก็บยังให้คำแนะนำเกี่ยวกับการย้ายข้อมูลที่ควรพิจารณาและ "ลำดับ" ที่เกี่ยวข้องของโมดูลการย้ายข้อมูลด้วย

8. แหล่งข้อมูลเพิ่มเติม

ด้านล่างนี้คือแหล่งข้อมูลเพิ่มเติมสำหรับนักพัฒนาแอปที่ต้องการศึกษาโมดูลการย้ายข้อมูลนี้หรือโมดูลที่เกี่ยวข้องเพิ่มเติม คุณสามารถแสดงความคิดเห็นเกี่ยวกับเนื้อหานี้ ค้นหาลิงก์ไปยังโค้ด และเอกสารประกอบต่างๆ ที่อาจเป็นประโยชน์ได้ที่ด้านล่าง

ปัญหา/ความคิดเห็นเกี่ยวกับ Codelabs

หากพบปัญหาเกี่ยวกับ Codelab นี้ โปรดค้นหาปัญหาของคุณก่อนที่จะยื่นเรื่อง ลิงก์สำหรับค้นหาและสร้างปัญหาใหม่

แหล่งข้อมูลเกี่ยวกับการย้ายข้อมูล

คุณดูลิงก์ไปยังโฟลเดอร์ที่เก็บสำหรับโมดูลที่ 20 (START) และโมดูลที่ 21 (FINISH) ได้ในตารางด้านล่าง

Codelab

Python 2

Python 3

Module 20

รหัส

(ไม่มี)

โมดูล 21 (Codelab นี้)

รหัส

รหัส

ข้อมูลอ้างอิงออนไลน์

แหล่งข้อมูลที่เกี่ยวข้องกับบทแนะนำนี้มีดังนี้

Cloud Identity Platform และ Cloud Marketplace

Cloud Resource Manager, Cloud IAM, Firebase Admin SDK

ผู้ใช้ App Engine, App Engine NDB, Cloud NDB, Cloud Datastore

แหล่งข้อมูลอื่นๆ ของโมดูลการย้ายข้อมูล

การย้ายข้อมูล App Engine

แพลตฟอร์ม App Engine

Cloud SDK

ข้อมูลอื่นๆ เกี่ยวกับระบบคลาวด์

วิดีโอ

ใบอนุญาต

ผลงานนี้ได้รับอนุญาตภายใต้สัญญาอนุญาตครีเอทีฟคอมมอนส์สำหรับยอมรับสิทธิของผู้สร้าง (Creative Commons Attribution License) 2.0 แบบทั่วไป