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

นี่เป็นคำแนะนำลำดับที่ 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 มีอยู่ในคู่มือ OAuth ของ Google Identity

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

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

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

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

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

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

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

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

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

Python

เปิดไฟล์ routes.py ขั้นแรก ให้ตั้งค่าค่าคงที่และการกำหนดค่าคุกกี้ 2 รายการตามคำแนะนำเกี่ยวกับความปลอดภัยของ 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);
    }
}

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

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

หากต้องการขอสิทธิ์ ให้สร้างและเปลี่ยนเส้นทางผู้ใช้ไปยัง URL การตรวจสอบสิทธิ์ 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

โปรดอ่านเอกสารประกอบเกี่ยวกับ OAuth 2.0 Discovery API ใช้เพื่อรับออบเจ็กต์ 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 เป็นหนึ่งในผู้ใช้ทดสอบของ Teacher ไปที่แท็บงานของชั้นเรียน แล้วสร้างงานใหม่ คลิกปุ่มส่วนเสริมใต้พื้นที่ข้อความ แล้วเลือกส่วนเสริม iframe จะเปิดขึ้นและส่วนเสริมจะโหลด URI การตั้งค่าไฟล์แนบที่คุณระบุไว้ในหน้าการกำหนดค่าแอปของ GWM SDK

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