Đăng nhập người dùng

Đây là hướng dẫn từng bước thứ hai về các tiện ích bổ sung cho Google Lớp học loạt video hướng dẫn từng bước.

Trong hướng dẫn từng bước này, bạn sẽ thêm tính năng Đăng nhập bằng Google vào ứng dụng web. Đây là một hành vi bắt buộc đối với tiện ích bổ sung cho Google Lớp học. Sử dụng thông tin xác thực từ quy trình uỷ quyền này cho tất cả các lệnh gọi trong tương lai đến API.

Trong hướng dẫn từng bước này, bạn cần hoàn thành các bước sau:

  • Định cấu hình ứng dụng web của bạn để duy trì dữ liệu phiên trong iframe.
  • Triển khai quy trình đăng nhập từ máy chủ đến máy chủ của Google OAuth 2.0.
  • Gọi lệnh đến API OAuth 2.0.
  • Tạo các tuyến bổ sung để hỗ trợ việc uỷ quyền, đăng xuất và thử nghiệm Lệnh gọi API.

Sau khi hoàn tất, bạn có thể uỷ quyền đầy đủ cho người dùng trong ứng dụng web của mình và thực hiện cuộc gọi để API của Google.

Tìm hiểu quy trình uỷ quyền

API của Google sử dụng giao thức OAuth 2.0 để xác thực và uỷ quyền. Bạn có thể xem nội dung mô tả đầy đủ về việc triển khai OAuth của Google trong Hướng dẫn về OAuth của Google Identity.

Thông tin đăng nhập của ứng dụng được quản lý trong Google Cloud. Sau khi các địa chỉ này hãy triển khai quy trình 4 bước để xác thực và cấp quyền người dùng:

  1. Yêu cầu uỷ quyền. Cung cấp URL gọi lại trong yêu cầu này. Khi hoàn tất, bạn sẽ nhận được một URL ủy quyền.
  2. Chuyển hướng người dùng đến URL uỷ quyền. Trang kết quả sẽ thông báo người dùng của các quyền mà ứng dụng của bạn yêu cầu và nhắc họ cho phép truy cập. Khi hoàn tất, người dùng sẽ được chuyển đến URL gọi lại.
  3. Nhận mã uỷ quyền theo phương thức gọi lại. Trao đổi mã uỷ quyền cho mã truy cậpmã làm mới.
  4. Thực hiện lệnh gọi đến API của Google bằng mã thông báo.

Lấy thông tin đăng nhập OAuth 2.0

Đảm bảo rằng bạn đã tạo và tải thông tin đăng nhập OAuth xuống theo mô tả trong trang Tổng quan. Dự án của bạn phải sử dụng những thông tin đăng nhập này để đăng nhập người dùng.

Triển khai quy trình uỷ quyền

Thêm logic và các tuyến vào ứng dụng web để nhận ra luồng được mô tả, bao gồm các tính năng này:

  • Bắt đầu quy trình uỷ quyền khi trang đích.
  • Yêu cầu uỷ quyền và xử lý phản hồi của máy chủ uỷ quyền.
  • Xoá thông tin đăng nhập đã lưu trữ.
  • Thu hồi quyền của ứng dụng.
  • Kiểm thử lệnh gọi API.

Bắt đầu uỷ quyền

Sửa đổi trang đích của bạn để bắt đầu quy trình uỷ quyền nếu cần. Chiến lược phát hành đĩa đơn tiện ích bổ sung có thể ở hai trạng thái; có mã thông báo đã lưu trong phiên hiện tại hoặc bạn cần lấy mã thông báo từ máy chủ OAuth 2.0. Thực hiện một lệnh gọi API kiểm thử nếu có mã thông báo trong phiên hoặc lời nhắc người dùng để đăng nhập.

Python

Mở tệp routes.py của bạn. Trước tiên, hãy thiết lập một vài hằng số và cookie cấu hình theo đề xuất bảo mật 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",
)

Di chuyển đến tuyến trang đích của tiện ích bổ sung (trong ví dụ là /classroom-addon tệp). Thêm logic để hiển thị trang đăng nhập nếu phiên không chứa "thông tin xác thực" .

@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

Bạn có thể tìm thấy mã cho hướng dẫn từng bước này trong mô-đun step_02_sign_in.

Mở tệp application.properties và thêm cấu hình phiên tuân theo đề xuất bảo mật của 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

Tạo một lớp dịch vụ (AuthService.java trong mô-đun step_02_sign_in) để xử lý logic phía sau các điểm cuối trong tệp bộ điều khiển và thiết lập URI chuyển hướng, vị trí tệp bí mật của ứng dụng khách và phạm vi mà tiện ích bổ sung của bạn yêu cầu. URI chuyển hướng được dùng để định tuyến lại người dùng của bạn đến một URI cụ thể sau khi họ cho phép ứng dụng của bạn. Xem phần Thiết lập dự án trong README.md trong mã nguồn để biết thông tin về vị trí đặt 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));
    }
}

Mở tệp tay điều khiển (AuthController.java trong step_02_sign_in mô-đun) và thêm logic vào tuyến đích để hiển thị trang đăng nhập nếu phiên không chứa khoá 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);
    }
}

Trang uỷ quyền của bạn nên chứa một đường liên kết hoặc nút để người dùng "ký in". Khi nhấp vào đường liên kết này, người dùng sẽ được chuyển hướng đến tuyến authorize.

Yêu cầu uỷ quyền

Để yêu cầu cấp quyền, hãy tạo và chuyển hướng người dùng đến một quy trình xác thực URL. URL này bao gồm một số thông tin, chẳng hạn như phạm vi được yêu cầu, tuyến đích cho sau khi uỷ quyền, và mã ứng dụng khách. Bạn có thể xem chúng trong URL uỷ quyền mẫu này.

Python

Thêm dữ liệu nhập sau vào tệp routes.py.

import google_auth_oauthlib.flow

Tạo tuyến đường mới /authorize. Tạo một thực thể của google_auth_oauthlib.flow.Flow; bạn nên sử dụng from_client_secrets_file để làm điều đó.

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

Đặt redirect_uri của flow; đây là tuyến đường mà bạn dự định cho người dùng quay lại sau khi cấp phép cho ứng dụng của bạn. Đây là /callback ở phía sau ví dụ:

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

Sử dụng đối tượng luồng để tạo authorization_urlstate. Cửa hàng state trong phiên hoạt động; nó được dùng để xác minh tính xác thực của phản hồi của máy chủ sau. Cuối cùng, hãy chuyển hướng người dùng đến 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

Thêm các phương thức sau vào tệp AuthService.java để tạo thực thể và sau đó sử dụng đối tượng luồng này để truy xuất URL uỷ quyền:

  • Phương thức getClientSecrets() đọc tệp mật khẩu ứng dụng khách và tạo cấu trúc đối tượng GoogleClientSecrets.
  • Phương thức getFlow() tạo một bản sao của GoogleAuthorizationCodeFlow.
  • Phương thức authorize() sử dụng đối tượng GoogleAuthorizationCodeFlow, đối tượng tham số state và URI chuyển hướng để truy xuất URL uỷ quyền. Tham số state được dùng để xác minh tính xác thực của câu trả lời từ máy chủ uỷ quyền. Sau đó, phương thức này sẽ trả về một bản đồ có URL uỷ quyền và tham số 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;
    }
}

Sử dụng tính năng chèn hàm khởi tạo để tạo một thực thể của lớp dịch vụ trong phần điều khiển.

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

Thêm điểm cuối /authorize vào lớp tay điều khiển. Điểm cuối này gọi phương thức authorize() AuthService để truy xuất thông số state và URL uỷ quyền. Sau đó, điểm cuối lưu trữ state trong phiên và chuyển hướng người dùng đến URL ủy quyền.

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

Xử lý phản hồi của máy chủ

Sau khi cấp phép, người dùng quay lại tuyến redirect_uri từ bước trước đó. Trong ví dụ trước, tuyến này là /callback.

Bạn sẽ nhận được một code trong phản hồi khi người dùng quay lại từ trang uỷ quyền. Sau đó, hãy đổi mã đó lấy mã truy cập và làm mới mã:

Python

Thêm các nội dung nhập sau vào tệp máy chủ Flask của bạn.

import google.oauth2.credentials
import googleapiclient.discovery

Thêm tuyến vào máy chủ của bạn. Xây dựng một thực thể khác của google_auth_oauthlib.flow.Flow, nhưng lần này sử dụng lại trạng thái đã lưu trong bước trước đó.

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

Tiếp theo, hãy yêu cầu quyền truy cập và làm mới mã thông báo. Rất may là đối tượng flow cũng sẽ chứa phương thức fetch_token để thực hiện việc này. Phương thức dự kiến đối số code hoặc authorization_response. Sử dụng authorization_response, vì đây là URL đầy đủ trong yêu cầu.

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

Bây giờ, bạn đã có thông tin đăng nhập hoàn chỉnh! Lưu trữ chúng trong phiên để chúng có thể được truy xuất trong các phương thức hoặc tuyến khác, sau đó chuyển hướng đến một tiện ích bổ sung trang đích.

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

Thêm một phương thức vào lớp dịch vụ để trả về đối tượng Credentials bằng cách chuyển mã uỷ quyền được truy xuất từ lệnh chuyển hướng được thực hiện bởi URL uỷ quyền. Sau này, đối tượng Credentials này sẽ được dùng để truy xuất mã truy cập và mã làm mới.

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

Thêm điểm cuối của URI chuyển hướng vào bộ điều khiển. Truy xuất mã uỷ quyền và tham số state trong yêu cầu. So sánh nội dung này tham số state cho thuộc tính state được lưu trữ trong phiên. Nếu so khớp, sau đó tiếp tục quy trình uỷ quyền. Nếu chúng không khớp nhau, trả về lỗi.

Sau đó, hãy gọi phương thức AuthService getAndSaveCredentials và truyền vào mã uỷ quyền dưới dạng tham số. Sau khi truy xuất Credentials hãy lưu trữ đối tượng đó trong phiên hoạt động. Sau đó, đóng hộp thoại và chuyển hướng truy cập vào trang đích của tiện ích bổ sung.

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

Kiểm thử lệnh gọi API

Sau khi hoàn tất quy trình này, bạn hiện có thể thực hiện lệnh gọi đến các API của Google!

Ví dụ: yêu cầu thông tin hồ sơ của người dùng. Bạn có thể yêu cầu thông tin của người dùng qua API OAuth 2.0.

Python

Hãy đọc tài liệu về phần API khám phá OAuth 2.0 Hãy sử dụng lớp này để lấy đối tượng UserInfo được điền sẵn.

# 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

Tạo một phương thức trong lớp dịch vụ để tạo đối tượng UserInfo bằng Credentials làm tham số.

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

Thêm điểm cuối /test vào trình điều khiển có hiển thị email của người dùng.

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

Xóa thông tin xác thực

Bạn có thể "xoá" thông tin đăng nhập của người dùng bằng cách xoá họ khỏi phiên hiện tại. Việc này cho phép bạn kiểm tra việc định tuyến trên trang đích của tiện ích bổ sung.

Bạn nên cho thấy một chỉ báo cho biết người dùng từng đăng xuất trước đó chuyển hướng họ đến trang đích của tiện ích bổ sung. Ứng dụng của bạn sẽ trải qua để lấy thông tin xác thực mới, nhưng người dùng không được nhắc ủy quyền lại ứng dụng.

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

Hoặc, sử dụng flask.session.clear(), nhưng thao tác này có thể đã xảy ra ngoài ý muốn nếu bạn có các giá trị khác được lưu trữ trong phiên.

Java

Trong bộ điều khiển, hãy thêm một điểm cuối /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);
    }
}

Thu hồi quyền của ứng dụng

Người dùng có thể thu hồi quyền của ứng dụng bằng cách gửi yêu cầu POST tới https://oauth2.googleapis.com/revoke. Yêu cầu nên chứa thông tin mã truy cập.

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

Thêm phương thức vào lớp dịch vụ để thực hiện lệnh gọi đến điểm cuối thu hồi.

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

Thêm điểm cuối /revoke vào trình điều khiển sẽ xoá phiên và chuyển hướng người dùng đến trang uỷ quyền nếu việc thu hồi là thành công.

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

Kiểm thử tiện ích bổ sung

Đăng nhập vào Google Lớp học với tư cách là một trong những người dùng thử nghiệm với Giáo viên. Chuyển đến thẻ Bài tập trên lớp và tạo một Bài tập mới. Nhấp vào nút Tiện ích bổ sung bên dưới vùng văn bản sau đó chọn tiện ích bổ sung của bạn. iframe sẽ mở ra và tiện ích bổ sung sẽ tải URI thiết lập tệp đính kèm mà bạn đã chỉ định trong Ứng dụng của SDK GWM Cấu hình .

Xin chúc mừng! Bạn đã sẵn sàng để chuyển sang bước tiếp theo: xử lý lần lặp lại các lượt truy cập vào tiện ích bổ sung của bạn.