معالج معاودة الاتصال لتأكيد الإعداد

يوضّح هذا المستند كيفية تنفيذ معالِج طلب إعادة الاتصال الخاص بتفويض OAuth 2.0 باستخدام تطبيقات Java Servlet من خلال نموذج لتطبيق ويب سيعرض مهام المستخدم باستخدام Google Tasks API. سيطلب نموذج التطبيق أولاً إذن الوصول إلى "مهام Google" للمستخدم، ثم سيعرض مهام المستخدم في قائمة المهام التلقائية.

الجمهور

تم تصميم هذا المستند للأشخاص الذين لديهم دراية ببنية تطبيقات الويب Java وJ2EE. ننصحك بالاطّلاع على بعض المعلومات عن مسار التفويض في OAuth 2.0.

المحتويات

للحصول على نموذج يعمل بشكل كامل، عليك اتّباع عدة خطوات:

تحديد عمليات ربط تطبيقات servlet في ملف web.xml

سنستخدم servlets اثنين في تطبيقنا:

  • PrintTasksTitlesServlet (تم ربطه بـ /): نقطة دخول التطبيق التي ستتولى مصادقة المستخدم، وتعرض مهامه
  • OAuthCodeCallbackHandlerServlet (تمّ ربطه بـ ‎/oauth2callback): دالة الاستدعاء في OAuth 2.0 التي تعالج الاستجابة الواردة من نقطة نهاية مصادقة OAuth

في ما يلي ملف web.xml الذي يربط بين خدمتَي servlet هاتين وعناوين 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>
ملف ‎/WEB-INF/web.xml

مصادقة المستخدمين على نظامك وطلب تفويض للوصول إلى مهامه

يدخل المستخدم إلى التطبيق من خلال عنوان URL الجذر "‎/" الذي تم ربطه بتطبيق servlet PrintTaskListsTitlesServlet. في هذه الخدمة الصغيرة، يتم تنفيذ المهام التالية:

  • التحقّق مما إذا تمّت مصادقة المستخدم على النظام
  • إذا لم تتم مصادقة المستخدم، تتم إعادة توجيهه إلى صفحة المصادقة.
  • في حال تمّت مصادقة المستخدم، نتحقّق ممّا إذا كان لدينا رمز إعادة تعيين متوفرًا في مساحة تخزين البيانات، وهو ما يعالجه OAuthTokenDao أدناه. إذا لم يكن هناك رمز مميّز لإعادة التحميل محفوظًا للمستخدم، يعني ذلك أنّ المستخدم لم يمنح التطبيق إذن الوصول إلى مهامه بعد. وفي هذه الحالة، تتم إعادة توجيه المستخدم إلى نقطة نهاية التفويض في بروتوكول 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;
  }
}
ملف PrintTasksTitlesServlet.java

ملاحظة: يستخدم الإجراء أعلاه بعض مكتبات App Engine، ويتم استخدامها لتبسيط العملية. إذا كنت تُطوّر لنظام أساسي آخر، يمكنك إعادة تنفيذ واجهة UserService التي تتعامل مع مصادقة المستخدم.

يستخدم التطبيق واجهة برمجة تطبيقات للحفاظ على رموز التفويض للمستخدمين والوصول إليها. في ما يلي الواجهة 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);
}
ملف 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);
  }
}
ملف OAuthTokenDaoMemoryImpl.java

يتم أيضًا تخزين بيانات اعتماد 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 {
  }
}
ملف OAuthProperties.java

في ما يلي ملف 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.properties

يحدِّد معرِّف عميل OAuth 2.0 وسرّ العميل تطبيقك ويسمح لواجهة برمجة التطبيقات Tasks API بتطبيق الفلاتر وقواعد الحصة المحدَّدة لتطبيقك. يمكن العثور على معرّف العميل وسرّه في وحدة تحكّم Google APIs. بعد الانتقال إلى وحدة التحكّم، عليك إجراء ما يلي:

  • إنشاء مشروع أو اختياره
  • فعِّل واجهة برمجة التطبيقات Tasks API من خلال ضبط حالتها على تفعيل في قائمة الخدمات.
  • ضمن الوصول إلى واجهة برمجة التطبيقات، أنشئ معرِّف عميل OAuth 2.0 إذا لم يسبق إنشاؤه.
  • تأكَّد من تسجيل عنوان URL لمعالج طلب إعادة الاتصال برمز OAuth 2.0 في المشروع أو إضافته إلى القائمة المسموح بها في معرّفات الموارد المنتظمة لإعادة التوجيه. على سبيل المثال، في نموذج المشروع هذا، عليك تسجيل https://www.example.com/oauth2callback إذا كان تطبيق الويب الخاص بك معروضًا من نطاق https://www.example.com.

معرّف الموارد المنتظم (URI) لإعادة التوجيه في &quot;وحدة تحكّم واجهات برمجة التطبيقات&quot;
معرّف الموارد المنتظم (URI) لإعادة التوجيه في "وحدة تحكّم واجهات برمجة التطبيقات"

الاستماع إلى رمز التفويض من نقطة نهاية "تفويض Google"

في حال لم يمنح المستخدم بعد الإذن للتطبيق بالوصول إلى مهامه، وبالتالي تمت إعادة توجيهه إلى نقطة نهاية التفويض في بروتوكول OAuth 2.0 من Google، سيظهر للمستخدم مربّع حوار تفويض من Google يطلب منه منح تطبيقك الإذن بالوصول إلى مهامه:

مربّع حوار تفويض 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. هناك حالتان يجب التعامل معهما:

  • منح المستخدم الإذن بالوصول: يتم تحليل الطلب للحصول على رمز 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.java

تبديل رمز التفويض برمزَي إعادة التحميل والدخول

بعد ذلك، يتبادل 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 static final String REDIRECT_URL = "/"; /** The OAuth Token DAO implementation. ننصحك بإدخالها بدلاً من استخدام * عملية إعداد ثابتة. نستخدم أيضًا عملية تنفيذ ذاكرة بسيطة * كنموذج. غيِّر عملية التنفيذ لاستخدام نظام قاعدة البيانات. */ 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;
    }
// إنشاء عنوان URL للطلب الوافد String requestUrl = getOAuthCodeCallbackHandlerUrl(req); // استبدال الرمز بعلامات مميّزة لبروتوكول OAuth AccessTokenResponse accessTokenResponse = exchangeCodeForAccessAndRefreshTokens(code[0], requestUrl); // الحصول على المستخدم الحالي // يتم استخدام خدمة المستخدم في App Engine، ولكن عليك استبدالها // بتطبيقك الخاص لتسجيل الدخول/المستخدمين UserService userService = UserServiceFactory.getUserService(); String email = userService.getCurrentUser().getEmail(); // حفظ العلامات المميّزة 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;
  }
/** * تبديل الرمز المحدَّد برمز تبادل ورمز تحديث * * @param code الرمز الذي تم استرجاعه من خدمة التفويض * @param currentUrl عنوان URL لطلب إعادة الاتصال * @param oauthProperties العنصر الذي يحتوي على إعدادات OAuth * @return العنصر الذي يحتوي على كل من رمز الوصول ورمز إعادة التحميل * @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(); } }
ملف OAuthCodeCallbackHandlerServlet.java

ملاحظة: يستخدم الإجراء أعلاه بعض مكتبات App Engine، ويتم استخدامها لتبسيط العملية. إذا كنت تُطوّر لنظام أساسي آخر، يمكنك إعادة تنفيذ واجهة UserService التي تتعامل مع مصادقة المستخدم.

قراءة مهام المستخدم وعرضها

منح المستخدم التطبيق إذن الوصول إلى مهامه. يحتوي التطبيق على رمز إعادة تحميل يتم حفظه في قاعدة البيانات التي يمكن الوصول إليها من خلال OAuthTokenDao. يمكن الآن لتطبيق servlet‏ 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("عناوين قوائم المهام للمستخدم " + 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 API لاسترداد قائمة بمهام المستخدِمين في قائمة المهام التلقائية *. * * @param accessTokenResponse عنصر AccessTokenResponse في OAuth 2.0 * يحتوي على رمز الدخول المميّز ورمز إعادة التحميل. * @param output كاتب بث الإخراج الذي يتم فيه كتابة عناوين قوائم المهام * @return قائمة بعناوين مهام المستخدمين في قائمة المهام التلقائية * @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"); } } }
ملف PrintTasksTitlesServlet.java

سيتم عرض المستخدم مع مهامه:

مهام المستخدم
مهام المستخدم

نموذج طلب

يمكن تنزيل رمز نموذج التطبيق هذا هنا. لا تتردد في الاطّلاع عليه.