هذه هي الجولةالثالثة في سلسلة جولاتنا التفصيلية حول إضافات Classroom.
في هذه الجولة الإرشادية، يمكنك معالجة الزيارات المتكررة إلى إضافتنا من خلال استرداد بيانات اعتماد المستخدم التي منحها سابقًا تلقائيًا. يمكنك بعد ذلك توجيه المستخدمين إلى صفحات يمكنهم من خلالها إصدار طلبات واجهة برمجة التطبيقات على الفور. هذا سلوك مطلوب لإضافات Classroom.
خلال هذه الجولة الإرشادية، يمكنك إكمال ما يلي:
- تنفيذ سعة تخزين دائمة لبيانات اعتماد المستخدمين
- استرداد مَعلمة طلب البحث الإضافية
login_hint
وتقييمها هذا هو رقم تعريف Google الفريد للمستخدم الذي سجّل الدخول.
بعد الانتهاء، يمكنك تفويض المستخدمين بالكامل في تطبيق الويب الخاص بك وإصدار طلبات إلى Google APIs.
فهم مَعلمات طلب البحث في إطار iframe
تحمِّل Classroom عنوان URL لإعداد المرفق الخاص بإضافتك عند
فتحها. يلصِق 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 للحصول على وصف detailed لهذا السيناريو.
هناك خمس مَعلمات طلب بحث محتمَلة لعنوان URL الخاص بمستند الاستكشاف:
courseId
: رقم تعريف الدورة التدريبية الحالية في ClassroomitemId
: رقم تعريف عنصر البث الذي يعدّله المستخدم أو ينشئه.-
itemType
: نوع عنصر البث الذي ينشئه المستخدم أو يعدّله، وهو أحد الأنواع التالية:courseWork
أوcourseWorkMaterial
أوannouncement
. addOnToken
: رمز مميّز يُستخدَم للسماح ببعض إجراءات إضافة Classroom.login_hint
: رقم تعريف المستخدم الحالي على Google
يتناول هذا الدليل التفصيلي login_hint
. يتم توجيه المستخدمين استنادًا إلى ما إذا كانت مَعلمة طلب البحث هذه متوفرة، إما إلى عملية التفويض في حال عدم توفّرها، أو إلى
صفحة اكتشاف الإضافة في حال توفّرها.
الوصول إلى مَعلمات طلب البحث
يتمّ تمرير مَعلمات طلب البحث إلى تطبيق الويب في سلسلة معرّف الموارد المنتظم (URI). تخزِّن هذه القيم في جلستك، ويتم استخدامها في عملية التفويض و لتخزين معلومات عن المستخدم واستردادها. ولا يتم تمرير مَعلمات طلبات البحث هذه إلا عند فتح الإضافة لأول مرة.
Python
انتقِل إلى تعريفات مسارات Flask (routes.py
إذا كنت تتبع المثال المقدَّم). في أعلى مسار الإحالة الناجحة الإضافية
(/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
انتقِل إلى مسار الصفحة المقصودة للإضافات في فئة وحدة التحكّم
(/addon-discovery
في AuthController.java
في المثال المقدَّم). في
بداية هذا المسار، استرجع مَعلمةlogin_hint
query
واحفظها.
/** 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_hint
.display_name
: الاسم الأول واسم العائلة للمستخدم، مثل "أحمد كمال".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_hint
.email
: عنوان البريد الإلكتروني للمستخدم.
أنشئ ملف 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
.
يُرجى العلم أنّ مثال الرمز البرمجي يتضمّن وظائف إنشاء وإعداد لسمتَي. يتم استخدام أسلوب الإنشاء وطرق الضبط في
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
الحفاظ على البيانات المخزّنة في قاعدة البيانات عند إعادة تشغيل التطبيق.
لمحو قاعدة البيانات في كل مرة تتم فيها إعادة تشغيل التطبيق، اضبط قيمة
هذا الإعداد على 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;
}
تخزين بيانات الاعتماد
هناك سيناريوهان عند تخزين بيانات الاعتماد. إذا كان 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()
هناك حالتان قد تحفظ فيهما بيانات الاعتماد في
قاعدة بياناتك: عندما يعود المستخدم إلى تطبيقك في نهاية تدفق
التفويض وعند إصدار طلب بيانات من واجهة برمجة التطبيقات. هذه هي الأماكن التي سبق أن أعددنا فيها مفتاح الجلسة 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)
يجب أيضًا تعديل بيانات الاعتماد بعد إجراء طلبات البيانات من واجهة برمجة التطبيقات. في
هذا الحالة، يمكنك تقديم بيانات الاعتماد المعدَّلة كوسيطات لطريقة
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);
}
}
هناك حالتان قد تحفظ فيهما بيانات الاعتماد في
قاعدة بياناتك: عندما يعود المستخدم إلى تطبيقك في نهاية تدفق
التفويض وعند إصدار طلب بيانات من واجهة برمجة التطبيقات. هذه هي الأماكن التي سبق أن أعددنا فيها مفتاح الجلسة 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);
يجب أيضًا تعديل بيانات الاعتماد بعد إجراء طلبات البيانات من واجهة برمجة التطبيقات. في
هذا السياق، يمكنك تقديم بيانات الاعتماد المعدَّلة كوسيطات لطريقة saveUser
.
/** Save credentials in case access token was refreshed. */
saveUser(credentials, null, session);
بيانات اعتماد منتهية الصلاحية
يُرجى العِلم أنّ هناك بعض الأسباب التي قد تؤدي إلى أن تصبح رموز إعادة التنشيط غير صالحة. وتشمل هذه العناصر ما يلي:
- لم يتم استخدام الرمز المميّز لإعادة التحميل منذ ستة أشهر.
- إذا ألغى المستخدم أذونات الوصول إلى تطبيقك
- يغيّر المستخدم كلمات المرور.
- ينتمي المستخدم إلى مؤسسة على Google Cloud لديها سياسات التحكّم في الجلسات فعالة.
يمكنك الحصول على رموز مميّزة جديدة من خلال توجيه المستخدم إلى عملية التفويض مرة أخرى إذا أصبحت بيانات اعتماده غير صالحة.
توجيه المستخدم تلقائيًا
تعديل مسار الصفحة المقصودة للإضافات لرصد ما إذا كان المستخدم قد فوّض في السابق تطبيقنا إذا كان الأمر كذلك، يمكنك توجيههم إلى صفحة الإضافة الرئيسية. وبخلاف ذلك، اطلب منه تسجيل الدخول.
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
انتقِل إلى مسار الصفحة المقصودة للإضافات (/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);
}
}
أخيرًا، وجِّه المستخدم إلى الصفحة المقصودة للإضافة.
/** 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 بصفتك أحد المعلّمين المشارِكين في الاختبار. انتقِل إلى علامة التبويب الواجب الدراسي وأنشِئ واجبًا دراسيًا جديدًا. انقر على زر الإضافات أسفل منطقة النص، ثم اختَر الإضافة. يتم فتح الإطار المتداخل ويحمِّل التطبيق الإضافة عنوان URL لإعداد المرفق الذي حدّدته في صفحة إعداد التطبيق في حزمة تطوير البرامج (SDK) في Google Workspace Marketplace.
تهانينا! أنت الآن جاهز للانتقال إلى الخطوة التالية: إنشاء مرفقات وتحديد دور المستخدم.