هذا هو الإجراء الثالث في سلسلة الإجراءات التفصيلية الخاصة بإضافات Classroom.
في هذا الإجراء، ستتعامل مع الزيارات المتكررة إلى الإضافة من خلال استرداد بيانات اعتماد المستخدم التي تم منحها سابقًا تلقائيًا. بعد ذلك، ستوجّه المستخدمين إلى الصفحات التي يمكنهم من خلالها إرسال طلبات واجهة برمجة التطبيقات على الفور. هذا السلوك مطلوب لإضافات Classroom.
أثناء هذا الإجراء، ستُكمل ما يلي:
- تنفيذ مساحة تخزين دائمة لبيانات اعتماد المستخدم.
- استرداد وتقييم مَعلمة طلب البحث
login_hintالخاصة بالإضافة. هذا هو رقم تعريف فريد في Google للمستخدم الذي سجّل الدخول.
بعد الانتهاء، يمكنك منح المستخدمين الإذن بالكامل في تطبيق الويب وإجراء طلبات إلى واجهات برمجة تطبيقات Google.
.فهم مَعلمات طلب البحث في 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 هي أرقام تعريف سلسلة. يُرجى الاطّلاع على دليل iframes للحصول على
وصف تفصيلي لهذا السيناريو.
هناك خمس مَعلمات طلب بحث محتمَلة لعنوان URL للاكتشاف:
courseId: رقم تعريف دورة Classroom الحالية.itemId: رقم تعريف عنصر ساحة المشاركات الذي يعدّله المستخدم أو ينشئه.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()
جافا
انتقِل إلى مسار الصفحة المقصودة للإضافة في فئة التحكّم
(/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"),
جافا
انتقِل إلى الطريقة 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. سبق لك حفظ بيانات الاعتماد هذه في الجلسة، ولكن عليك تخزينها للتعامل مع الزيارات المتكررة.
تحديد مخطط User وإعداد قاعدة البيانات
يمكنك إعداد مخطط قاعدة بيانات لـ User.
Python
تحديد مخطط User
يحتوي 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
يشير هذا إلى ملف 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()
جافا
تحديد مخطط User
يحتوي 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 للإشارة إلى أنّ هذا هو كائن Java عادي يمكن حفظه في قاعدة البيانات. أضِف التعليق التوضيحي @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 Client
libraries للتحقّق من id_token. يُرجى الاطّلاع على [صفحة Google Identity حول
التحقّق من رمز تعريف 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)
جافا
في الفئة 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)
جافا
أولاً، حدِّد طريقة تخزّن كائن 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.")
جافا
انتقِل إلى مسار الصفحة المقصودة للإضافة (/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 بصفتك أحد مستخدمي الاختبار المعلّمين. انتقِل إلى علامة التبويب الواجب الدراسي وأنشِئ مهمة جديدة. انقر على الزر الإضافات أسفل مساحة النص، ثم اختَر الإضافة. يفتح iframe وتحمّل الإضافة معرّف URI لإعداد المرفق الذي حدّدته في صفحة إعدادات التطبيق في حزمة تطوير البرامج (SDK) في Google Workspace Marketplace.
تهانينا! أنت مستعد للمتابعة إلى الخطوة التالية، وهي إنشاء المرفقات وتحديد دور المستخدم.