ลงชื่อเข้าใช้ให้ผู้ใช้

นี่คือคำแนะนำแบบทีละขั้นที่ 2 ในส่วนเสริมของ Classroom คำแนะนำแบบทีละขั้น

ในคำแนะนำแบบทีละขั้นนี้ คุณจะเพิ่ม Google Sign-In ลงในเว็บแอปพลิเคชัน นี่คือ ลักษณะการทำงานที่จำเป็นสำหรับส่วนเสริมของ Classroom ใช้ข้อมูลเข้าสู่ระบบจาก กระบวนการให้สิทธิ์นี้สำหรับการเรียก API ในอนาคตทั้งหมด

ในคำแนะนำแบบทีละขั้นนี้ คุณจะต้องดำเนินการต่อไปนี้ให้เสร็จสมบูรณ์

  • กำหนดค่าเว็บแอปให้เก็บรักษาข้อมูลเซสชันภายใน iframe
  • ใช้ขั้นตอนการลงชื่อเข้าใช้แบบเซิร์ฟเวอร์ต่อเซิร์ฟเวอร์ของ Google OAuth 2.0
  • เรียกใช้ OAuth 2.0 API
  • สร้างเส้นทางเพิ่มเติมเพื่อรองรับการให้สิทธิ์ การออกจากระบบ และการทดสอบ การเรียก API

เมื่อดำเนินการเสร็จแล้ว คุณจะให้สิทธิ์ผู้ใช้ในเว็บแอปได้อย่างเต็มที่ และออกการโทรไปยัง Google APIs

ทำความเข้าใจขั้นตอนการให้สิทธิ์

Google APIs ใช้โปรโตคอล OAuth 2.0 สำหรับการตรวจสอบสิทธิ์และการให้สิทธิ์ คำอธิบายแบบเต็มเกี่ยวกับการใช้งาน OAuth ของ Google มีอยู่ใน คู่มือ Google Identity OAuth

ข้อมูลเข้าสู่ระบบของแอปพลิเคชันได้รับการจัดการใน Google Cloud เมื่อแอปเหล่านี้ ได้สร้างกระบวนการ 4 ขั้นตอนในการตรวจสอบสิทธิ์และให้สิทธิ์ ผู้ใช้:

  1. ส่งคำขอการให้สิทธิ์ ระบุ URL เรียกกลับเป็นส่วนหนึ่งของคำขอนี้ เมื่อดำเนินการเสร็จแล้ว คุณจะได้รับ URL การให้สิทธิ์
  2. เปลี่ยนเส้นทางผู้ใช้ไปยัง URL การให้สิทธิ์ หน้าผลลัพธ์จะให้ข้อมูล ผู้ใช้สิทธิ์ที่แอปของคุณต้องการ และแจ้งให้อนุญาตการเข้าถึง เมื่อดำเนินการเสร็จแล้ว ระบบจะกำหนดเส้นทางผู้ใช้ไปยัง URL เรียกกลับ
  3. รับรหัสการให้สิทธิ์ในเส้นทางของ Callback แลกเปลี่ยน รหัสการให้สิทธิ์สำหรับโทเค็นเพื่อการเข้าถึงและโทเค็นการรีเฟรช
  4. เรียก Google API โดยใช้โทเค็น

รับข้อมูลเข้าสู่ระบบ OAuth 2.0

โปรดตรวจสอบว่าคุณได้สร้างและดาวน์โหลดข้อมูลรับรอง OAuth แล้วตามที่อธิบายไว้ใน หน้าภาพรวม โปรเจ็กต์ต้องใช้ข้อมูลเข้าสู่ระบบเหล่านี้เพื่อลงชื่อเข้าใช้ผู้ใช้

ใช้ขั้นตอนการให้สิทธิ์

เพิ่มตรรกะและเส้นทางลงในเว็บแอปของเราเพื่อให้ทราบถึงขั้นตอนที่อธิบายไว้ ซึ่งรวมถึง ฟีเจอร์เหล่านี้

  • เริ่มขั้นตอนการให้สิทธิ์เมื่อไปถึงหน้า Landing Page
  • ส่งคำขอการให้สิทธิ์และจัดการการตอบกลับของเซิร์ฟเวอร์การให้สิทธิ์
  • ล้างข้อมูลเข้าสู่ระบบที่จัดเก็บไว้
  • เพิกถอนสิทธิ์ของแอป
  • ทดสอบการเรียก API

เริ่มการให้สิทธิ์

แก้ไขหน้า Landing Page ของคุณเพื่อเริ่มต้นขั้นตอนการให้สิทธิ์ หากจำเป็น ส่วนเสริมมีได้ 2 สถานะ ซึ่งอาจมีโทเค็นที่บันทึกไว้ใน เซสชันปัจจุบัน หรือคุณต้องรับโทเค็นจากเซิร์ฟเวอร์ OAuth 2.0 ดำเนินการ ทดสอบการเรียก API ว่ามีโทเค็นในเซสชันหรือไม่ หรือแสดงข้อความแจ้งผู้ใช้ เพื่อลงชื่อเข้าใช้

Python

เปิดไฟล์ routes.py ขั้นแรก ตั้งค่าคงที่ 2-3 รายการและคุกกี้ของเรา การกำหนดค่าตามคำแนะนำด้านความปลอดภัยของ iframe

# The file that contains the OAuth 2.0 client_id and client_secret.
CLIENT_SECRETS_FILE = "client_secret.json"

# The OAuth 2.0 access scopes to request.
# These scopes must match the scopes in your Google Cloud project's OAuth Consent
# Screen: https://console.cloud.google.com/apis/credentials/consent
SCOPES = [
    "openid",
    "https://www.googleapis.com/auth/userinfo.profile",
    "https://www.googleapis.com/auth/userinfo.email",
    "https://www.googleapis.com/auth/classroom.addons.teacher",
    "https://www.googleapis.com/auth/classroom.addons.student"
]

# Flask cookie configurations.
app.config.update(
    SESSION_COOKIE_SECURE=True,
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SAMESITE="None",
)

ย้ายไปที่เส้นทาง Landing Page ของส่วนเสริม (นี่คือ /classroom-addon ในตัวอย่าง ) เพิ่มตรรกะเพื่อแสดงหน้าลงชื่อเข้าใช้หากเซสชันไม่มี "ข้อมูลเข้าสู่ระบบ"

@app.route("/classroom-addon")
def classroom_addon():
    if "credentials" not in flask.session:
        return flask.render_template("authorization.html")

    return flask.render_template(
        "addon-discovery.html",
        message="You've reached the addon discovery page.")

Java

โค้ดสำหรับคำแนะนำแบบทีละขั้นนี้จะอยู่ในโมดูล step_02_sign_in

เปิดไฟล์ application.properties แล้วเพิ่มการกำหนดค่าเซสชันที่ ปฏิบัติตามคำแนะนำด้านความปลอดภัยของ iframe

# iFrame security recommendations call for cookies to have the HttpOnly and
# secure attribute set
server.servlet.session.cookie.http-only=true
server.servlet.session.cookie.secure=true

# Ensures that the session is maintained across the iframe and sign-in pop-up.
server.servlet.session.cookie.same-site=none

สร้างคลาสบริการ (AuthService.java ในโมดูล step_02_sign_in) เพื่อจัดการตรรกะที่อยู่เบื้องหลังปลายทางในไฟล์ตัวควบคุมและตั้งค่า URI การเปลี่ยนเส้นทาง ตำแหน่งไฟล์รหัสลับไคลเอ็นต์ และขอบเขตที่ส่วนเสริมของคุณ ต้องการ URI การเปลี่ยนเส้นทางใช้ในการเปลี่ยนเส้นทางผู้ใช้ไปยัง URI ที่เจาะจง หลังจากให้สิทธิ์แอปของคุณแล้ว โปรดดูส่วนการตั้งค่าโครงการของ README.md ในซอร์สโค้ดสำหรับข้อมูลเกี่ยวกับตำแหน่งที่ควรวาง client_secret.json

@Service
public class AuthService {
    private static final String REDIRECT_URI = "https://localhost:5000/callback";
    private static final String CLIENT_SECRET_FILE = "client_secret.json";
    private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
    private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();

    private static final String[] REQUIRED_SCOPES = {
        "https://www.googleapis.com/auth/userinfo.profile",
        "https://www.googleapis.com/auth/userinfo.email",
        "https://www.googleapis.com/auth/classroom.addons.teacher",
        "https://www.googleapis.com/auth/classroom.addons.student"
    };

    /** Creates and returns a Collection object with all requested scopes.
    *   @return Collection of scopes requested by the application.
    */
    public static Collection<String> getScopes() {
        return new ArrayList<>(Arrays.asList(REQUIRED_SCOPES));
    }
}

เปิดไฟล์ตัวควบคุม (AuthController.java ใน step_02_sign_in ) และเพิ่มตรรกะให้กับเส้นทาง Landing Page เพื่อแสดงผลหน้าลงชื่อเข้าใช้ หาก เซสชันไม่มีคีย์ credentials

@GetMapping(value = {"/start-auth-flow"})
public String startAuthFlow(Model model) {
    try {
        return "authorization";
    } catch (Exception e) {
        return onError(e.getMessage(), model);
    }
}

@GetMapping(value = {"/addon-discovery"})
public String addon_discovery(HttpSession session, Model model) {
    try {
        if (session == null || session.getAttribute("credentials") == null) {
            return startAuthFlow(model);
        }
        return "addon-discovery";
    } catch (Exception e) {
        return onError(e.getMessage(), model);
    }
}

หน้าการให้สิทธิ์ของคุณควรมีลิงก์หรือปุ่มเพื่อให้ผู้ใช้ "ลงนาม" in" การคลิกนี้จะเปลี่ยนเส้นทางผู้ใช้ไปยังเส้นทาง authorize

ส่งคำขอการให้สิทธิ์

หากต้องการขอสิทธิ์ โปรดสร้างและเปลี่ยนเส้นทางผู้ใช้ไปยังการตรวจสอบสิทธิ์ URL URL นี้มีข้อมูลหลายอย่าง เช่น ขอบเขต เส้นทางปลายทางสำหรับหลังการให้สิทธิ์ และเว็บแอปของ รหัสไคลเอ็นต์ ซึ่งคุณดูได้ในตัวอย่างนี้คือ URL การให้สิทธิ์

Python

เพิ่มการนำเข้าต่อไปนี้ลงในไฟล์ routes.py

import google_auth_oauthlib.flow

สร้างเส้นทางใหม่ /authorize สร้างอินสแตนซ์ของ google_auth_oauthlib.flow.Flow; เราขอแนะนำอย่างยิ่งให้ใช้ from_client_secrets_file ในการดำเนินการนี้

@app.route("/authorize")
def authorize():
    # Create flow instance to manage the OAuth 2.0 Authorization Grant Flow
    # steps.
    flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
        CLIENT_SECRETS_FILE, scopes=SCOPES)

ตั้งค่าredirect_uriของ flow นี่คือเส้นทางที่คุณตั้งใจให้ผู้ใช้ เพื่อส่งคืนหลังจากให้สิทธิ์แอปแล้ว นี่คือ /callback ในเงื่อนไขต่อไปนี้

# The URI created here must exactly match one of the authorized redirect
# URIs for the OAuth 2.0 client, which you configured in the API Console. If
# this value doesn't match an authorized URI, you will get a
# "redirect_uri_mismatch" error.
flow.redirect_uri = flask.url_for("callback", _external=True)

ใช้ออบเจ็กต์โฟลว์เพื่อสร้าง authorization_url และ state ร้านค้า state ในเซสชัน ซึ่งใช้เพื่อยืนยันความถูกต้องของ การตอบกลับของเซิร์ฟเวอร์ในภายหลัง สุดท้าย ให้เปลี่ยนเส้นทางผู้ใช้ไปยัง 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")

# Store the state so the callback can verify the auth server response.
flask.session["state"] = state

# Redirect the user to the OAuth authorization URL.
return flask.redirect(authorization_url)

Java

เพิ่มเมธอดต่อไปนี้ลงในไฟล์ AuthService.java เพื่อสร้างอินสแตนซ์ โฟลว์ออบเจ็กต์ แล้วใช้เพื่อดึง URL การให้สิทธิ์

  • เมธอด getClientSecrets() จะอ่านไฟล์รหัสลับไคลเอ็นต์และสร้าง ออบเจ็กต์ GoogleClientSecrets
  • เมธอด getFlow() จะสร้างอินสแตนซ์ของ GoogleAuthorizationCodeFlow
  • เมธอด authorize() ใช้ออบเจ็กต์ GoogleAuthorizationCodeFlow state และ URI การเปลี่ยนเส้นทางเพื่อดึงข้อมูล URL การให้สิทธิ์ ใช้พารามิเตอร์ state เพื่อยืนยันความถูกต้องของคำตอบ จากเซิร์ฟเวอร์การให้สิทธิ์ จากนั้นเมธอดจะแสดงแผนที่ที่มี URL การให้สิทธิ์และพารามิเตอร์ state
/** Reads the client secret file downloaded from Google Cloud.
 *   @return GoogleClientSecrets read in from client secret file. */
public GoogleClientSecrets getClientSecrets() throws Exception {
    try {
        InputStream in = SignInApplication.class.getClassLoader()
            .getResourceAsStream(CLIENT_SECRET_FILE);
        if (in == null) {
            throw new FileNotFoundException("Client secret file not found: "
                +   CLIENT_SECRET_FILE);
        }
        GoogleClientSecrets clientSecrets = GoogleClientSecrets
            .load(JSON_FACTORY, new InputStreamReader(in));
        return clientSecrets;
    } catch (Exception e) {
        throw e;
    }
}

/** Builds and returns authorization code flow.
*   @return GoogleAuthorizationCodeFlow object used to retrieve an access
*   token and refresh token for the application.
*   @throws Exception if reading client secrets or building code flow object
*   is unsuccessful.
*/
public GoogleAuthorizationCodeFlow getFlow() throws Exception {
    try {
        GoogleAuthorizationCodeFlow authorizationCodeFlow =
            new GoogleAuthorizationCodeFlow.Builder(
                HTTP_TRANSPORT,
                JSON_FACTORY,
                getClientSecrets(),
                getScopes())
                .setAccessType("offline")
                .build();
        return authorizationCodeFlow;
    } catch (Exception e) {
        throw e;
    }
}

/** Builds and returns a map with the authorization URL, which allows the
*   user to give the app permission to their account, and the state parameter,
*   which is used to prevent cross site request forgery.
*   @return map with authorization URL and state parameter.
*   @throws Exception if building the authorization URL is unsuccessful.
*/
public HashMap authorize() throws Exception {
    HashMap<String, String> authDataMap = new HashMap<>();
    try {
        String state = new BigInteger(130, new SecureRandom()).toString(32);
        authDataMap.put("state", state);

        GoogleAuthorizationCodeFlow flow = getFlow();
        String authUrl = flow
            .newAuthorizationUrl()
            .setState(state)
            .setRedirectUri(REDIRECT_URI)
            .build();
        String url = authUrl;
        authDataMap.put("url", url);

        return authDataMap;
    } catch (Exception e) {
        throw e;
    }
}

ใช้การแทรกตัวสร้างเพื่อสร้างอินสแตนซ์ของคลาสบริการใน ตัวควบคุม

/** Declare AuthService to be used in the Controller class constructor. */
private final AuthService authService;

/** AuthController constructor. Uses constructor injection to instantiate
*   the AuthService and UserRepository classes.
*   @param authService the service class that handles the implementation logic
*   of requests.
*/
public AuthController(AuthService authService) {
    this.authService = authService;
}

เพิ่มปลายทาง /authorize ลงในคลาสตัวควบคุม การเรียกปลายทางนี้ เมธอด AuthService authorize() เพื่อเรียกพารามิเตอร์ state และ URL การให้สิทธิ์ จากนั้นปลายทางจะจัดเก็บ state ในเซสชันและเปลี่ยนเส้นทางผู้ใช้ไปยัง URL การให้สิทธิ์

/** Redirects the sign-in pop-up to the authorization URL.
*   @param response the current response to pass information to.
*   @param session the current session.
*   @throws Exception if redirection to the authorization URL is unsuccessful.
*/
@GetMapping(value = {"/authorize"})
public void authorize(HttpServletResponse response, HttpSession session)
    throws Exception {
    try {
        HashMap authDataMap = authService.authorize();
        String authUrl = authDataMap.get("url").toString();
        String state = authDataMap.get("state").toString();
        session.setAttribute("state", state);
        response.sendRedirect(authUrl);
    } catch (Exception e) {
        throw e;
    }
}

จัดการการตอบสนองของเซิร์ฟเวอร์

หลังจากให้สิทธิ์แล้ว ผู้ใช้จะกลับไปยังเส้นทาง redirect_uri จาก ขั้นตอนก่อนหน้า ในตัวอย่างก่อนหน้านี้ เส้นทางนี้คือ /callback

คุณจะได้รับ code ในการตอบกลับเมื่อผู้ใช้กลับมาจาก หน้าการให้สิทธิ์ จากนั้นแลกเปลี่ยนรหัสเพื่อการเข้าถึงและรีเฟรชโทเค็น ดังนี้

Python

เพิ่มการนำเข้าต่อไปนี้ลงในไฟล์เซิร์ฟเวอร์ Flask

import google.oauth2.credentials
import googleapiclient.discovery

เพิ่มเส้นทางไปยังเซิร์ฟเวอร์ สร้างอินสแตนซ์อื่นของ google_auth_oauthlib.flow.Flow แต่ครั้งนี้ใช้สถานะที่บันทึกไว้ใน ขั้นตอนก่อนหน้า

@app.route("/callback")
def callback():
    state = flask.session["state"]

    flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
        CLIENT_SECRETS_FILE, scopes=SCOPES, state=state)
    flow.redirect_uri = flask.url_for("callback", _external=True)

ถัดไป ให้ขอสิทธิ์เข้าถึงและรีเฟรชโทเค็น โชคดีที่ออบเจ็กต์ flow ยัง มีเมธอด fetch_token ในการดำเนินการนี้ เมธอดจะต้องการ อาร์กิวเมนต์ code หรือ authorization_response ใช้เมนู authorization_response เนื่องจากเป็น URL แบบเต็มจากคำขอ

authorization_response = flask.request.url
flow.fetch_token(authorization_response=authorization_response)

ตอนนี้คุณมีข้อมูลเข้าสู่ระบบครบถ้วนแล้ว จัดเก็บไว้ในเซสชันเพื่อให้ สามารถดึงข้อมูลได้ในวิธีการหรือเส้นทางอื่นๆ จากนั้นจึงเปลี่ยนเส้นทางไปยังส่วนเสริม หน้า Landing Page

credentials = flow.credentials
flask.session["credentials"] = {
    "token": credentials.token,
    "refresh_token": credentials.refresh_token,
    "token_uri": credentials.token_uri,
    "client_id": credentials.client_id,
    "client_secret": credentials.client_secret,
    "scopes": credentials.scopes
}

# Close the pop-up by rendering an HTML page with a script that redirects
# the owner and closes itself. This can be done with a bit of JavaScript:
# <script>
#     window.opener.location.href = "{{ url_for('classroom_addon') }}";
#     window.close();
# </script>
return flask.render_template("close-me.html")

Java

เพิ่มเมธอดลงในคลาสบริการที่ส่งคืนออบเจ็กต์ Credentials ด้วยวิธี การส่งรหัสการให้สิทธิ์ที่ดึงมาจากการเปลี่ยนเส้นทางซึ่งดำเนินการโดย URL การให้สิทธิ์ ระบบใช้ออบเจ็กต์ Credentials นี้เพื่อดึงข้อมูลในภายหลัง โทเค็นเพื่อการเข้าถึงและโทเค็นการรีเฟรช

/** Returns the required credentials to access Google APIs.
*   @param authorizationCode the authorization code provided by the
*   authorization URL that's used to obtain credentials.
*   @return the credentials that were retrieved from the authorization flow.
*   @throws Exception if retrieving credentials is unsuccessful.
*/
public Credential getAndSaveCredentials(String authorizationCode) throws Exception {
    try {
        GoogleAuthorizationCodeFlow flow = getFlow();
        GoogleClientSecrets googleClientSecrets = getClientSecrets();
        TokenResponse tokenResponse = flow.newTokenRequest(authorizationCode)
            .setClientAuthentication(new ClientParametersAuthentication(
                googleClientSecrets.getWeb().getClientId(),
                googleClientSecrets.getWeb().getClientSecret()))
            .setRedirectUri(REDIRECT_URI)
            .execute();
        Credential credential = flow.createAndStoreCredential(tokenResponse, null);
        return credential;
    } catch (Exception e) {
        throw e;
    }
}

เพิ่มปลายทางสำหรับ URI การเปลี่ยนเส้นทางไปยังตัวควบคุม เรียกข้อมูล รหัสการให้สิทธิ์และพารามิเตอร์ state จากคำขอ เปรียบเทียบ พารามิเตอร์ state กับแอตทริบิวต์ state ที่จัดเก็บไว้ในเซสชัน หาก จากนั้นดำเนินการตามขั้นตอนการให้สิทธิ์ต่อ หากไม่ตรงกัน แสดงข้อผิดพลาด

จากนั้นเรียกใช้เมธอด AuthService getAndSaveCredentials และส่งผ่าน รหัสการให้สิทธิ์เป็นพารามิเตอร์ หลังจากเรียกข้อมูล Credentials แล้ว เก็บไว้ในเซสชัน จากนั้นปิดกล่องโต้ตอบและเปลี่ยนเส้นทาง ผู้ใช้ไปยังหน้า Landing Page ของส่วนเสริม

/** Handles the redirect URL to grant the application access to the user's
*   account.
*   @param request the current request used to obtain the authorization code
*   and state parameter from.
*   @param session the current session.
*   @param response the current response to pass information to.
*   @param model the Model interface to pass error information that's
*   displayed on the error page.
*   @return the close-pop-up template if authorization is successful, or the
*   onError method to handle and display the error message.
*/
@GetMapping(value = {"/callback"})
public String callback(HttpServletRequest request, HttpSession session,
    HttpServletResponse response, Model model) {
    try {
        String authCode = request.getParameter("code");
        String requestState = request.getParameter("state");
        String sessionState = session.getAttribute("state").toString();
        if (!requestState.equals(sessionState)) {
            response.setStatus(401);
            return onError("Invalid state parameter.", model);
        }
        Credential credentials = authService.getAndSaveCredentials(authCode);
        session.setAttribute("credentials", credentials);
        return "close-pop-up";
    } catch (Exception e) {
        return onError(e.getMessage(), model);
    }
}

ทดสอบการเรียก API

เมื่อดำเนินการขั้นตอนนี้เสร็จสมบูรณ์แล้ว คุณจะออกการเรียกไปยัง Google APIs ได้

เช่น ขอข้อมูลโปรไฟล์ของผู้ใช้ คุณสามารถขอ ข้อมูลผู้ใช้จาก OAuth 2.0 API

Python

โปรดอ่านเอกสารประกอบสำหรับ API การสำรวจ OAuth 2.0 ให้ใช้รายการนี้เพื่อรับออบเจ็กต์ UserInfo ที่ป้อนข้อมูล

# Retrieve the credentials from the session data and construct a
# Credentials instance.
credentials = google.oauth2.credentials.Credentials(
    **flask.session["credentials"])

# Construct the OAuth 2.0 v2 discovery API library.
user_info_service = googleapiclient.discovery.build(
    serviceName="oauth2", version="v2", credentials=credentials)

# Request and store the username in the session.
# This allows it to be used in other methods or in an HTML template.
flask.session["username"] = (
    user_info_service.userinfo().get().execute().get("name"))

Java

สร้างเมธอดในคลาสบริการที่สร้างออบเจ็กต์ UserInfo โดยใช้ Credentials เป็นพารามิเตอร์

/** Obtains the Userinfo object by passing in the required credentials.
*   @param credentials retrieved from the authorization flow.
*   @return the Userinfo object for the currently signed-in user.
*   @throws IOException if creating UserInfo service or obtaining the
*   Userinfo object is unsuccessful.
*/
public Userinfo getUserInfo(Credential credentials) throws IOException {
    try {
        Oauth2 userInfoService = new Oauth2.Builder(
            new NetHttpTransport(),
            new GsonFactory(),
            credentials).build();
        Userinfo userinfo = userInfoService.userinfo().get().execute();
        return userinfo;
    } catch (Exception e) {
        throw e;
    }
}

เพิ่มปลายทาง /test ลงในตัวควบคุมที่แสดงอีเมลของผู้ใช้

/** Returns the test request page with the user's email.
*   @param session the current session.
*   @param model the Model interface to pass error information that's
*   displayed on the error page.
*   @return the test page that displays the current user's email or the
*   onError method to handle and display the error message.
*/
@GetMapping(value = {"/test"})
public String test(HttpSession session, Model model) {
    try {
        Credential credentials = (Credential) session.getAttribute("credentials");
        Userinfo userInfo = authService.getUserInfo(credentials);
        String userInfoEmail = userInfo.getEmail();
        if (userInfoEmail != null) {
            model.addAttribute("userEmail", userInfoEmail);
        } else {
            return onError("Could not get user email.", model);
        }
        return "test";
    } catch (Exception e) {
        return onError(e.getMessage(), model);
    }
}

ล้างข้อมูลรับรอง

คุณสามารถ "ล้าง" ข้อมูลเข้าสู่ระบบของผู้ใช้โดยนำผู้ใช้ออกจากเซสชันปัจจุบัน วิธีนี้ช่วยให้คุณทดสอบการกำหนดเส้นทางในหน้า Landing Page ของส่วนเสริมได้

เราขอแนะนำให้แสดงตัวบ่งชี้ว่าผู้ใช้ออกจากระบบไปแล้วก่อน โดยเปลี่ยนเส้นทางไปยังหน้า Landing Page ของส่วนเสริม แอปของคุณควรดำเนินการตาม กระบวนการให้สิทธิ์เพื่อรับข้อมูลรับรองใหม่ แต่ผู้ใช้จะไม่ได้รับข้อความแจ้ง ให้สิทธิ์แอปอีกครั้ง

Python

@app.route("/clear")
def clear_credentials():
    if "credentials" in flask.session:
        del flask.session["credentials"]
        del flask.session["username"]

    return flask.render_template("signed-out.html")

หรือใช้ flask.session.clear() แต่การดำเนินการนี้อาจไม่ได้ตั้งใจ ในกรณีที่คุณเก็บค่าอื่นๆ ในเซสชันนั้น

Java

เพิ่มปลายทาง /clear ในตัวควบคุม

/** Clears the credentials in the session and returns the sign-out
*   confirmation page.
*   @param session the current session.
*   @return the sign-out confirmation page.
*/
@GetMapping(value = {"/clear"})
public String clear(HttpSession session) {
    try {
        if (session != null && session.getAttribute("credentials") != null) {
            session.removeAttribute("credentials");
        }
        return "sign-out";
    } catch (Exception e) {
        return onError(e.getMessage(), model);
    }
}

เพิกถอนสิทธิ์ของแอป

ผู้ใช้เพิกถอนสิทธิ์ของแอปได้โดยส่งคำขอPOSTไปที่ https://oauth2.googleapis.com/revoke คำขอควรมี โทเค็นเพื่อการเข้าถึง

Python

import requests

@app.route("/revoke")
def revoke():
    if "credentials" not in flask.session:
        return flask.render_template("addon-discovery.html",
                            message="You need to authorize before " +
                            "attempting to revoke credentials.")

    credentials = google.oauth2.credentials.Credentials(
        **flask.session["credentials"])

    revoke = requests.post(
        "https://oauth2.googleapis.com/revoke",
        params={"token": credentials.token},
        headers={"content-type": "application/x-www-form-urlencoded"})

    if "credentials" in flask.session:
        del flask.session["credentials"]
        del flask.session["username"]

    status_code = getattr(revoke, "status_code")
    if status_code == 200:
        return flask.render_template("authorization.html")
    else:
        return flask.render_template(
            "index.html", message="An error occurred during revocation!")

Java

เพิ่มเมธอดลงในคลาสบริการที่เรียกใช้ปลายทางการเพิกถอน

/** Revokes the app's permissions to the user's account.
*   @param credentials retrieved from the authorization flow.
*   @return response entity returned from the HTTP call to obtain response
*   information.
*   @throws RestClientException if the POST request to the revoke endpoint is
*   unsuccessful.
*/
public ResponseEntity<String> revokeCredentials(Credential credentials) throws RestClientException {
    try {
        String accessToken = credentials.getAccessToken();
        String url = "https://oauth2.googleapis.com/revoke?token=" + accessToken;

        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE);
        HttpEntity<Object> httpEntity = new HttpEntity<Object>(httpHeaders);
        ResponseEntity<String> responseEntity = new RestTemplate().exchange(
            url,
            HttpMethod.POST,
            httpEntity,
            String.class);
        return responseEntity;
    } catch (RestClientException e) {
        throw e;
    }
}

เพิ่มปลายทาง /revoke ลงในตัวควบคุมที่ล้างเซสชันและ จะเปลี่ยนเส้นทางผู้ใช้ไปยังหน้าการให้สิทธิ์ หากการเพิกถอน สำเร็จ

/** Revokes the app's permissions and returns the authorization page.
*   @param session the current session.
*   @return the authorization page.
*   @throws Exception if revoking access is unsuccessful.
*/
@GetMapping(value = {"/revoke"})
public String revoke(HttpSession session) throws Exception {
    try {
        if (session != null && session.getAttribute("credentials") != null) {
            Credential credentials = (Credential) session.getAttribute("credentials");
            ResponseEntity responseEntity = authService.revokeCredentials(credentials);
            Integer httpStatusCode = responseEntity.getStatusCodeValue();

            if (httpStatusCode != 200) {
                return onError("There was an issue revoking access: " +
                    responseEntity.getStatusCode(), model);
            }
            session.removeAttribute("credentials");
        }
        return startAuthFlow(model);
    } catch (Exception e) {
        return onError(e.getMessage(), model);
    }
}

ทดสอบส่วนเสริม

ลงชื่อเข้าใช้ Google Classroom ในฐานะผู้ใช้การทดสอบครูคนหนึ่งของคุณ ไปที่แท็บงานของชั้นเรียน และ สร้าง Assignment ใหม่ คลิกปุ่มส่วนเสริมใต้พื้นที่ข้อความ จากนั้นเลือกส่วนเสริม iframe จะเปิดขึ้นและส่วนเสริมจะโหลด URL การตั้งค่าไฟล์แนบที่คุณระบุในแอปของ GWM SDK การกำหนดค่า

ยินดีด้วย คุณพร้อมที่จะไปยังขั้นตอนถัดไปแล้ว: การจัดการการทำซ้ำ ไปยังส่วนเสริม