นี่คือคำแนะนำแบบทีละขั้นตอนที่ 3 ในชุดคำแนะนำแบบทีละขั้นตอนเกี่ยวกับส่วนเสริมของ Classroom
ในคำแนะนำแบบทีละขั้นตอนนี้ คุณจะจัดการการเข้าชมส่วนเสริมซ้ำๆ โดยการดึงข้อมูลเข้าสู่ระบบที่ผู้ใช้ให้ไว้ก่อนหน้านี้โดยอัตโนมัติ จากนั้นเปลี่ยนเส้นทางผู้ใช้ไปยังหน้าที่ผู้ใช้สามารถส่งคำขอ API ได้ทันที ซึ่งเป็นลักษณะการทำงานที่จำเป็นสำหรับส่วนเสริมของ Classroom
ในคำแนะนำแบบทีละขั้นตอนนี้ คุณจะทำสิ่งต่อไปนี้
- ใช้พื้นที่เก็บข้อมูลถาวรสำหรับข้อมูลเข้าสู่ระบบของผู้ใช้
- ดึงและประเมินพารามิเตอร์การค้นหา
login_hintของส่วนเสริม ซึ่งเป็นหมายเลขรหัส Google ที่ไม่ซ้ำกันของผู้ใช้ที่ลงชื่อเข้าใช้
เมื่อเสร็จแล้ว คุณจะให้สิทธิ์ผู้ใช้ในเว็บแอปได้อย่างเต็มที่และเรียก Google APIs ได้
ทำความเข้าใจพารามิเตอร์การค้นหาของ iframe
Classroom จะโหลด URI การตั้งค่าสิ่งที่แนบ ของส่วนเสริมเมื่อเปิด โดยแอป Classroom จะเพิ่มพารามิเตอร์การค้นหา GET หลายรายการลงใน URI ซึ่งมีข้อมูลตามบริบทที่เป็นประโยชน์ เช่น หาก URI การค้นหาสิ่งที่แนบคือ
https://example.com/addon Classroom จะสร้าง iframe โดยตั้งค่า URL แหล่งที่มาเป็น
https://example.com/addon?courseId=XXX&itemId=YYY&itemType=courseWork&addOnToken=ZZZ,
โดยที่ XXX, YYY และ ZZZ เป็นรหัสสตริง ดูคำอธิบายโดยละเอียดเกี่ยวกับสถานการณ์นี้ได้ในคู่มือ iframe
URL การค้นหามีพารามิเตอร์การค้นหาที่เป็นไปได้ 5 รายการดังนี้
courseId: รหัสของหลักสูตร Classroom ปัจจุบันitemId: รหัสของรายการในสตรีมที่ผู้ใช้กำลังแก้ไขหรือสร้างitemType: ประเภทของรายการในสตรีมที่ผู้ใช้กำลังสร้างหรือแก้ไข ซึ่งอาจเป็นcourseWork,courseWorkMaterial, หรือannouncement.addOnToken: โทเค็นที่ใช้ให้สิทธิ์การดำเนินการบางอย่างของส่วนเสริม Classroomlogin_hint: รหัส Google ของผู้ใช้ปัจจุบัน
คำแนะนำแบบทีละขั้นตอนนี้จะกล่าวถึง login_hint ระบบจะเปลี่ยนเส้นทางผู้ใช้ตามว่ามีการระบุพารามิเตอร์การค้นหานี้หรือไม่ โดยจะเปลี่ยนเส้นทางไปยังโฟลว์การให้สิทธิ์หากไม่มี หรือไปยังหน้าการค้นหาส่วนเสริมหากมี
เข้าถึงพารามิเตอร์การค้นหา
ระบบจะส่งพารามิเตอร์การค้นหาไปยังเว็บแอปพลิเคชันของคุณในสตริง URI จัดเก็บค่าเหล่านี้ไว้ในเซสชัน โดยจะใช้ในโฟลว์การให้สิทธิ์ รวมถึงใช้จัดเก็บและดึงข้อมูลเกี่ยวกับผู้ใช้ ระบบจะส่งพารามิเตอร์การค้นหาเหล่านี้เมื่อเปิดส่วนเสริมเป็นครั้งแรกเท่านั้น
Python
ไปที่คำจำกัดความของเส้นทาง Flask (routes.py หากคุณทำตามตัวอย่างที่เราให้ไว้) ที่ด้านบนของเส้นทาง Landing Page ของส่วนเสริม (/classroom-addon ในตัวอย่างที่เราให้ไว้) ให้ดึงและจัดเก็บพารามิเตอร์การค้นหา login_hint ดังนี้
# If the login_hint query parameter is available, we'll store it in the session.
if flask.request.args.get("login_hint"):
flask.session["login_hint"] = flask.request.args.get("login_hint")
ตรวจสอบว่าได้จัดเก็บ login_hint (หากมี) ไว้ในเซสชัน ตำแหน่งนี้เหมาะสำหรับการจัดเก็บค่าเหล่านี้ เนื่องจากค่าเหล่านี้เป็นค่าชั่วคราวและคุณจะได้รับค่าใหม่เมื่อเปิดส่วนเสริม
# It's possible that we might return to this route later, in which case the
# parameters will not be passed in. Instead, use the values cached in the
# session.
login_hint = flask.session.get("login_hint")
# If there's still no login_hint query parameter, this must be their first
# time signing in, so send the user to the sign in page.
if login_hint is None:
return start_auth_flow()
Java
ไปที่เส้นทาง Landing Page ของส่วนเสริมในคลาสคอนโทรลเลอร์ (/addon-discovery ใน AuthController.java ในตัวอย่างที่เราให้ไว้) ที่จุดเริ่มต้นของเส้นทางนี้ ให้ดึงและจัดเก็บพารามิเตอร์การค้นหา login_hint
/** Retrieve the login_hint query parameter from the request URL if present. */
String login_hint = request.getParameter("login_hint");
ตรวจสอบว่าได้จัดเก็บ login_hint (หากมี) ไว้ในเซสชัน ตำแหน่งนี้เหมาะสำหรับการจัดเก็บค่าเหล่านี้ เนื่องจากค่าเหล่านี้เป็นค่าชั่วคราวและคุณจะได้รับค่าใหม่เมื่อเปิดส่วนเสริม
/** If login_hint wasn't sent, use the values in the session. */
if (login_hint == null) {
login_hint = (String) session.getAttribute("login_hint");
}
/** If the there is still no login_hint, route the user to the authorization
* page. */
if (login_hint == null) {
return startAuthFlow(model);
}
/** If the login_hint query parameter is provided, add it to the session. */
else if (login_hint != null) {
session.setAttribute("login_hint", login_hint);
}
เพิ่มพารามิเตอร์การค้นหาลงในโฟลว์การให้สิทธิ์
คุณควรส่งพารามิเตอร์ login_hint ไปยังเซิร์ฟเวอร์การตรวจสอบสิทธิ์ของ Google ด้วย ซึ่งจะช่วยให้กระบวนการตรวจสอบสิทธิ์ง่ายขึ้น หากแอปพลิเคชันของคุณทราบว่าผู้ใช้รายใดพยายามตรวจสอบสิทธิ์ เซิร์ฟเวอร์จะใช้คำแนะนำเพื่อลดความซับซ้อนของโฟลว์การเข้าสู่ระบบโดยการป้อนข้อมูลในช่องอีเมลของแบบฟอร์มลงชื่อเข้าใช้ล่วงหน้า
Python
ไปที่เส้นทางการให้สิทธิ์ในไฟล์เซิร์ฟเวอร์ Flask (/authorize ในตัวอย่างที่เราให้ไว้) เพิ่มอาร์กิวเมนต์ login_hint ลงในการเรียก flow.authorization_url
authorization_url, state = flow.authorization_url(
# Enable offline access so that you can refresh an access token without
# re-prompting the user for permission. Recommended for web server apps.
access_type="offline",
# Enable incremental authorization. Recommended as a best practice.
include_granted_scopes="true",
# The user will automatically be selected if we have the login_hint.
login_hint=flask.session.get("login_hint"),
Java
ไปที่เมธอด authorize() ในคลาส AuthService.java เพิ่ม login_hint เป็นพารามิเตอร์ลงในเมธอด และเพิ่ม login_hint และอาร์กิวเมนต์ลงในเครื่องมือสร้าง URL การให้สิทธิ์
String authUrl = flow
.newAuthorizationUrl()
.setState(state)
.set("login_hint", login_hint)
.setRedirectUri(REDIRECT_URI)
.build();
เพิ่มพื้นที่เก็บข้อมูลถาวรสำหรับข้อมูลเข้าสู่ระบบของผู้ใช้
หากคุณได้รับ login_hint เป็นพารามิเตอร์การค้นหาเมื่อโหลดส่วนเสริม แสดงว่าผู้ใช้ได้ทำตามโฟลว์การให้สิทธิ์สำหรับแอปพลิเคชันของเราแล้ว คุณควรดึงข้อมูลเข้าสู่ระบบก่อนหน้าของผู้ใช้แทนที่จะบังคับให้ผู้ใช้ลงชื่อเข้าใช้อีกครั้ง
โปรดจำไว้ว่าคุณได้รับโทเค็นการรีเฟรช เมื่อโฟลว์การให้สิทธิ์เสร็จสมบูรณ์ ให้บันทึกโทเค็นนี้ไว้เพื่อนำไปใช้ซ้ำในการรับโทเค็นเพื่อการเข้าถึง, ซึ่งมีอายุการใช้งานสั้นและจำเป็นสำหรับการใช้ Google APIs ก่อนหน้านี้คุณได้บันทึกข้อมูลเข้าสู่ระบบเหล่านี้ไว้ในเซสชัน แต่คุณต้องจัดเก็บข้อมูลเข้าสู่ระบบเพื่อจัดการการเข้าชมซ้ำๆ
กำหนดสคีมาผู้ใช้และตั้งค่าฐานข้อมูล
ตั้งค่าสคีมาฐานข้อมูลสำหรับ User
Python
กำหนดสคีมาผู้ใช้
User มีแอตทริบิวต์ต่อไปนี้
id: รหัส Google ของผู้ใช้ ซึ่งควรตรงกับค่าที่ระบุไว้ในพารามิเตอร์การค้นหาlogin_hintdisplay_name: ชื่อและนามสกุลของผู้ใช้ เช่น "Alex Smith"email: อีเมลของผู้ใช้portrait_url: URL ของรูปโปรไฟล์ของผู้ใช้refresh_token: โทเค็นการรีเฟรชที่ได้รับก่อนหน้านี้
ตัวอย่างนี้ใช้การจัดเก็บข้อมูลโดยใช้ SQLite ซึ่ง Python รองรับโดยค่าเริ่มต้น และใช้โมดูล flask_sqlalchemy เพื่อช่วยในการจัดการฐานข้อมูล
ตั้งค่าฐานข้อมูล
ก่อนอื่น ให้ระบุตำแหน่งไฟล์สำหรับฐานข้อมูล ไปที่ไฟล์การกำหนดค่าเซิร์ฟเวอร์ (config.py ในตัวอย่างที่เราให้ไว้) แล้วเพิ่มข้อมูลต่อไปนี้
import os
# Point to a database file in the project root.
DATABASE_FILE_NAME = os.path.join(
os.path.abspath(os.path.dirname(__file__)), 'data.sqlite')
class Config(object):
SQLALCHEMY_DATABASE_URI = f"sqlite:///{DATABASE_FILE_NAME}"
SQLALCHEMY_TRACK_MODIFICATIONS = False
ซึ่งจะชี้ Flask ไปยังไฟล์ data.sqlite ในไดเรกทอรีเดียวกับไฟล์
main.py
จากนั้นไปที่ไดเรกทอรีโมดูลและสร้างไฟล์ models.py ใหม่
ซึ่งจะเป็น webapp/models.py หากคุณทำตามตัวอย่างที่เราให้ไว้ เพิ่มข้อมูลต่อไปนี้ลงในไฟล์ใหม่เพื่อกำหนดตาราง User โดยแทนที่ชื่อโมดูลด้วย webapp หากชื่อโมดูลของคุณแตกต่างออกไป
from webapp import db
# Database model to represent a user.
class User(db.Model):
# The user's identifying information:
id = db.Column(db.String(120), primary_key=True)
display_name = db.Column(db.String(80))
email = db.Column(db.String(120), unique=True)
portrait_url = db.Column(db.Text())
# The user's refresh token, which will be used to obtain an access token.
# Note that refresh tokens will become invalid if:
# - The refresh token has not been used for six months.
# - The user revokes your app's access permissions.
# - The user changes passwords.
# - The user belongs to a Google Cloud organization
# that has session control policies in effect.
refresh_token = db.Column(db.Text())
สุดท้าย ในไฟล์ __init__.py ของโมดูล ให้เพิ่มข้อมูลต่อไปนี้เพื่อนำเข้า
โมเดลใหม่และสร้างฐานข้อมูล
from webapp import models
from os import path
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy(app)
# Initialize the database file if not created.
if not path.exists(config.DATABASE_FILE_NAME):
db.create_all()
Java
กำหนดสคีมาผู้ใช้
User มีแอตทริบิวต์ต่อไปนี้
id: รหัส Google ของผู้ใช้ ซึ่งควรตรงกับค่าที่ระบุไว้ในพารามิเตอร์การค้นหาlogin_hintemail: อีเมลของผู้ใช้
สร้างไฟล์ schema.sql ในไดเรกทอรี resources ของโมดูล Spring
จะอ่านไฟล์นี้และสร้างสคีมาสำหรับฐานข้อมูลตามนั้น
กำหนดตารางโดยใช้ชื่อตาราง users และคอลัมน์เพื่อแสดง
แอตทริบิวต์ User ได้แก่ id และ email
CREATE TABLE IF NOT EXISTS users (
id VARCHAR(255) PRIMARY KEY, -- user's unique Google ID
email VARCHAR(255), -- user's email address
);
สร้างคลาส Java เพื่อกำหนดโมเดล User สำหรับฐานข้อมูล ซึ่งจะเป็น
User.java ในตัวอย่างที่เราให้ไว้
เพิ่มคำอธิบายประกอบ @Entity เพื่อระบุว่านี่คือ POJO ที่
บันทึกลงในฐานข้อมูลได้ เพิ่มคำอธิบายประกอบ @Table พร้อมชื่อตารางที่เกี่ยวข้องซึ่งคุณกำหนดค่าไว้ใน schema.sql
โปรดทราบว่าตัวอย่างโค้ดมีตัวสร้างและตัวตั้งค่าสำหรับแอตทริบิวต์ 2 รายการ
โดยเครื่องมือสร้างและ Setter จะใช้ใน
AuthController.java เพื่อสร้างหรืออัปเดตผู้ใช้ในฐานข้อมูล นอกจากนี้ คุณ
ยังอาจรวมตัวรับค่าและเมธอด toString ตามความเหมาะสม แต่สำหรับ
คำแนะนำแบบทีละขั้นตอนนี้ เราจะไม่ใช้เมธอดเหล่านี้และจะละเว้นจาก
ตัวอย่างโค้ดในหน้านี้เพื่อความกระชับ
/** An entity class that provides a model to store user information. */
@Entity
@Table(name = "users")
public class User {
/** The user's unique Google ID. The @Id annotation specifies that this
* is the primary key. */
@Id
@Column
private String id;
/** The user's email address. */
@Column
private String email;
/** Required User class no args constructor. */
public User() {
}
/** The User class constructor that creates a User object with the
* specified parameters.
* @param id the user's unique Google ID
* @param email the user's email address
*/
public User(String id, String email) {
this.id = id;
this.email = email;
}
public void setId(String id) { this.id = id; }
public void setEmail(String email) { this.email = email; }
}
สร้างอินเทอร์เฟซที่ชื่อ UserRepository.java เพื่อจัดการการดำเนินการ CRUD
กับฐานข้อมูล อินเทอร์เฟซนี้จะขยายอินเทอร์เฟซ CrudRepository
/** Provides CRUD operations for the User class by extending the
* CrudRepository interface. */
@Repository
public interface UserRepository extends CrudRepository<User, String> {
}
คลาสคอนโทรลเลอร์ช่วยให้การสื่อสารระหว่างไคลเอ็นต์กับที่เก็บข้อมูลเป็นไปได้ง่ายขึ้น ดังนั้น ให้อัปเดตตัวสร้างคลาสคอนโทรลเลอร์เพื่อแทรกคลาส UserRepository
/** Declare UserRepository to be used in the Controller class constructor. */
private final UserRepository userRepository;
/**
* ...
* @param userRepository the class that interacts with User objects stored in
* persistent storage.
*/
public AuthController(AuthService authService, UserRepository userRepository) {
this.authService = authService;
this.userRepository = userRepository;
}
ตั้งค่าฐานข้อมูล
หากต้องการจัดเก็บข้อมูลที่เกี่ยวข้องกับผู้ใช้ ให้ใช้ฐานข้อมูล H2 ที่ Spring Boot รองรับโดยค่าเริ่มต้น
นอกจากนี้ เรายังใช้ฐานข้อมูลนี้ในคำแนะนำแบบทีละขั้นตอนต่อๆ ไปเพื่อจัดเก็บข้อมูลอื่นๆ ที่เกี่ยวข้องกับ Classroom การตั้งค่าฐานข้อมูล H2 ต้องเพิ่มการกำหนดค่าต่อไปนี้
ลงใน application.properties
# Enable configuration for persistent storage using an H2 database
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:file:./h2/userdb
spring.datasource.username=<USERNAME>
spring.datasource.password=<PASSWORD>
spring.jpa.hibernate.ddl-auto=update
spring.jpa.open-in-view=false
การกำหนดค่า spring.datasource.url จะสร้างไดเรกทอรีที่ชื่อ h2 โดยมีไฟล์ userdb จัดเก็บอยู่ภายใน เพิ่มเส้นทางไปยังฐานข้อมูล H2 ลงใน
.gitignore คุณต้องอัปเดต spring.datasource.username และ
spring.datasource.password ก่อนที่จะเรียกใช้แอปพลิเคชันเพื่อตั้งค่า
ฐานข้อมูลด้วยชื่อผู้ใช้และรหัสผ่านที่คุณเลือก หากต้องการอัปเดตชื่อผู้ใช้และรหัสผ่านสำหรับฐานข้อมูลหลังจากเรียกใช้แอปพลิเคชันแล้ว ให้ลบไดเรกทอรี h2 ที่สร้างขึ้น อัปเดตการกำหนดค่า และเรียกใช้แอปพลิเคชันอีกครั้ง
การตั้งค่า spring.jpa.hibernate.ddl-auto เป็น update จะช่วยให้มั่นใจได้ว่าระบบจะเก็บรักษาข้อมูลที่จัดเก็บไว้ในฐานข้อมูลเมื่อรีสตาร์ทแอปพลิเคชัน
หากต้องการล้างฐานข้อมูลทุกครั้งที่รีสตาร์ทแอปพลิเคชัน ให้ตั้งค่านี้เป็น
config เป็น create
ตั้งค่า spring.jpa.open-in-view เป็น false การกำหนดค่านี้เปิดใช้
โดยค่าเริ่มต้นและอาจทำให้เกิดปัญหาด้านประสิทธิภาพที่
วินิจฉัยได้ยากในสภาพแวดล้อมจริง
ดังที่อธิบายไว้ก่อนหน้านี้ คุณต้องดึงข้อมูลเข้าสู่ระบบของผู้ใช้ที่กลับมาใช้แอปพลิเคชันได้
GoogleAuthorizationCodeFlow มีการรองรับพื้นที่เก็บข้อมูลเข้าสู่ระบบในตัว
ซึ่งช่วยให้การดำเนินการนี้เป็นไปได้ง่ายขึ้น
ในคลาส AuthService.java ให้กำหนดเส้นทางไปยังไฟล์ที่จัดเก็บคลาสข้อมูลเข้าสู่ระบบ ในตัวอย่างนี้ ระบบจะสร้างไฟล์ในไดเรกทอรี /credentialStore เพิ่มเส้นทางไปยังพื้นที่เก็บข้อมูลเข้าสู่ระบบลงใน
.gitignore. ระบบจะสร้างไดเรกทอรีนี้เมื่อผู้ใช้เริ่ม
โฟลว์การให้สิทธิ์
private static final File dataDirectory = new File("credentialStore");
จากนั้นสร้างเมธอดในไฟล์ AuthService.java ที่สร้างและ
แสดงออบเจ็กต์ FileDataStoreFactory ซึ่งเป็นพื้นที่เก็บข้อมูลที่
จัดเก็บข้อมูลเข้าสู่ระบบ
/** Creates and returns FileDataStoreFactory object to store credentials.
* @return FileDataStoreFactory dataStore used to save and obtain users ids
* mapped to Credentials.
* @throws IOException if creating the dataStore is unsuccessful.
*/
public FileDataStoreFactory getCredentialDataStore() throws IOException {
FileDataStoreFactory dataStore = new FileDataStoreFactory(dataDirectory);
return dataStore;
}
อัปเดตเมธอด getFlow() ใน AuthService.java เพื่อรวม
setDataStoreFactory ในเมธอด GoogleAuthorizationCodeFlow Builder()
และเรียก getCredentialDataStore() เพื่อตั้งค่าพื้นที่เก็บข้อมูล
GoogleAuthorizationCodeFlow authorizationCodeFlow =
new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT,
JSON_FACTORY,
getClientSecrets(),
getScopes())
.setAccessType("offline")
.setDataStoreFactory(getCredentialDataStore())
.build();
จากนั้นอัปเดตเมธอด getAndSaveCredentials(String authorizationCode)
ก่อนหน้านี้ เมธอดนี้จะรับข้อมูลเข้าสู่ระบบโดยไม่จัดเก็บไว้ที่ใดเลย
อัปเดตเมธอดเพื่อจัดเก็บข้อมูลเข้าสู่ระบบในพื้นที่เก็บข้อมูล
ที่จัดทำดัชนีตามรหัสผู้ใช้
คุณสามารถรับรหัสผู้ใช้จากออบเจ็กต์ TokenResponse โดยใช้
id_token แต่ต้องยืนยันก่อน มิเช่นนั้น แอปพลิเคชันไคลเอ็นต์อาจแอบอ้างเป็นผู้ใช้ได้โดยการส่งรหัสผู้ใช้ที่แก้ไขแล้วไปยังเซิร์ฟเวอร์ เราขอแนะนำให้คุณใช้ไลบรารีไคลเอ็นต์ Google API เพื่อตรวจสอบ id_token ดูข้อมูลเพิ่มเติมได้ที่ [หน้าข้อมูลประจำตัวของ Google เกี่ยวกับการ
ยืนยันโทเค็นรหัส Google]
// Obtaining the id_token will help determine which user signed in to the application.
String idTokenString = tokenResponse.get("id_token").toString();
// Validate the id_token using the GoogleIdTokenVerifier object.
GoogleIdTokenVerifier googleIdTokenVerifier = new GoogleIdTokenVerifier.Builder(
HTTP_TRANSPORT,
JSON_FACTORY)
.setAudience(Collections.singletonList(
googleClientSecrets.getWeb().getClientId()))
.build();
GoogleIdToken idToken = googleIdTokenVerifier.verify(idTokenString);
if (idToken == null) {
throw new Exception("Invalid ID token.");
}
เมื่อยืนยัน id_token แล้ว ให้รับ userId เพื่อจัดเก็บพร้อมกับข้อมูลเข้าสู่ระบบที่ได้รับ
// Obtain the user id from the id_token.
Payload payload = idToken.getPayload();
String userId = payload.getSubject();
อัปเดตการเรียก flow.createAndStoreCredential เพื่อรวม userId
// Save the user id and credentials to the configured FileDataStoreFactory.
Credential credential = flow.createAndStoreCredential(tokenResponse, userId);
เพิ่มเมธอดลงในคลาส AuthService.java ที่แสดงข้อมูลเข้าสู่ระบบสำหรับผู้ใช้ที่เฉพาะเจาะจงหากมีอยู่ในพื้นที่เก็บข้อมูล
/** Find credentials in the datastore based on a specific user id.
* @param userId key to find in the file datastore.
* @return Credential object to be returned if a matching key is found in the datastore. Null if
* the key doesn't exist.
* @throws Exception if building flow object or checking for userId key is unsuccessful. */
public Credential loadFromCredentialDataStore(String userId) throws Exception {
try {
GoogleAuthorizationCodeFlow flow = getFlow();
Credential credential = flow.loadCredential(userId);
return credential;
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
ดึงข้อมูลเข้าสู่ระบบ
กำหนดเมธอดสำหรับการดึงข้อมูล Users คุณจะได้รับ id ในพารามิเตอร์การค้นหา login_hint ซึ่งคุณสามารถใช้เพื่อดึงข้อมูลผู้ใช้ที่เฉพาะเจาะจงได้
Python
def get_credentials_from_storage(id):
"""
Retrieves credentials from the storage and returns them as a dictionary.
"""
return User.query.get(id)
Java
ในคลาส AuthController.java ให้กำหนดเมธอดเพื่อดึงผู้ใช้จากฐานข้อมูลตามรหัสผู้ใช้
/** Retrieves stored credentials based on the user id.
* @param id the id of the current user
* @return User the database entry corresponding to the current user or null
* if the user doesn't exist in the database.
*/
public User getUser(String id) {
if (id != null) {
Optional<User> user = userRepository.findById(id);
if (user.isPresent()) {
return user.get();
}
}
return null;
}
จัดเก็บข้อมูลเข้าสู่ระบบ
การจัดเก็บข้อมูลเข้าสู่ระบบมี 2 สถานการณ์ดังนี้ หาก id ของผู้ใช้อยู่ในฐานข้อมูลแล้ว ให้อัปเดตข้อมูลที่มีอยู่ด้วยค่าใหม่ มิเช่นนั้น ให้สร้างข้อมูล User ใหม่และเพิ่มลงในฐานข้อมูล
Python
ก่อนอื่น ให้กำหนดเมธอดอรรถประโยชน์ที่ใช้ลักษณะการทำงานในการจัดเก็บหรืออัปเดต
def save_user_credentials(credentials=None, user_info=None):
"""
Updates or adds a User to the database. A new user is added only if both
credentials and user_info are provided.
Args:
credentials: An optional Credentials object.
user_info: An optional dict containing user info returned by the
OAuth 2.0 API.
"""
existing_user = get_credentials_from_storage(
flask.session.get("login_hint"))
if existing_user:
if user_info:
existing_user.id = user_info.get("id")
existing_user.display_name = user_info.get("name")
existing_user.email = user_info.get("email")
existing_user.portrait_url = user_info.get("picture")
if credentials and credentials.refresh_token is not None:
existing_user.refresh_token = credentials.refresh_token
elif credentials and user_info:
new_user = User(id=user_info.get("id"),
display_name=user_info.get("name"),
email=user_info.get("email"),
portrait_url=user_info.get("picture"),
refresh_token=credentials.refresh_token)
db.session.add(new_user)
db.session.commit()
คุณอาจบันทึกข้อมูลเข้าสู่ระบบลงในฐานข้อมูล 2 กรณี ได้แก่ เมื่อผู้ใช้กลับมาที่แอปพลิเคชันของคุณเมื่อโฟลว์การให้สิทธิ์เสร็จสมบูรณ์ และเมื่อเรียก API ซึ่งเป็นตำแหน่งที่เราตั้งค่าคีย์ credentials ของเซสชันไว้ก่อนหน้านี้
เรียก save_user_credentials ที่ส่วนท้ายของเส้นทาง callback เก็บออบเจ็กต์ user_info ไว้แทนที่จะดึงเฉพาะชื่อของผู้ใช้
# The flow is complete! We'll use the credentials to fetch the user's info.
user_info_service = googleapiclient.discovery.build(
serviceName="oauth2", version="v2", credentials=credentials)
user_info = user_info_service.userinfo().get().execute()
flask.session["username"] = user_info.get("name")
save_user_credentials(credentials, user_info)
นอกจากนี้ คุณควรจะอัปเดตข้อมูลเข้าสู่ระบบหลังจากเรียก API ในกรณีนี้ คุณสามารถระบุข้อมูลเข้าสู่ระบบที่อัปเดตแล้วเป็นอาร์กิวเมนต์ลงในเมธอด save_user_credentials
# Save credentials in case access token was refreshed.
flask.session["credentials"] = credentials_to_dict(credentials)
save_user_credentials(credentials)
Java
ก่อนอื่น ให้กำหนดเมธอดที่จัดเก็บหรืออัปเดตออบเจ็กต์ User ในฐานข้อมูล H2
/** Adds or updates a user in the database.
* @param credential the credentials object to save or update in the database.
* @param userinfo the userinfo object to save or update in the database.
* @param session the current session.
*/
public void saveUser(Credential credential, Userinfo userinfo, HttpSession session) {
User storedUser = null;
if (session != null && session.getAttribute("login_hint") != null) {
storedUser = getUser(session.getAttribute("login_hint").toString());
}
if (storedUser != null) {
if (userinfo != null) {
storedUser.setId(userinfo.getId());
storedUser.setEmail(userinfo.getEmail());
}
userRepository.save(storedUser);
} else if (credential != null && userinfo != null) {
User newUser = new User(
userinfo.getId(),
userinfo.getEmail(),
);
userRepository.save(newUser);
}
}
คุณอาจบันทึกข้อมูลเข้าสู่ระบบลงในฐานข้อมูล 2 กรณี ได้แก่ เมื่อผู้ใช้กลับมาที่แอปพลิเคชันของคุณเมื่อโฟลว์การให้สิทธิ์เสร็จสมบูรณ์ และเมื่อเรียก API ซึ่งเป็นตำแหน่งที่เราตั้งค่าคีย์ credentials ของเซสชันไว้ก่อนหน้านี้
เรียก saveUser ที่ส่วนท้ายของเส้นทาง /callback คุณควรเก็บออบเจ็กต์ user_info ไว้แทนที่จะดึงเฉพาะอีเมลของผู้ใช้
/** This is the end of the auth flow. We should save user info to the database. */
Userinfo userinfo = authService.getUserInfo(credentials);
saveUser(credentials, userinfo, session);
นอกจากนี้ คุณควรจะอัปเดตข้อมูลเข้าสู่ระบบหลังจากเรียก API ในกรณีนี้ คุณสามารถระบุข้อมูลเข้าสู่ระบบที่อัปเดตแล้วเป็นอาร์กิวเมนต์ลงในเมธอด saveUser
/** Save credentials in case access token was refreshed. */
saveUser(credentials, null, session);
ข้อมูลเข้าสู่ระบบที่หมดอายุ
โปรดทราบว่าโทเค็นการรีเฟรชอาจไม่ถูกต้องด้วยเหตุผลบางประการ ซึ่งรวมถึงกรณีต่อไปนี้
- ไม่ได้ใช้โทเค็นการรีเฟรชมา 6 เดือนแล้ว
- ผู้ใช้เพิกถอนสิทธิ์เข้าถึงของแอป
- ผู้ใช้เปลี่ยนรหัสผ่าน
- ผู้ใช้เป็นสมาชิกขององค์กร Google Cloud ที่มีนโยบายการควบคุมเซสชันมีผลบังคับใช้
รับโทเค็นใหม่โดยส่งผู้ใช้ผ่านโฟลว์การให้สิทธิ์อีกครั้งหากข้อมูลเข้าสู่ระบบของผู้ใช้ไม่ถูกต้อง
เปลี่ยนเส้นทางผู้ใช้โดยอัตโนมัติ
แก้ไขเส้นทาง Landing Page ของส่วนเสริมเพื่อตรวจหาว่าผู้ใช้เคยให้สิทธิ์แอปพลิเคชันของเราหรือไม่ หากเคย ให้เปลี่ยนเส้นทางผู้ใช้ไปยังหน้าส่วนเสริมหลัก มิเช่นนั้น ให้แจ้งให้ผู้ใช้ลงชื่อเข้าใช้
Python
ตรวจสอบว่าได้สร้างไฟล์ฐานข้อมูลเมื่อเปิดแอปพลิเคชัน แทรกข้อมูลต่อไปนี้ลงในตัวเริ่มต้นโมดูล (เช่น webapp/__init__.py ในตัวอย่างที่เราให้ไว้) หรือในเมธอดหลักที่เปิดเซิร์ฟเวอร์
# Initialize the database file if not created.
if not os.path.exists(DATABASE_FILE_NAME):
db.create_all()
จากนั้นเมธอดของคุณควรจัดการพารามิเตอร์การค้นหา login_hint ตามที่
กล่าวไว้ข้างต้น จากนั้นโหลดข้อมูลเข้าสู่ระบบที่จัดเก็บไว้ หากผู้ใช้รายนี้เคยเข้าชมแอปพลิเคชันมาก่อน คุณจะทราบว่าผู้ใช้รายนี้เคยเข้าชมแอปพลิเคชันมาก่อนหากได้รับ login_hint
ดึงข้อมูลเข้าสู่ระบบที่จัดเก็บไว้สำหรับผู้ใช้รายนี้และโหลดลงในเซสชัน
stored_credentials = get_credentials_from_storage(login_hint)
# If we have stored credentials, store them in the session.
if stored_credentials:
# Load the client secrets file contents.
client_secrets_dict = json.load(
open(CLIENT_SECRETS_FILE)).get("web")
# Update the credentials in the session.
if not flask.session.get("credentials"):
flask.session["credentials"] = {}
flask.session["credentials"] = {
"token": stored_credentials.access_token,
"refresh_token": stored_credentials.refresh_token,
"token_uri": client_secrets_dict["token_uri"],
"client_id": client_secrets_dict["client_id"],
"client_secret": client_secrets_dict["client_secret"],
"scopes": SCOPES
}
# Set the username in the session.
flask.session["username"] = stored_credentials.display_name
สุดท้าย ให้เปลี่ยนเส้นทางผู้ใช้ไปยังหน้าลงชื่อเข้าใช้หากเราไม่มีข้อมูลเข้าสู่ระบบของผู้ใช้ หากมี ให้เปลี่ยนเส้นทางผู้ใช้ไปยังหน้าส่วนเสริมหลัก
if "credentials" not in flask.session or \
flask.session["credentials"]["refresh_token"] is None:
return flask.render_template("authorization.html")
return flask.render_template(
"addon-discovery.html",
message="You've reached the addon discovery page.")
Java
ไปที่เส้นทาง Landing Page ของส่วนเสริม (/addon-discovery ในตัวอย่างที่เราให้ไว้) ดังที่ กล่าวไว้ข้างต้น ตำแหน่งนี้คือตำแหน่งที่คุณจัดการ login_hint
พารามิเตอร์การค้นหา
ก่อนอื่น ให้ตรวจสอบว่ามีข้อมูลเข้าสู่ระบบอยู่ในเซสชันหรือไม่ หากไม่มี ให้เปลี่ยนเส้นทางผู้ใช้ผ่านโฟลว์การตรวจสอบสิทธิ์โดยการเรียกเมธอด startAuthFlow
/** Check if the credentials exist in the session. The session could have
* been cleared when the user clicked the Sign-Out button, and the expected
* behavior after sign-out would be to display the sign-in page when the
* iframe is opened again. */
if (session.getAttribute("credentials") == null) {
return startAuthFlow(model);
}
จากนั้นโหลดผู้ใช้จากฐานข้อมูล H2 หากผู้ใช้รายนี้เคยเข้าชมแอปพลิเคชันมาก่อน ผู้ใช้รายนี้เคยเข้าชมแอปพลิเคชันมาก่อนหากคุณได้รับพารามิเตอร์การค้นหา login_hint หากผู้ใช้มีอยู่ในฐานข้อมูล H2 ให้โหลดข้อมูลเข้าสู่ระบบจากพื้นที่เก็บข้อมูลเข้าสู่ระบบที่ตั้งค่าไว้ก่อนหน้านี้ และตั้งค่าข้อมูลเข้าสู่ระบบในเซสชัน หากไม่ได้รับข้อมูลเข้าสู่ระบบจากพื้นที่เก็บข้อมูลข้อมูลเข้าสู่ระบบ ให้เปลี่ยนเส้นทางผู้ใช้ผ่านโฟลว์การตรวจสอบสิทธิ์โดยการเรียก startAuthFlow
/** At this point, we know that credentials exist in the session, but we
* should update the session credentials with the credentials in persistent
* storage in case they were refreshed. If the credentials in persistent
* storage are null, we should navigate the user to the authorization flow
* to obtain persisted credentials. */
User storedUser = getUser(login_hint);
if (storedUser != null) {
Credential credential = authService.loadFromCredentialDataStore(login_hint);
if (credential != null) {
session.setAttribute("credentials", credential);
} else {
return startAuthFlow(model);
}
}
สุดท้าย ให้เปลี่ยนเส้นทางผู้ใช้ไปยังหน้า Landing Page ของส่วนเสริม
/** Finally, if there are credentials in the session and in persistent
* storage, direct the user to the addon-discovery page. */
return "addon-discovery";
ทดสอบส่วนเสริม
ลงชื่อเข้าใช้ Google Classroom ในฐานะผู้ใช้ทดสอบประเภท ครู ไปที่แท็บงานของชั้นเรียน แล้วสร้างงาน ใหม่ คลิกปุ่มส่วนเสริม ใต้พื้นที่ข้อความ แล้วเลือกส่วนเสริม iframe จะเปิดขึ้นและส่วนเสริมจะโหลด URI การตั้งค่าสิ่งที่แนบ ที่คุณระบุไว้ใน หน้าการกำหนดค่าแอปของ Google Workspace Marketplace SDK
ยินดีด้วย คุณพร้อมที่จะไปยังขั้นตอนถัดไปแล้ว นั่นคือการสร้างสิ่งที่แนบ และระบุบทบาทของผู้ใช้