ورودهای تکراری را مدیریت کنید

این سومین راهنمای گام به گام از مجموعه راهنمای افزونه‌های Classroom است.

در این راهنما، شما با بازیابی خودکار اعتبارنامه‌های قبلاً اعطا شده به کاربر، بازدیدهای مکرر از افزونه ما را مدیریت می‌کنید. سپس کاربران را به صفحاتی هدایت می‌کنید که می‌توانند بلافاصله درخواست‌های API را از آنجا ارسال کنند. این یک رفتار الزامی برای افزونه‌های Classroom است.

در طول این راهنمای گام به گام، موارد زیر را تکمیل می‌کنید:

  • فضای ذخیره‌سازی پایدار را برای اعتبارنامه‌های کاربران خود پیاده‌سازی کنید.
  • پارامتر کوئری افزونه login_hint را بازیابی و ارزیابی کنید. این یک شماره شناسه گوگل منحصر به فرد برای کاربر وارد شده است.

پس از اتمام، می‌توانید کاربران را در برنامه وب خود به طور کامل مجاز کنید و فراخوانی‌هایی را به APIهای گوگل انجام دهید.

پارامترهای کوئری 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 اکتشاف وجود دارد:

  • courseId : شناسه‌ی دوره‌ی فعلی Classroom.
  • itemId : شناسه آیتم استریم که کاربر در حال ویرایش یا ایجاد آن است.
  • itemType : نوع آیتم استریمی که کاربر در حال ایجاد یا ویرایش آن است، یکی از انواع courseWork ، courseWorkMaterial یا announcement .
  • addOnToken : توکنی که برای مجاز کردن برخی از اقدامات افزونه Classroom استفاده می‌شود.
  • login_hint : شناسه گوگل کاربر فعلی.

این راهنما به login_hint می‌پردازد. کاربران بر اساس اینکه آیا این پارامتر پرس‌وجو ارائه شده است یا خیر، هدایت می‌شوند، یا در صورت عدم وجود به جریان مجوز، یا در صورت وجود به صفحه کشف افزونه.

دسترسی به پارامترهای پرس و جو

پارامترهای پرس‌وجو در رشته‌ی URI به برنامه‌ی وب شما ارسال می‌شوند. این مقادیر را در جلسه‌ی خود ذخیره کنید؛ آن‌ها در جریان مجوزدهی و برای ذخیره و بازیابی اطلاعات مربوط به کاربر استفاده می‌شوند. این پارامترهای پرس‌وجو فقط زمانی ارسال می‌شوند که افزونه برای اولین بار باز شود.

پایتون

به تعاریف مسیرهای 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 (در صورت وجود) در session ذخیره شده باشد. این مکان مناسبی برای ذخیره این مقادیر است؛ آنها موقتی هستند و با باز شدن افزونه، مقادیر جدید دریافت می‌کنید.

# 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 (در صورت وجود) در session ذخیره شده باشد. این مکان مناسبی برای ذخیره این مقادیر است؛ آنها موقتی هستند و با باز شدن افزونه، مقادیر جدید دریافت می‌کنید.

/** 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 باید به سرورهای احراز هویت گوگل نیز ارسال شود. این کار فرآیند احراز هویت را تسهیل می‌کند؛ اگر برنامه شما بداند کدام کاربر در حال تلاش برای احراز هویت است، سرور با پر کردن فیلد ایمیل در فرم ورود، از این راهنما برای ساده‌سازی جریان ورود استفاده می‌کند.

پایتون

به مسیر احراز هویت در فایل سرور 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 و آرگومان را به سازنده‌ی آدرس مجوز اضافه کنید.

String authUrl = flow
    .newAuthorizationUrl()
    .setState(state)
    .set("login_hint", login_hint)
    .setRedirectUri(REDIRECT_URI)
    .build();

افزودن فضای ذخیره‌سازی پایدار برای اعتبارنامه‌های کاربر

اگر هنگام بارگذاری افزونه، login_hint به عنوان پارامتر کوئری دریافت کنید، نشان می‌دهد که کاربر قبلاً جریان احراز هویت را برای برنامه ما تکمیل کرده است. شما باید به جای مجبور کردن او به ورود مجدد، اطلاعات احراز هویت قبلی‌اش را بازیابی کنید.

به یاد داشته باشید که پس از اتمام جریان مجوز، یک توکن به‌روزرسانی دریافت کردید. این توکن را ذخیره کنید؛ می‌توانید از آن برای دریافت توکن دسترسی که کوتاه‌مدت است و برای استفاده از APIهای گوگل ضروری است، دوباره استفاده کنید. شما قبلاً این اعتبارنامه‌ها را در جلسه ذخیره کرده‌اید، اما برای مدیریت بازدیدهای مکرر، باید اعتبارنامه‌ها را ذخیره کنید.

طرحواره کاربر را تعریف کنید و پایگاه داده را تنظیم کنید

یک طرحواره پایگاه داده برای یک User تنظیم کنید.

پایتون

تعریف طرحواره کاربر

یک User شامل ویژگی‌های زیر است:

  • id : شناسه گوگل کاربر. این باید با مقادیر ارائه شده در پارامتر کوئری login_hint مطابقت داشته باشد.
  • display_name : نام و نام خانوادگی کاربر، مانند "Alex Smith".
  • email : آدرس ایمیل کاربر.
  • portrait_url : آدرس تصویر پروفایل کاربر.
  • refresh_token : توکن refresh که قبلاً به دست آمده است.

این مثال، ذخیره‌سازی را با استفاده از SQLite پیاده‌سازی می‌کند که به صورت بومی توسط پایتون پشتیبانی می‌شود. این مثال از ماژول 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()

جاوا

تعریف طرحواره کاربر

یک User شامل ویژگی‌های زیر است:

  • id : شناسه گوگل کاربر. این باید با مقدار ارائه شده در پارامتر کوئری login_hint مطابقت داشته باشد.
  • email : آدرس ایمیل کاربر.

یک فایل schema.sql در دایرکتوری resources ماژول ایجاد کنید. Spring این فایل را می‌خواند و بر اساس آن یک schema برای پایگاه داده ایجاد می‌کند. جدول را با نام جدول، 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
);

یک کلاس جاوا برای تعریف مدل User model) برای پایگاه داده ایجاد کنید. در مثال ارائه شده، این 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 ، مسیری را برای فایلی که کلاس credential در آن ذخیره شده است، تعریف کنید. در این مثال، فایل در دایرکتوری /credentialStore ایجاد می‌شود. مسیر مخزن credential را به .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) را به‌روزرسانی کنید. پیش از این، این متد، اعتبارنامه‌ها را بدون ذخیره آنها در جایی دریافت می‌کرد. این متد را به‌روزرسانی کنید تا اعتبارنامه‌ها را در پایگاه داده‌ای که توسط شناسه کاربر ایندکس شده است، ذخیره کند.

شناسه کاربر را می‌توان با استفاده از id_token از شیء TokenResponse دریافت کرد، اما ابتدا باید تأیید شود. در غیر این صورت، برنامه‌های کلاینت ممکن است بتوانند با ارسال شناسه‌های کاربری اصلاح‌شده به سرور، هویت کاربران را جعل کنند. توصیه می‌شود از کتابخانه‌های کلاینت API گوگل برای تأیید id_token استفاده کنید. برای اطلاعات بیشتر به [صفحه هویت گوگل در مورد تأیید توکن شناسه گوگل] مراجعه کنید.

// 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 تعریف کنید. در پارامتر کوئری login_hint یک id به شما ارائه شده است که می‌توانید از آن برای بازیابی یک رکورد کاربر خاص استفاده کنید.

پایتون

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 جدید ایجاد کرده و آن را به پایگاه داده اضافه کنید.

پایتون

ابتدا یک متد کاربردی تعریف کنید که رفتار ذخیره‌سازی یا به‌روزرسانی را پیاده‌سازی کند.

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

دو حالت وجود دارد که می‌توانید اعتبارنامه‌ها را در پایگاه داده خود ذخیره کنید: زمانی که کاربر در پایان جریان مجوز به برنامه شما برمی‌گردد و هنگام صدور فراخوانی 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)

جاوا

ابتدا متدی تعریف کنید که یک شیء 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);
    }
}

دو حالت وجود دارد که می‌توانید اعتبارنامه‌ها را در پایگاه داده خود ذخیره کنید: زمانی که کاربر در پایان جریان مجوز به برنامه شما برمی‌گردد و هنگام صدور فراخوانی 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);

اعتبارنامه‌های منقضی شده

توجه داشته باشید که چند دلیل وجود دارد که ممکن است توکن‌های به‌روزرسانی نامعتبر شوند. این دلایل عبارتند از:

  • توکن به‌روزرسانی به مدت شش ماه استفاده نشده است.
  • کاربر مجوزهای دسترسی برنامه شما را لغو می‌کند.
  • کاربر رمزهای عبور را تغییر می‌دهد.
  • کاربر متعلق به یک سازمان Google Cloud است که سیاست‌های کنترل جلسه در آن اعمال می‌شود.

در صورت نامعتبر شدن اعتبارنامه‌های کاربر، با ارسال مجدد کاربر از طریق جریان مجوز، توکن‌های جدید را به دست آورید.

هدایت خودکار کاربر

مسیر ورود افزونه را تغییر دهید تا مشخص شود که آیا کاربر قبلاً برنامه ما را تأیید کرده است یا خیر. در این صورت، او را به صفحه اصلی افزونه هدایت کنید. در غیر این صورت، از او بخواهید که وارد سیستم شود.

پایتون

مطمئن شوید که فایل پایگاه داده هنگام اجرای برنامه ایجاد شده است. کد زیر را در یک مقداردهی اولیه ماژول (مانند webapp/__init__.py در مثال ارائه شده ما) یا در متد main که سرور را اجرا می‌کند، وارد کنید.

# 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 شوید. به برگه Classwork بروید و یک تکلیف جدید ایجاد کنید. روی دکمه Add-ons در زیر قسمت متن کلیک کنید، سپس افزونه خود را انتخاب کنید. iframe باز می‌شود و افزونه، URI تنظیمات پیوست را که در صفحه پیکربندی برنامه Google Workspace Marketplace SDK مشخص کرده‌اید، بارگذاری می‌کند.

تبریک! شما آماده‌اید تا به مرحله بعدی بروید: ایجاد پیوست‌ها و شناسایی نقش کاربر .