Đây là hướng dẫn từng bước thứ hai trong loạt hướng dẫn từng bước về tiện ích bổ sung trên Google Lớp họ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à hành vi bắt buộc đối với các 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 đến API trong tương lai.
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à kiểm thử các lệnh gọi API.
Sau khi hoàn tất, bạn có thể uỷ quyền hoàn toàn cho người dùng trong ứng dụng web của mình và thực hiện lệnh gọi đến các 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ề cách 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 tạo các quy trình này, hãy triển khai quy trình 4 bước để xác thực và uỷ quyền cho người dùng:
- 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.
- Chuyển hướng người dùng đến URL uỷ quyền. Trang kết quả sẽ thông báo cho người dùng về các quyền mà ứng dụng của bạn yêu cầu và nhắc họ cấp quyền truy cập. Khi hoàn tất, người dùng sẽ được chuyển đến URL gọi lại.
- Nhận mã uỷ quyền theo phương thức gọi lại. Trao đổi mã uỷ quyền để lấy mã truy cập và mã làm mới.
- 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ả trên 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à tuyến vào ứng dụng web để nhận ra quy trình được mô tả, bao gồm các tính năng sau:
- 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. 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. Hãy thực hiện lệnh gọi API kiểm thử nếu có mã thông báo trong phiên, hoặc 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 đặt một vài hằng số và cấu hình cookie
theo đề xuất bảo mật của 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",
)
Chuyển đến tuyến đích của tiện ích bổ sung (trong tệp ví dụ là /classroom-addon
). Thêm logic để hiển thị trang đăng nhập nếu phiên không chứa khoá "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 trình đ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à các 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. Hãy xem phần Thiết lập dự án của README.md
trong mã nguồn để biết thông tin về vị trí đặt tệp client_secret.json
của bạn.
@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 mô-đun step_02_sign_in
) và thêm logic vào tuyến đích để kết xuất 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 phải chứa một đường liên kết hoặc nút để người dùng "đăng nhập". 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 uỷ quyền, hãy tạo và chuyển hướng người dùng đến một URL xác thực. 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 của ứng dụng web. 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 phương thức from_client_secrets_file
đi kèm để thực hiện việc này.
@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 mà bạn muốn người dùng quay lại sau khi cho phép ứng dụng của bạn. Đây là /callback
trong ví dụ sau.
# 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_url
và state
. Lưu trữ state
trong phiên; mã này được dùng để xác minh tính xác thực của phản hồi của máy chủ sau này. 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ể cho đối tượng luồng rồi sử dụng đối tượng đó để 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 đối tượngGoogleClientSecrets
. - Phương thức
getFlow()
tạo một bản sao củaGoogleAuthorizationCodeFlow
. - Phương thức
authorize()
sử dụng đối tượngGoogleAuthorizationCodeFlow
, tham sốstate
và URI chuyển hướng để truy xuất URL uỷ quyền. Tham sốstate
dùng để xác minh tính xác thực của phản hồi từ máy chủ uỷ quyền. Sau đó, phương thức này sẽ trả về một tệp ánh xạ 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 lớp trình đ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ữ tham số state
trong phiên và chuyển hướng người dùng đến URL uỷ 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. Tạo một thực thể khác của google_auth_oauthlib.flow.Flow
, nhưng lần này hãy sử dụng lại trạng thái đã lưu ở 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 chứa phương thức fetch_token
để thực hiện việc này. Phương thức này yêu cầu các đố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 để có thể được truy xuất trong các phương thức hoặc tuyến đường khác, sau đó chuyển hướng đến trang đích của tiện ích bổ sung.
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 truy xuất từ lệnh chuyển hướng do URL uỷ quyền thực hiệ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 trình điều khiển. Truy xuất mã uỷ quyền và tham số state
từ yêu cầu. So sánh tham số state
này với thuộc tính state
được lưu trữ trong phiên. Nếu các mã khớp nhau, hãy tiếp tục quy trình uỷ quyền. Nếu chúng không khớp,
hãy trả về lỗi.
Sau đó, hãy gọi phương thức AuthService
getAndSaveCredentials
và chuyển vào
mã uỷ quyền dưới dạng thông số. Sau khi truy xuất đối tượng 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 người dùng
đến 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
Đọc tài liệu về API khám phá OAuth 2.0 Sử dụng API 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 cách sử dụ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 hiển thị chỉ báo cho biết người dùng đã đăng xuất trước khi chuyển hướng họ đến trang đích của tiện ích bổ sung. Ứng dụng của bạn phải trải qua quy trình uỷ quyền để lấy thông tin xác thực mới, nhưng người dùng sẽ không được nhắc uỷ quyền lại cho ứ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")
Ngoài ra, hãy sử dụng flask.session.clear()
, nhưng việc này có thể gây ra những kết quả không mong 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
đến https://oauth2.googleapis.com/revoke
. Yêu cầu phải chứa mã truy cập của người dùng.
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 bộ điều khiển để xoá phiên và chuyển hướng người dùng đến trang uỷ quyền nếu quá trình thu hồi 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 kiểm thử 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 trên trang Cấu hình ứng dụng của SDK GWM.
Xin chúc mừng! Bạn đã sẵn sàng để chuyển sang bước tiếp theo: xử lý các lượt truy cập lặp lại vào tiện ích bổ sung của bạn.