Trình xử lý gọi lại uỷ quyền bản dựng

Tài liệu này giải thích cách triển khai trình xử lý lệnh gọi lại uỷ quyền OAuth 2.0 bằng cách sử dụng servlet Java thông qua một ứng dụng web mẫu hiển thị các việc cần làm của người dùng bằng Google Tasks API. Trước tiên, ứng dụng mẫu sẽ yêu cầu uỷ quyền truy cập vào Google Tasks của người dùng, sau đó hiển thị các việc cần làm của người dùng trong danh sách việc cần làm mặc định.

Đối tượng

Tài liệu này dành cho những người quen thuộc với Java và cấu trúc ứng dụng web J2EE. Bạn nên có một số kiến thức về quy trình uỷ quyền OAuth 2.0.

Nội dung

Để có mẫu hoạt động đầy đủ như vậy, bạn cần thực hiện một số bước sau:

Khai báo các ánh xạ servlet trong tệp web.xml

Ứng dụng này sử dụng 2 servlet sau:

  • PrintTasksTitlesServlet (được liên kết với /): Điểm truy cập của ứng dụng sẽ xử lý việc xác thực người dùng và hiển thị các việc cần làm của người dùng
  • OAuthCodeCallbackHandlerServlet (được liên kết với /oauth2callback): Lệnh gọi lại OAuth 2.0 xử lý phản hồi từ điểm cuối uỷ quyền OAuth

Tệp web.xml sau đây sẽ liên kết 2 servlet này với các URL trong ứng dụng của chúng ta:

<?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>
Tệp /WEB-INF/web.xml

Xác thực người dùng trên hệ thống của họ và yêu cầu uỷ quyền truy cập vào các việc cần làm của họ

Người dùng truy cập vào ứng dụng thông qua URL gốc "/" được liên kết với servlet PrintTaskListsTitlesServlet. Trong servlet đó, các tác vụ sau được thực hiện:

  • Kiểm tra xem người dùng có được xác thực trên hệ thống hay không.
  • Nếu chưa được xác thực, người dùng sẽ được chuyển hướng đến trang xác thực.
  • Nếu người dùng được xác thực, hệ thống sẽ kiểm tra xem mã làm mới đã có trong bộ nhớ dữ liệu hay chưa. Thao tác này do OAuthTokenDao bên dưới xử lý. Nếu người dùng không có mã thông báo trong bộ nhớ, điều này có nghĩa là người dùng chưa cấp cho ứng dụng quyền truy cập vào các việc cần làm của họ. Sau đó, người dùng sẽ được chuyển hướng đến Điểm cuối uỷ quyền OAuth 2.0 của Google.

Sau đây là một cách để triển khai việc này:

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. Additionally, a
   * simple memory implementation is used as a mock. Change the implementation to
   * using the user's own user/login implementation.
   */
  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 tokens are not available for this user
    if (accessTokenResponse == null) {
      OAuthProperties oauthProperties = new OAuthProperties();
      // Redirects 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;
  }
}

Lưu ý: Việc triển khai ở trên sử dụng một số thư viện App Engine. Các giá trị này được dùng để đơn giản hoá. Nếu bạn đang phát triển cho một nền tảng khác, hãy triển khai lại giao diện UserService xử lý việc xác thực người dùng.

Ứng dụng này sử dụng một DAO để duy trì và truy cập vào mã thông báo uỷ quyền của người dùng. Giao diện OAuthTokenDao và một hoạt động triển khai mô phỏng (trong bộ nhớ) – OAuthTokenDaoMemoryImpl – được dùng trong mẫu này, xuất hiện trong các ví dụ sau:

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);
}
Tệp OAuthTokenDao.java
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 Map<String, AccessTokenResponse> tokenPersistance = new HashMap<String, AccessTokenResponse>();

  public void saveKeys(AccessTokenResponse tokens, String userName) {
    tokenPersistance.put(userName, tokens);
  }

  public AccessTokenResponse getKeys(String userName) {
    return tokenPersistance.get(userName);
  }
}
Tệp OAuthTokenDaoMemoryImpl.java

Thông tin đăng nhập OAuth 2.0 cho ứng dụng được lưu trữ trong một tệp thuộc tính. Ngoài ra, bạn có thể lưu trữ các hằng số này ở đâu đó trong một trong các lớp java của mình. Sau đây là lớp OAuthProperties và tệp oauth.properties đang được dùng trong mẫu:

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 in the correct format (does not contain 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 {
  }
}
Tệp OAuthProperties.java

Tệp oauth.properties chứa thông tin đăng nhập OAuth 2.0 cho ứng dụng của bạn, như trong ví dụ sau. Bạn cần thay đổi các giá trị trong tệp này.

# 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
Tệp oauth.properties

Mã ứng dụng khách và Khoá bí mật của ứng dụng khách OAuth 2.0 xác định ứng dụng và cho phép Tasks API áp dụng các bộ lọc và quy tắc hạn mức được xác định cho ứng dụng. Bạn có thể tìm thấy mã ứng dụng và khoá bí mật của ứng dụng khách trong Bảng điều khiển API của Google. Sau khi truy cập vào bảng điều khiển, người dùng phải:

  • Tạo hoặc chọn một dự án.
  • Bật Tasks API bằng cách đặt trạng thái Tasks API thành BẬT trong danh sách dịch vụ.
  • Trong phần Quyền truy cập vào API, hãy tạo một mã ứng dụng OAuth 2.0 nếu bạn chưa tạo.
  • Đảm bảo rằng URL trình xử lý lệnh gọi lại mã OAuth 2.0 của dự án được đăng ký/đưa vào danh sách cho phép trong URI chuyển hướng. Ví dụ: trong dự án mẫu này, người dùng sẽ phải đăng ký https://www.example.com/oauth2callback nếu ứng dụng web được phân phát từ miền https://www.example.com.

URI chuyển hướng trong APIs Console
URI chuyển hướng trong APIs Console

Xử lý mã uỷ quyền từ điểm cuối Uỷ quyền của Google

Trong trường hợp người dùng chưa uỷ quyền cho ứng dụng truy cập vào các việc cần làm của họ và do đó được chuyển hướng đến điểm cuối Uỷ quyền OAuth 2.0 của Google, người dùng sẽ thấy một hộp thoại uỷ quyền của Google nhắc người dùng cấp cho ứng dụng quyền truy cập vào các việc cần làm của họ:

Hộp thoại uỷ quyền của Google
Hộp thoại uỷ quyền của Google

Sau khi cấp hoặc từ chối quyền truy cập, người dùng sẽ được chuyển hướng trở lại trình xử lý lệnh gọi lại mã OAuth 2.0 được chỉ định làm lệnh chuyển hướng/lệnh gọi lại khi tạo URL uỷ quyền của Google:

new GoogleAuthorizationRequestUrl(oauthProperties.getClientId(),
      OAuthCodeCallbackHandlerServlet.getOAuthCodeCallbackHandlerUrl(req), oauthProperties
          .getScopesAsString()).build()

Trình xử lý lệnh gọi lại mã OAuth 2.0 – OAuthCodeCallbackHandlerServlet – xử lý lệnh chuyển hướng từ điểm cuối OAuth 2.0 của Google. Có 2 trường hợp cần xử lý:

  • Người dùng đã cấp quyền truy cập: yêu cầu được phân tích cú pháp để lấy mã OAuth 2.0 từ các tham số URL.
  • Người dùng đã từ chối cấp quyền truy cập: một thông báo sẽ xuất hiện cho người dùng.
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;
  }
}
Tệp OAuthCodeCallbackHandlerServlet.java

Trao đổi mã uỷ quyền để lấy mã làm mới và mã truy cập

Sau đó, OAuthCodeCallbackHandlerServlet sẽ trao đổi mã Auth 2.0 để lấy mã làm mới và mã truy cập, duy trì mã này trong kho dữ liệu và chuyển hướng người dùng trở lại URL PrintTaskListsTitlesServlet:

Mã được thêm vào tệp sẽ được đánh dấu.

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";
  /** The URL to redirect the user to after handling the callback. Consider
   * saving this in a cookie before redirecting users to the Google
   * authorization URL if you have multiple possible URL to redirect people to. */
  public static final String REDIRECT_URL = "/";

  /** The OAuth Token DAO implementation. 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 "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 incoming request URL
    String requestUrl = getOAuthCodeCallbackHandlerUrl(req);

    // Exchange the code for OAuth tokens
    AccessTokenResponse accessTokenResponse = exchangeCodeForAccessAndRefreshTokens(code[0],
        requestUrl);

    // Getting the current user
    // This is using App Engine's User Service, but the user should replace this
    // with their own user/login implementation
    UserService userService = UserServiceFactory.getUserService();
    String email = userService.getCurrentUser().getEmail();

    // Save the tokens
    oauthTokenDao.saveKeys(accessTokenResponse, email);

    resp.sendRedirect(REDIRECT_URL);
  }

  /**
   * 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;
  }
  /**
   * Exchanges the given code for an exchange and a refresh token.
   *
   * @param code The code gotten back from the authorization service
   * @param currentUrl The URL of the callback
   * @param oauthProperties The object containing the OAuth configuration
   * @return The object containing both an access and refresh token
   * @throws IOException
   */
  public AccessTokenResponse exchangeCodeForAccessAndRefreshTokens(String code, String currentUrl)
      throws IOException {

    HttpTransport httpTransport = new NetHttpTransport();
    JacksonFactory jsonFactory = new JacksonFactory();

    // Loading the oauth config file
    OAuthProperties oauthProperties = new OAuthProperties();

    return new GoogleAuthorizationCodeGrant(httpTransport, jsonFactory, oauthProperties
        .getClientId(), oauthProperties.getClientSecret(), code, currentUrl).execute();
  }
}
Tệp OAuthCodeCallbackHandlerServlet.java

Lưu ý: Việc triển khai trước đó sử dụng một số thư viện App Engine. Các thư viện này được dùng để đơn giản hoá. Nếu bạn đang phát triển cho một nền tảng khác, hãy triển khai lại giao diện UserService xử lý việc xác thực người dùng.

Đọc các việc cần làm của người dùng và hiển thị các việc đó

Người dùng đã cấp cho ứng dụng quyền truy cập vào các việc cần làm của họ. Ứng dụng có một mã làm mới được lưu trong kho dữ liệu có thể truy cập thông qua OAuthTokenDao. Giờ đây, servlet PrintTaskListsTitlesServlet có thể dùng các mã thông báo này để truy cập vào các tác vụ của người dùng và hiển thị chúng:

Mã được thêm vào tệp sẽ được đánh dấu.

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. Additionally, a
   * simple memory implementation is used as a mock. Change the implementation to
   * use your own 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;
    }
// Prints the user's task list titles in the response
    resp.setContentType("text/plain");
    resp.getWriter().append("Task Lists titles 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;
  }
  /**
   * Uses the Google Tasks API to retrieve a list of the user's tasks in the default
   * tasks list.
   *
   * @param accessTokenResponse The OAuth 2.0 AccessTokenResponse object
   *          containing the access token and a refresh token.
   * @param output The output stream writer to write the task list titles to.
   * @return A list of the user's task titles in the default task list.
   * @throws IOException
   */
  public void printTasksTitles(AccessTokenResponse accessTokenResponse, Writer output) throws IOException {

    // Initializing the Tasks service
    HttpTransport transport = new NetHttpTransport();
    JsonFactory jsonFactory = new JacksonFactory();
    OAuthProperties oauthProperties = new OAuthProperties();

    GoogleAccessProtectedResource accessProtectedResource = new GoogleAccessProtectedResource(
        accessTokenResponse.accessToken, transport, jsonFactory, oauthProperties.getClientId(),
        oauthProperties.getClientSecret(), accessTokenResponse.refreshToken);

    Tasks service = new Tasks(transport, accessProtectedResource, jsonFactory);

    // Using the initialized Tasks API service to query the list of tasks lists
    com.google.api.services.tasks.model.Tasks tasks = service.tasks.list("@default").execute();

    for (Task task : tasks.items) {
      output.append(task.title + "\n");
    }
  }
}
Tệp PrintTasksTitlesServlet.java

Các việc cần làm của người dùng sẽ xuất hiện:

Danh sách các việc cần làm của người dùng
Việc cần làm của người dùng

Ứng dụng mẫu

Bạn có thể tải mã cho ứng dụng mẫu này xuống.