التعامل مع عمليات تسجيل الدخول المتكرّرة

هذه هي الجولة التفصيلية الثالثة في إضافات Classroom. لسلسلة الجولات التفصيلية.

في هذه الجولة التفصيلية، يمكنك التعامل مع الزيارات المتكررة إلى إضافتنا بشكل تلقائي استرداد بيانات الاعتماد التي تم منحها للمستخدم سابقًا. ويمكنك بعد ذلك توجيه المستخدمين إلى يمكن من خلالها إصدار طلبات البيانات من واجهة برمجة التطبيقات فورًا. هذا الحقل مطلوب السلوك في إضافات Classroom

خلال هذه الجولة التفصيلية، يمكنك إكمال ما يلي:

  • استخدام التخزين الثابت لبيانات اعتماد المستخدم لدينا
  • استعِد مَعلمة طلب البحث لإضافة "login_hint" وقيِّمها. هذا هو معرّف Google الفريد للمستخدم الذي سجّل الدخول.

بعد الانتهاء، يمكنك منح المستخدمين الإذن بالكامل في تطبيق الويب وإصدار مكالمات واجهات Google APIs.

فهم مَعلمات طلب البحث لإطار iframe

يُحمِّل Classroom معرّف الموارد المنتظم (URI) لإعداد المرفق الخاص بإضافتك عند فتح. الفصل الدراسي تُرفِق العديد من مَعلمات طلب البحث 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 الخاص بميزة "اقتراحات":

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

Java

انتقِل إلى المسار المقصود للإضافة في فئة وحدة التحكّم. (/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_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()

Java

تحديد مخطط المستخدم

تحتوي User على السمات التالية:

  • id: رقم تعريف Google للمستخدم يجب أن يتطابق هذا العنصر مع القيمة المقدّمة في معلَمة طلب بحث واحدة (login_hint)
  • email: عنوان البريد الإلكتروني الخاص بالمستخدم

أنشِئ ملف schema.sql في دليل resources الخاص بالوحدة. موسم الربيع يقرأ هذا الملف وينشئ مخططًا لقاعدة البيانات وفقًا لذلك. حدِّد الجدول باسم الجدول و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 لإنشاء مستخدم أو تعديله في قاعدة البيانات. إِنْتَ أيضًا على الطريقة getters و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 التي تمثل المدعومة في التمهيد الربيعي. تستخدم قاعدة البيانات هذه أيضًا في عمليات الفيديوهات التفصيلية لتخزين المعلومات المعلومات. يتطلب إعداد قاعدة بيانات 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 ID المميز] للحصول على مزيد من المعلومات.

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

بيانات الاعتماد الخاصة بمتجر Play

هناك سيناريوهان عند تخزين بيانات الاعتماد. إذا سبق أن تم ضبط 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 كأحد اختبارات المعلّمين. المستخدمين. انتقِل إلى علامة التبويب الواجب الدراسي وأنشئ مهمة جديدة. (يُرجى النقر.) زر الإضافات أسفل مربّع النص ثم اختَر إضافتك. إطار iframe تفتح وتحمّل الإضافة عنوان URI لإعداد المرفق الذي حددته في صفحة إعداد التطبيق في حزمة تطوير البرامج (SDK) في Google Workspace Marketplace.

تهانينا! يمكنك الآن المتابعة إلى الخطوة التالية: إنشاء المرفقات. وتحديد دور المستخدم