זהו המדריך השני בסדרת המדריכים בנושא תוספים ל-Classroom.
במדריך הזה תלמדו איך להוסיף את 'כניסה באמצעות חשבון Google' לאפליקציית האינטרנט. זוהי התנהגות חובה בתוספים ל-Classroom. משתמשים בפרטי הכניסה מתהליך ההרשאה הזה בכל הקריאות העתידיות ל-API.
במהלך המדריך הזה תלמדו:
- מגדירים את אפליקציית האינטרנט כדי לשמור על נתוני הסשנים ב-iframe.
- הטמעת תהליך כניסה מסוג שרת-אל-שרת של Google OAuth 2.0.
- שולחים קריאה ל-API של OAuth 2.0.
- יצירת מסלולים נוספים לתמיכה באישור, ביציאה ובבדיקה של קריאות ל-API.
בסיום התהליך תוכלו להעניק הרשאה מלאה למשתמשים באפליקציית האינטרנט שלכם ולבצע קריאות לממשקי Google APIs.
הסבר על תהליך ההרשאה
ממשקי Google API משתמשים בפרוטוקול OAuth 2.0 לצורך אימות והרשאה. התיאור המלא של הטמעת OAuth של Google זמין במדריך בנושא OAuth ב-Google.
פרטי הכניסה של האפליקציה מנוהלים ב-Google Cloud. לאחר היצירה, צריך להטמיע תהליך בן ארבעה שלבים לאימות ולמתן הרשאה למשתמש:
- שולחים בקשה להרשאה. יש לציין כתובת URL לקריאה חוזרת (callback) כחלק מהבקשה הזו. בסיום, מקבלים כתובת URL להרשאה.
- מפנים את המשתמש לכתובת ה-URL של ההרשאה. בדף שייפתח יוצגו למשתמש ההרשאות הנדרשות לאפליקציה, והוא יתבקש לאשר את הגישה. בסיום, המשתמש מופנה לכתובת ה-URL לקריאה חוזרת.
- מקבלים קוד הרשאה במסלול הקריאה החוזרת. מחליפים את קוד ההרשאה באסימון גישה ובאסימון רענון.
- ביצוע קריאות ל-Google API באמצעות האסימונים.
קבלת פרטי כניסה ל-OAuth 2.0
מוודאים שיצרתם והורדתם את פרטי הכניסה ל-OAuth כפי שמתואר בדף 'סקירה כללית'. פרטי הכניסה האלה צריכים לשמש את הפרויקט כדי לאפשר למשתמש להיכנס.
הטמעת תהליך ההרשאה
מוסיפים לוגיקה ומסלולים לאפליקציית האינטרנט כדי ליצור את התהליך שמתואר, כולל התכונות הבאות:
- מפעילים את תהליך ההרשאה כשמגיעים לדף הנחיתה.
- שליחת בקשה להרשאה וטיפול בתגובה של שרת ההרשאות.
- מנקים את פרטי הכניסה השמורים.
- ביטול ההרשאות של האפליקציה.
- בדיקת קריאה ל-API.
הפעלת ההרשאה
אם צריך, משנים את דף הנחיתה כדי להפעיל את תהליך ההרשאה. התוסף יכול להיות בשני מצבים אפשריים: יש אסימונים שמורים בסשן הנוכחי, או שצריך לקבל אסימונים מהשרת של OAuth 2.0. מבצעים קריאה ל-API לצורך בדיקה אם יש אסימונים בסשן, או מבקשים מהמשתמש להיכנס לחשבון בדרך אחרת.
Python
פותחים את קובץ ה-routes.py
. קודם כול מגדירים כמה קבועים והגדרות של קובצי cookie בהתאם להמלצות האבטחה של 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",
)
עוברים למסלול דף הנחיתה של התוסף (הוא /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
) ומוסיפים לוגיקה למסלול הנחיתה כדי לעבד את דף הכניסה אם הסשן לא מכיל את המפתח 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 לדוגמה לאישור.
Python
מוסיפים את ייבוא הנתונים הבא לקובץ routes.py
.
import google_auth_oauthlib.flow
יוצרים מסלול חדש /authorize
. יוצרים מופע של google_auth_oauthlib.flow.Flow
. מומלץ מאוד להשתמש לשם כך בשיטה from_client_secrets_file
שכלולה ב-SDK.
@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 להרשאה:
- ה-method
getClientSecrets()
קוראת את הקובץ של סוד הלקוח ויוצרת אובייקטGoogleClientSecrets
. - השיטה
getFlow()
יוצרת מופע שלGoogleAuthorizationCodeFlow
. - ה-method
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;
}
}
משתמשים בהזרקה של מגדיר (constructor) כדי ליצור מופע של סוג השירות בכיתה של הבקר.
/** 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;
}
מוסיפים את נקודת הקצה (endpoint) /authorize
למחלקה של הבקרה. נקודת הקצה הזו קוראת לשיטה authorize()
של AuthService כדי לאחזר את הפרמטר 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
שבעזרתה אפשר לעשות זאת. ה-method מצפה לארגומנטים code
או authorization_response
. צריך להשתמש ב-authorization_response
, כי זו כתובת ה-URL המלאה מהבקשה.
authorization_response = flask.request.url
flow.fetch_token(authorization_response=authorization_response)
עכשיו יש לך פרטי כניסה מלאים! מאחסנים אותם בסשן כדי שיהיה אפשר לאחזר אותם בשיטות או במסלולים אחרים, ואז להפנות אותם לדף נחיתה של תוסף.
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
מוסיפים ל-class של השירות method שמחזיר את האובייקט 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
, שומרים אותו בסשן. לאחר מכן, סוגרים את תיבת הדו-שיח ומפנים את המשתמשים לדף הנחיתה של התוסף.
/** 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);
}
}
ניקוי פרטי כניסה
אפשר "למחוק" את פרטי הכניסה של משתמש על ידי הסרתם מהסשן הנוכחי. כך תוכלו לבדוק את הניתוב בדף הנחיתה של התוסף.
מומלץ להציג אינדיקציה לכך שהמשתמש יצא מהחשבון לפני שמפנים אותו לדף הנחיתה של התוסף. האפליקציה צריכה לעבור את תהליך ההרשאה כדי לקבל פרטי כניסה חדשים, אבל המשתמשים לא מתבקשים להעניק לאפליקציה הרשאה מחדש.
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
ב-Controller, מוסיפים נקודת קצה מסוג /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
מוסיפים שיטה למחלקת השירות שמבצעת קריאה לנקודת הקצה (endpoint) לביטול.
/** 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 בתור אחד ממשתמשי הבדיקה בתפקיד מורה. נכנסים לכרטיסייה עבודות ויוצרים מטלה חדשה. לוחצים על הלחצן Adds (תוספים) שמתחת לאזור הטקסט ובוחרים את התוסף הרצוי. ה-iframe נפתח והתוסף טוען את URI להגדרת הקובץ המצורף שציינתם בדף הגדרת האפליקציה של GWM SDK.
מעולה! עכשיו אפשר להמשיך לשלב הבא: טיפול בביקור חוזר בחבילת התוספים.