เอกสารนี้อธิบายวิธีใช้เครื่องจัดการเรียกกลับเรื่องการให้สิทธิ์ OAuth 2.0 โดยใช้เซิร์ฟเล็ตของ Java ผ่านเว็บแอปพลิเคชันตัวอย่างที่จะแสดงงานของผู้ใช้โดยใช้ Google Tasks API แอปพลิเคชันตัวอย่างจะขอสิทธิ์การเข้าถึง Google Tasks ของผู้ใช้ก่อน จากนั้นจะแสดงงานของผู้ใช้ในรายการงานเริ่มต้น
ผู้ชม
เอกสารนี้จัดทำมาเพื่อผู้ที่คุ้นเคยกับสถาปัตยกรรมเว็บแอปพลิเคชัน Java และ J2EE ขอแนะนำให้ทราบข้อมูลบางส่วนเกี่ยวกับขั้นตอนการให้สิทธิ์ OAuth 2.0
เนื้อหา
คุณต้องทำดังนี้เพื่อให้ตัวอย่างที่ทำงานได้อย่างเต็มรูปแบบเช่นนี้มีหลายขั้นตอนที่เป็นสิ่งจำเป็น
- ประกาศการแมปเซิร์ฟเล็ตในไฟล์ web.xml
- ตรวจสอบสิทธิ์ผู้ใช้ในระบบและขอสิทธ์เพื่อเข้าถึง Tasks
- ฟังรหัสการให้สิทธิ์จากปลายทางการให้สิทธิ์ของ Google
- แลกเปลี่ยนรหัสการให้สิทธิ์เพื่อรีเฟรชและโทเค็นเพื่อการเข้าถึง
- อ่านงานของผู้ใช้และแสดง
ประกาศการแมปเซิร์ฟเล็ตในไฟล์ web.xml
เราจะใช้ servlets 2 ตัวในแอปพลิเคชันของเรา:
- PrintTasksTitlesServlet (ที่แมปกับ /): จุดแรกเข้าของแอปพลิเคชันที่จะจัดการการตรวจสอบสิทธิ์ผู้ใช้ และจะแสดงงานของผู้ใช้
- OAuthCodeCallbackHandlerServlet (แมปกับ /oauth2callback): โค้ดเรียกกลับของ OAuth 2.0 ซึ่งจัดการการตอบสนองจากปลายทางการให้สิทธิ์ OAuth
ด้านล่างคือไฟล์ web.xml ซึ่งจับคู่เซิร์ฟเล็ต 2 รายการนี้กับ URL ในแอปพลิเคชันของเรา
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <servlet> <servlet-name>PrintTasksTitles</servlet-name> <servlet-class>com.google.oauthsample.PrintTasksTitlesServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>PrintTasksTitles</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <servlet> <servlet-name>OAuthCodeCallbackHandlerServlet</servlet-name> <servlet-class>com.google.oauthsample.OAuthCodeCallbackHandlerServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>OAuthCodeCallbackHandlerServlet</servlet-name> <url-pattern>/oauth2callback</url-pattern> </servlet-mapping> </web-app>
ตรวจสอบสิทธิ์ผู้ใช้ในระบบและขอสิทธิ์เข้าถึงงาน
ผู้ใช้ป้อนแอปพลิเคชันผ่าน URL "/" รูทที่แมปกับเซิร์ฟเล็ต PrintTaskListsTitlesServlet ในเซิร์ฟเล็ตนั้นจะมีการดำเนินการต่อไปนี้
- ตรวจสอบว่าผู้ใช้ผ่านการตรวจสอบสิทธิ์ในระบบหรือไม่
- หากผู้ใช้ไม่ผ่านการตรวจสอบสิทธิ์ ระบบจะเปลี่ยนเส้นทางไปยังหน้าการตรวจสอบสิทธิ์
- หากผู้ใช้ผ่านการตรวจสอบสิทธิ์ เราจะตรวจสอบว่ามีโทเค็นการรีเฟรชอยู่ในพื้นที่เก็บข้อมูลของเราแล้วหรือยัง ซึ่ง OAuthTokenDao จัดการด้านล่าง หากไม่มีโทเค็นการรีเฟรชใน Store สำหรับผู้ใช้ หมายความว่าผู้ใช้ยังไม่ได้ให้สิทธิ์แอปพลิเคชันเพื่อเข้าถึงงานของตน ในกรณีนี้ ระบบจะเปลี่ยนเส้นทางผู้ใช้ไปยังปลายทางการให้สิทธิ์ OAuth 2.0 ของ Google
package com.google.oauthsample; import ... /** * Simple sample Servlet which will display the tasks in the default task list of the user. */ @SuppressWarnings("serial") public class PrintTasksTitlesServlet extends HttpServlet { /** * The OAuth Token DAO implementation, used to persist the OAuth refresh token. * Consider injecting it instead of using a static initialization. Also we are * using a simple memory implementation as a mock. Change the implementation to * using your database system. */ public static OAuthTokenDao oauthTokenDao = new OAuthTokenDaoMemoryImpl(); public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { // Getting the current user // This is using App Engine's User Service but you should replace this to // your own user/login implementation UserService userService = UserServiceFactory.getUserService(); User user = userService.getCurrentUser(); // If the user is not logged-in it is redirected to the login service, then back to this page if (user == null) { resp.sendRedirect(userService.createLoginURL(getFullRequestUrl(req))); return; } // Checking if we already have tokens for this user in store AccessTokenResponse accessTokenResponse = oauthTokenDao.getKeys(user.getEmail()); // If we don't have tokens for this user if (accessTokenResponse == null) { OAuthProperties oauthProperties = new OAuthProperties(); // Redirect to the Google OAuth 2.0 authorization endpoint resp.sendRedirect(new GoogleAuthorizationRequestUrl(oauthProperties.getClientId(), OAuthCodeCallbackHandlerServlet.getOAuthCodeCallbackHandlerUrl(req), oauthProperties .getScopesAsString()).build()); return; } } /** * Construct the request's URL without the parameter part. * * @param req the HttpRequest object * @return The constructed request's URL */ public static String getFullRequestUrl(HttpServletRequest req) { String scheme = req.getScheme() + "://"; String serverName = req.getServerName(); String serverPort = (req.getServerPort() == 80) ? "" : ":" + req.getServerPort(); String contextPath = req.getContextPath(); String servletPath = req.getServletPath(); String pathInfo = (req.getPathInfo() == null) ? "" : req.getPathInfo(); String queryString = (req.getQueryString() == null) ? "" : "?" + req.getQueryString(); return scheme + serverName + serverPort + contextPath + servletPath + pathInfo + queryString; } }
หมายเหตุ: การใช้งานข้างต้นใช้ไลบรารี App Engine บางรายการ ซึ่งมีไว้เพื่อความเรียบง่าย หากคุณจะพัฒนาสำหรับแพลตฟอร์มอื่น คุณก็สามารถนำอินเทอร์เฟซ UserService ที่จัดการการตรวจสอบสิทธิ์ผู้ใช้มาใช้งานอีกครั้งได้
แอปพลิเคชันจะใช้ DAO เพื่อคงและเข้าถึงโทเค็นการให้สิทธิ์ของผู้ใช้ ด้านล่างนี้คืออินเทอร์เฟซ OAuthTokenDao และการจำลองการใช้งาน (ในหน่วยความจำ) - OAuthTokenDaoMemoryImpl ที่ใช้ในตัวอย่างนี้
package com.google.oauthsample; import com.google.api.client.auth.oauth2.draft10.AccessTokenResponse; /** * Allows easy storage and access of authorization tokens. */ public interface OAuthTokenDao { /** * Stores the given AccessTokenResponse using the {@code username}, the OAuth * {@code clientID} and the tokens scopes as keys. * * @param tokens The AccessTokenResponse to store * @param userName The userName associated wit the token */ public void saveKeys(AccessTokenResponse tokens, String userName); /** * Returns the AccessTokenResponse stored for the given username, clientId and * scopes. Returns {@code null} if there is no AccessTokenResponse for this * user and scopes. * * @param userName The username of which to get the stored AccessTokenResponse * @return The AccessTokenResponse of the given username */ public AccessTokenResponse getKeys(String userName); }
package com.google.oauthsample; import com.google.api.client.auth.oauth2.draft10.AccessTokenResponse; ... /** * Quick and Dirty memory implementation of {@link OAuthTokenDao} based on * HashMaps. */ public class OAuthTokenDaoMemoryImpl implements OAuthTokenDao { /** Object where all the Tokens will be stored */ private static MaptokenPersistance = new HashMap (); public void saveKeys(AccessTokenResponse tokens, String userName) { tokenPersistance.put(userName, tokens); } public AccessTokenResponse getKeys(String userName) { return tokenPersistance.get(userName); } }
นอกจากนี้ ข้อมูลเข้าสู่ระบบ OAuth 2.0 สำหรับแอปพลิเคชันจะเก็บอยู่ในไฟล์พร็อพเพอร์ตี้ หรือคุณจะกำหนดให้เป็นค่าคงที่ในคลาส Java ก็ได้ แต่ต่อไปนี้คือคลาส OAuthProperties และไฟล์ oauth.properties ที่ใช้ในตัวอย่าง
package com.google.oauthsample; import ... /** * Object representation of an OAuth properties file. */ public class OAuthProperties { public static final String DEFAULT_OAUTH_PROPERTIES_FILE_NAME = "oauth.properties"; /** The OAuth 2.0 Client ID */ private String clientId; /** The OAuth 2.0 Client Secret */ private String clientSecret; /** The Google APIs scopes to access */ private String scopes; /** * Instantiates a new OauthProperties object reading its values from the * {@code OAUTH_PROPERTIES_FILE_NAME} properties file. * * @throws IOException IF there is an issue reading the {@code propertiesFile} * @throws OauthPropertiesFormatException If the given {@code propertiesFile} * is not of the right format (does not contains the keys {@code * clientId}, {@code clientSecret} and {@code scopes}) */ public OAuthProperties() throws IOException { this(OAuthProperties.class.getResourceAsStream(DEFAULT_OAUTH_PROPERTIES_FILE_NAME)); } /** * Instantiates a new OauthProperties object reading its values from the given * properties file. * * @param propertiesFile the InputStream to read an OAuth Properties file. The * file should contain the keys {@code clientId}, {@code * clientSecret} and {@code scopes} * @throws IOException IF there is an issue reading the {@code propertiesFile} * @throws OAuthPropertiesFormatException If the given {@code propertiesFile} * is not of the right format (does not contains the keys {@code * clientId}, {@code clientSecret} and {@code scopes}) */ public OAuthProperties(InputStream propertiesFile) throws IOException { Properties oauthProperties = new Properties(); oauthProperties.load(propertiesFile); clientId = oauthProperties.getProperty("clientId"); clientSecret = oauthProperties.getProperty("clientSecret"); scopes = oauthProperties.getProperty("scopes"); if ((clientId == null) || (clientSecret == null) || (scopes == null)) { throw new OAuthPropertiesFormatException(); } } /** * @return the clientId */ public String getClientId() { return clientId; } /** * @return the clientSecret */ public String getClientSecret() { return clientSecret; } /** * @return the scopes */ public String getScopesAsString() { return scopes; } /** * Thrown when the OAuth properties file was not at the right format, i.e not * having the right properties names. */ @SuppressWarnings("serial") public class OAuthPropertiesFormatException extends RuntimeException { } }
ด้านล่างนี้เป็นไฟล์ oauth.properties ที่มีข้อมูลเข้าสู่ระบบ OAuth 2.0 ของแอปพลิเคชันของคุณ คุณต้องเปลี่ยนค่าด้านล่างด้วยตนเอง
# Client ID and secret. They can be found in the APIs console. clientId=1234567890.apps.googleusercontent.com clientSecret=aBcDeFgHiJkLmNoPqRsTuVwXyZ # API scopes. Space separated. scopes=https://www.googleapis.com/auth/tasks
รหัสไคลเอ็นต์ OAuth 2.0 และรหัสลับไคลเอ็นต์จะระบุแอปพลิเคชันและอนุญาตให้ Tasks API ใช้ตัวกรองและกฎโควต้าที่กำหนดไว้สำหรับแอปพลิเคชันของคุณ ดูรหัสไคลเอ็นต์และรหัสลับได้ในคอนโซล Google API เมื่ออยู่บนคอนโซล คุณจะต้องดำเนินการดังนี้
- สร้างหรือเลือกโปรเจ็กต์
- เปิดใช้ Tasks API โดยสลับสถานะ Tasks API เป็นเปิด ในรายการบริการ
- ในส่วนการเข้าถึง API ให้สร้างรหัสไคลเอ็นต์ OAuth 2.0 หากยังไม่ได้สร้าง
- ตรวจสอบว่า URL ของตัวแฮนเดิลโค้ดเรียกกลับของ OAuth 2.0 ของโปรเจ็กต์ลงทะเบียน/อนุญาตพิเศษใน URI การเปลี่ยนเส้นทางแล้ว ตัวอย่างเช่น ในโครงการตัวอย่างนี้ คุณจะต้องลงทะเบียน https://www.example.com/oauth2callback หากเว็บแอปพลิเคชันของคุณให้บริการจากโดเมน https://www.example.com
ฟังรหัสการให้สิทธิ์จากปลายทางของ Google Authorization
ในกรณีที่ผู้ใช้ยังไม่ได้ให้สิทธิ์แอปพลิเคชันในการเข้าถึงงาน และถูกเปลี่ยนเส้นทางไปยังปลายทางการให้สิทธิ์ OAuth 2.0 ของ Google ผู้ใช้จะเห็นกล่องโต้ตอบการให้สิทธิ์จาก Google ที่ขอให้ผู้ใช้ให้สิทธิ์เข้าถึงงานแก่แอปพลิเคชันของคุณ ดังนี้
หลังจากอนุญาตหรือปฏิเสธการเข้าถึงแล้ว ระบบจะเปลี่ยนเส้นทางผู้ใช้กลับไปที่เครื่องจัดการโค้ดเรียกกลับ OAuth 2.0 ซึ่งระบุว่าเป็นการเปลี่ยนเส้นทาง/เรียกกลับเมื่อสร้าง URL การให้สิทธิ์ของ Google ดังนี้
new GoogleAuthorizationRequestUrl(oauthProperties.getClientId(), OAuthCodeCallbackHandlerServlet.getOAuthCodeCallbackHandlerUrl(req), oauthProperties .getScopesAsString()).build()
ตัวแฮนเดิลเรียกกลับของรหัส OAuth 2.0 ซึ่งก็คือ OAuthCodeCallbackHandlerServlet จะจัดการการเปลี่ยนเส้นทางจากปลายทาง Google OAuth 2.0 คุณต้องจัดการ 2 กรณีดังนี้
- ผู้ใช้ได้ให้สิทธิ์เข้าถึง: แยกวิเคราะห์คำขอเพื่อรับรหัส OAuth 2.0 จากพารามิเตอร์ของ URL
- ผู้ใช้ปฏิเสธการเข้าถึง: แสดงข้อความต่อผู้ใช้
package com.google.oauthsample; import ... /** * Servlet handling the OAuth callback from the authentication service. We are * retrieving the OAuth code, then exchanging it for a refresh and an access * token and saving it. */ @SuppressWarnings("serial") public class OAuthCodeCallbackHandlerServlet extends HttpServlet { /** The name of the Oauth code URL parameter */ public static final String CODE_URL_PARAM_NAME = "code"; /** The name of the OAuth error URL parameter */ public static final String ERROR_URL_PARAM_NAME = "error"; /** The URL suffix of the servlet */ public static final String URL_MAPPING = "/oauth2callback"; public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { // Getting the "error" URL parameter String[] error = req.getParameterValues(ERROR_URL_PARAM_NAME); // Checking if there was an error such as the user denied access if (error != null && error.length > 0) { resp.sendError(HttpServletResponse.SC_NOT_ACCEPTABLE, "There was an error: \""+error[0]+"\"."); return; } // Getting the "code" URL parameter String[] code = req.getParameterValues(CODE_URL_PARAM_NAME); // Checking conditions on the "code" URL parameter if (code == null || code.length == 0) { resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "The \"code\" URL parameter is missing"); return; } } /** * Construct the OAuth code callback handler URL. * * @param req the HttpRequest object * @return The constructed request's URL */ public static String getOAuthCodeCallbackHandlerUrl(HttpServletRequest req) { String scheme = req.getScheme() + "://"; String serverName = req.getServerName(); String serverPort = (req.getServerPort() == 80) ? "" : ":" + req.getServerPort(); String contextPath = req.getContextPath(); String servletPath = URL_MAPPING; String pathInfo = (req.getPathInfo() == null) ? "" : req.getPathInfo(); return scheme + serverName + serverPort + contextPath + servletPath + pathInfo; } }
แลกเปลี่ยนรหัสการให้สิทธิ์เพื่อรีเฟรชและโทเค็นเพื่อการเข้าถึง
จากนั้น OAuthCodeCallbackHandlerServlet แลกเปลี่ยนรหัส Auth 2.0 เพื่อเติมโทเค็นการรีเฟรชและเข้าถึงโทเค็นดังกล่าวไว้ในพื้นที่เก็บข้อมูล แล้วเปลี่ยนเส้นทางผู้ใช้กลับไปยัง URL ของ PrintTaskListsTitlesServlet ดังนี้
โค้ดที่เพิ่มลงในไฟล์ด้านล่างมีการไฮไลต์ไวยากรณ์ แต่โค้ดที่มีอยู่แล้วจะเป็นสีเทา
package com.google.oauthsample; import ... /** * Servlet handling the OAuth callback from the authentication service. We are * retrieving the OAuth code, then exchanging it for a refresh and an access * token and saving it. */ @SuppressWarnings("serial") public class OAuthCodeCallbackHandlerServlet extends HttpServlet { /** The name of the Oauth code URL parameter */ public static final String CODE_URL_PARAM_NAME = "code"; /** The name of the OAuth error URL parameter */ public static final String ERROR_URL_PARAM_NAME = "error"; /** The URL suffix of the servlet */ public static final String URL_MAPPING = "/oauth2callback";/** URL ที่จะเปลี่ยนเส้นทางผู้ใช้ไปหลังจากจัดการโค้ดเรียกกลับ โปรดพิจารณา * บันทึกส่วนนี้ในคุกกี้ก่อนเปลี่ยนเส้นทางผู้ใช้ไปยัง URL การให้สิทธิ์ของ Google * หากคุณมี URL หลายรายการที่จะเปลี่ยนเส้นทางผู้ใช้ไปได้ */ Public staticFinal String REDIRECT_URL = "/"; /** การใช้งาน DAO ของโทเค็น OAuth ลองแทรกโค้ดแทนการใช้ * การเริ่มต้นแบบคงที่ นอกจากนี้เรายังใช้หน่วยความจำอย่างง่าย * เป็นการจำลอง เปลี่ยนการใช้งานไปใช้ระบบฐานข้อมูลของคุณ */ อีเมล OAuthTokenDao แบบคงที่ สาธารณะ .public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { // Getting the "error" URL parameter String[] error = req.getParameterValues(ERROR_URL_PARAM_NAME); // Checking if there was an error such as the user denied access if (error != null && error.length > 0) { resp.sendError(HttpServletResponse.SC_NOT_ACCEPTABLE, "There was an error: \""+error[0]+"\"."); return; } // Getting the "code" URL parameter String[] code = req.getParameterValues(CODE_URL_PARAM_NAME); // Checking conditions on the "code" URL parameter if (code == null || code.length == 0) { resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "The \"code\" URL parameter is missing"); return; }/** * Construct the OAuth code callback handler URL. * * @param req the HttpRequest object * @return The constructed request's URL */ public static String getOAuthCodeCallbackHandlerUrl(HttpServletRequest req) { String scheme = req.getScheme() + "://"; String serverName = req.getServerName(); String serverPort = (req.getServerPort() == 80) ? "" : ":" + req.getServerPort(); String contextPath = req.getContextPath(); String servletPath = URL_MAPPING; String pathInfo = (req.getPathInfo() == null) ? "" : req.getPathInfo(); return scheme + serverName + serverPort + contextPath + servletPath + pathInfo; }* * โค้ด @param โค้ดที่ส่งคืนจากบริการการให้สิทธิ์ * @param currentUrl URL ของฟังก์ชันเรียกกลับ * @param oauthProperties ออบเจ็กต์ที่มีการกำหนดค่า OAuth * @return ออบเจ็กต์ที่มีทั้งโทเค็นการเข้าถึงและรีเฟรช * @throws IO DSA */ รูปภาพ @param currentUrl รหัสการเรียกกลับ * @param oauthProperties ออบเจ็กต์ที่มีการกำหนดค่า OAuth * @return ออบเจ็กต์ที่มีทั้งโทเค็นการเข้าถึงและรีเฟรชไฟล์ OAuthCodeCallbackHandlerServlet.javaหมายเหตุ: การใช้งานข้างต้นใช้ไลบรารี App Engine บางรายการ ซึ่งมีไว้เพื่อความเรียบง่าย หากคุณจะพัฒนาสำหรับแพลตฟอร์มอื่น คุณก็สามารถนำอินเทอร์เฟซ UserService ที่จัดการการตรวจสอบสิทธิ์ผู้ใช้มาใช้งานอีกครั้งได้
อ่านงานของผู้ใช้และแสดง
ผู้ใช้ได้ให้สิทธิ์เข้าถึงงานแก่แอปพลิเคชันแล้ว แอปพลิเคชันมีโทเค็นการรีเฟรชที่บันทึกไว้ในพื้นที่เก็บข้อมูลซึ่งเข้าถึงได้ผ่าน OAuthTokenDao ปัจจุบันเซิร์ฟเล็ต PrintTaskListsTitlesServlet สามารถใช้โทเค็นเหล่านี้เพื่อเข้าถึงงานของผู้ใช้และแสดงงานได้
โค้ดที่เพิ่มลงในไฟล์ด้านล่างมีการไฮไลต์ไวยากรณ์ แต่โค้ดที่มีอยู่แล้วจะเป็นสีเทา
package com.google.oauthsample; import ... /** * Simple sample Servlet which will display the tasks in the default task list of the user. */ @SuppressWarnings("serial") public class PrintTasksTitlesServlet extends HttpServlet { /** * The OAuth Token DAO implementation, used to persist the OAuth refresh token. * Consider injecting it instead of using a static initialization. Also we are * using a simple memory implementation as a mock. Change the implementation to * using your database system. */ public static OAuthTokenDao oauthTokenDao = new OAuthTokenDaoMemoryImpl(); public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { // Getting the current user // This is using App Engine's User Service but you should replace this to // your own user/login implementation UserService userService = UserServiceFactory.getUserService(); User user = userService.getCurrentUser(); // If the user is not logged-in it is redirected to the login service, then back to this page if (user == null) { resp.sendRedirect(userService.createLoginURL(getFullRequestUrl(req))); return; } // Checking if we already have tokens for this user in store AccessTokenResponse accessTokenResponse = oauthTokenDao.getKeys(user.getEmail()); // If we don't have tokens for this user if (accessTokenResponse == null) { OAuthProperties oauthProperties = new OAuthProperties(); // Redirect to the Google OAuth 2.0 authorization endpoint resp.sendRedirect(new GoogleAuthorizationRequestUrl(oauthProperties.getClientId(), OAuthCodeCallbackHandlerServlet.getOAuthCodeCallbackHandlerUrl(req), oauthProperties .getScopesAsString()).build()); return; }// การพิมพ์งานของผู้ใช้จะแสดงชื่อในคำตอบ resp.setContentType("text/plain"); resp.getWriter().append("Task List name for user " + user.getEmail() + ":\n\n"); PrintTasksTitles(accessTokenResponse, resp.getWriter());} /** * Construct the request's URL without the parameter part. * * @param req the HttpRequest object * @return The constructed request's URL */ public static String getFullRequestUrl(HttpServletRequest req) { String scheme = req.getScheme() + "://"; String serverName = req.getServerName(); String serverPort = (req.getServerPort() == 80) ? "" : ":" + req.getServerPort(); String contextPath = req.getContextPath(); String servletPath = req.getServletPath(); String pathInfo = (req.getPathInfo() == null) ? "" : req.getPathInfo(); String queryString = (req.getQueryString() == null) ? "" : "?" + req.getQueryString(); return scheme + serverName + serverPort + contextPath + servletPath + pathInfo + queryString; }/** * เรียกใช้รายการงานของ Google ใน Tasks ตามค่าเริ่มต้น * * @param accessTokenResponse ออบเจ็กต์ AccessTokenResponse ของ OAuth 2.0 * ที่มีโทเค็นเพื่อการเข้าถึงและโทเค็นการรีเฟรช * @param เอาต์พุตจะเป็นผู้เขียนสตรีมเอาต์พุตที่ต้องการให้แสดงงานแสดงชื่อ * @return รายการชื่องานของผู้ใช้ในรายการงานเริ่มต้น * @throws IOException */ Public void PrintTasksTitles(AccessTokenResponse accessTokenResponse, เอาต์พุตของ Writer) จะส่ง IOException { // เริ่มต้นบริการงาน. <a < > < {/1}>การทำงาน [เรียกดูการทำงาน]ไฟล์ PrintTasksTitlesServlet.javaระบบจะแสดงผู้ใช้พร้อมกับงานต่อไปนี้
งานของผู้ใช้แอปพลิเคชันตัวอย่าง
โค้ดสำหรับแอปพลิเคชันตัวอย่างนี้ดาวน์โหลดได้ที่นี่ ลองเข้าไปดูได้เลย