반복 로그인 처리

클래스룸 부가기능의 세 번째 둘러보기입니다. 오신 것을 환영합니다

이 둘러보기에서는 Google의 부가기능에 대한 반복 방문을 자동으로 처리하여 사용자 인증 정보를 가져올 수 있습니다 그런 다음 사용자를 즉시 API 요청을 보낼 수 있는 페이지 필수 항목입니다. 클래스룸 부가기능의 동작

이 둘러보기 과정에서 다음을 완료합니다.

  • 사용자 인증 정보를 위한 영구 저장소를 구현합니다.
  • login_hint 부가기능 쿼리 매개변수를 가져오고 평가합니다. 이것은 로그인한 사용자의 고유 Google ID 번호입니다.
를 통해 개인정보처리방침을 정의할 수 있습니다.

완료되면 웹 앱에서 사용자를 완전히 승인하고 Google API

iframe 쿼리 매개변수 이해

클래스룸에서 부가기능의 첨부파일 설정 URI를 로드함 있습니다. 클래스룸 여러 GET 쿼리 매개변수를 URI에 추가합니다. 유용한 기억하실 것입니다. 예를 들어 첨부파일 검색 URI가 https://example.com/addon, 클래스룸은 소스 URL이 https://example.com/addon?courseId=XXX&itemId=YYY&itemType=courseWork&addOnToken=ZZZ, 여기서 XXX, YYY, ZZZ는 문자열 ID입니다. iframe 가이드에서 자세히 설명해 드리겠습니다.

검색 URL에 사용할 수 있는 쿼리 매개변수는 5가지입니다.

  • courseId: 현재 클래스룸 과정의 ID입니다.
  • itemId: 사용자가 수정하거나 만들고 있는 스트림 항목의 ID입니다.
  • itemType: 사용자가 만들거나 수정하는 스트림 항목의 종류로, 다음 중 하나입니다. courseWork, courseWorkMaterial 또는 announcement입니다.
  • addOnToken: 특정 권한을 승인하는 데 사용되는 토큰 클래스룸 부가기능 작업
  • login_hint: 현재 사용자의 Google ID입니다.
를 통해 개인정보처리방침을 정의할 수 있습니다.

이 둘러보기에서는 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()

자바

컨트롤러 클래스에서 부가기능 시작 경로로 이동 (제공된 예에서는 AuthController.java/addon-discovery) 위치 이 경로의 시작 부분. 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"),

자바

AuthService.java 클래스의 authorize() 메서드로 이동합니다. 추가 login_hint를 메서드에 매개변수로 사용하고 login_hint를 추가합니다. 및 인수를 승인 URL 작성 도구에 추가합니다.

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

사용자 인증 정보용 영구 스토리지 추가

부가기능이 로드될 때 login_hint가 쿼리 매개변수로 수신되면 해당 앱에 대한 승인 절차를 이미 완료했음을 애플리케이션입니다. 기존 사용자 인증 정보를 강제로 가져오는 대신 다시 로그인하세요.

완료 시 갱신 토큰 사용할 수 있습니다. 이 토큰을 저장합니다. 액세스 토큰을 얻는 데 재사용됩니다. 이는 수명이 짧으며 Google API를 사용하는 데 필요합니다. 이전에 저장한 항목 이 사용자 인증 정보를 세션에서 얻게 되지만, 사용자 인증 정보를 저장하여 반복 방문을 처리할 수 있습니다.

사용자 스키마 정의 및 데이터베이스 설정

User의 데이터베이스 스키마를 설정합니다.

Python

사용자 스키마 정의

User에는 다음 속성이 포함됩니다.

  • id: 사용자의 Google ID입니다. 이 값은 login_hint 쿼리 매개변수
  • display_name: 사용자의 성과 이름(예: '홍길동')
  • email: 사용자의 이메일 주소입니다.
  • portrait_url: 사용자 프로필 사진의 URL입니다.
  • refresh_token: 이전에 획득한 갱신 토큰입니다.
를 통해 개인정보처리방침을 정의할 수 있습니다.

이 예에서는 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: 사용자의 Google ID입니다. 이 값은 login_hint 쿼리 매개변수
  • email: 사용자의 이메일 주소입니다.
를 통해 개인정보처리방침을 정의할 수 있습니다.

모듈의 resources 디렉터리에 schema.sql 파일을 생성합니다. 봄 는 이 파일을 읽고 그에 따라 데이터베이스의 스키마를 생성합니다. 테이블 이름, 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에서 구성한 해당 테이블 이름입니다.

코드 예에는 두 가지 생성자와 setter가 포함되어 있습니다. 속성 생성자와 setter는 AuthController.java: 데이터베이스에서 사용자를 만들거나 업데이트합니다. 나 필요에 따라 getter와 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; }
}

CRUD 작업을 처리하기 위해 UserRepository.java라는 인터페이스를 만듭니다. 데이터베이스로 전달됩니다 이 인터페이스는 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에서 지원됩니다 이 데이터베이스는 이후 클래스룸 관련 기타 문서 저장 둘러보기 확인할 수 있습니다 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;
}

다음을 포함하도록 AuthService.javagetFlow() 메서드를 업데이트합니다. GoogleAuthorizationCodeFlow Builder()setDataStoreFactory 메서드를 호출하고 getCredentialDataStore()를 호출하여 데이터 저장소를 설정합니다.

GoogleAuthorizationCodeFlow authorizationCodeFlow =
    new GoogleAuthorizationCodeFlow.Builder(
        HTTP_TRANSPORT,
        JSON_FACTORY,
        getClientSecrets(),
        getScopes())
    .setAccessType("offline")
    .setDataStoreFactory(getCredentialDataStore())
    .build();

다음으로, getAndSaveCredentials(String authorizationCode) 메서드를 업데이트합니다. 이전에는 이 방법을 사용하여 사용자 인증 정보를 저장하지 않고 액세스할 수 있습니다 사용자 인증 정보를 Datastore에 저장하도록 메서드를 업데이트합니다. 사용자 ID로 색인이 생성됩니다.

사용자 ID는 다음을 사용하여 TokenResponse 객체에서 가져올 수 있습니다. id_token이지만 먼저 반드시 인증을 받아야 합니다. 그렇지 않으면 응용 프로그램에 수정된 사용자를 전송하여 사용자를 가장할 수 있음 전송합니다. Google API 클라이언트 라이브러리를 사용하여 id_token의 유효성을 검사하세요. [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();

userId를 포함하도록 flow.createAndStoreCredential 호출을 업데이트합니다.

// 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 클래스에서 사용자를 검색하는 메서드를 정의합니다. 사용자 ID를 기반으로 데이터베이스를 생성할 수 있습니다

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

사용자 인증 정보를 데이터베이스: 사용자가 애플리케이션으로 돌아올 때 API 호출을 실행할 때 발생합니다. 이 두 가지는 이전에 세션 credentials 키를 설정한 경우

callback 경로의 끝에 있는 save_user_credentials에 전화를 겁니다. 이 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)

자바

먼저 H2에 User 객체를 저장하거나 업데이트하는 메서드를 정의합니다. 데이터베이스입니다.

/** 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 키를 설정한 경우

/callback 경로 끝에서 saveUser를 호출합니다. 포드의 user_info 객체를 사용하여 사용자 이메일을 추출할 수 있습니다.

/** This is the end of the auth flow. We should save user info to the database. */
Userinfo userinfo = authService.getUserInfo(credentials);
saveUser(credentials, userinfo, session);

또한 API 호출 후 사용자 인증 정보를 업데이트해야 합니다. 이 이 경우 업데이트된 사용자 인증 정보를 saveUser의 인수로 제공할 수 있습니다. 메서드를 사용하여 축소하도록 요청합니다.

/** Save credentials in case access token was refreshed. */
saveUser(credentials, null, session);

만료된 사용자 인증 정보

갱신 토큰이 유효하지 않게 되는 데는 몇 가지 이유가 있습니다. 예를 들면 다음과 같습니다.

  • 갱신 토큰이 6개월 동안 사용되지 않았습니다.
  • 사용자가 앱의 액세스 권한을 취소하는 경우
  • 사용자가 비밀번호를 변경합니다.
  • 사용자가 세션 제어를 사용할 수 있는 Google Cloud 조직에 속해 있습니다. 정책을 시행합니다

다음에 해당하는 경우 사용자를 승인 흐름을 통해 다시 전송하여 새 토큰을 획득합니다. 사용자 인증 정보가 무효화됩니다

사용자 자동 라우팅

사용자가 이전에 승인한 적이 있는지 감지하도록 부가기능 시작 경로를 수정합니다. 살펴보겠습니다 그렇다면 Google의 기본 부가기능 페이지로 연결하세요. 또는 메시지를 표시합니다. 로그인하세요.

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 클래스룸에 로그인합니다. 있습니다. 수업 과제 탭으로 이동하여 새 과제를 만듭니다. 클릭 텍스트 영역 아래의 부가기능 버튼을 누른 다음 사용 중인 부가기능을 선택합니다. iframe 그러면 부가기능이 연결 설정 URI를 Google Workspace Marketplace SDK의 앱 구성 페이지

축하합니다. 이제 다음 단계(첨부파일 만들기)를 진행할 준비가 되었습니다. 사용자 역할 식별을 참조하세요.