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

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

الجمهور

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

الفهرس

من أجل أن تعمل العينة بالكامل، تعد عدة خطوات ضرورية، تحتاج إلى:

تعريف تعيينات servlet في ملف web.xml

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

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

في ما يلي ملف web.xml الذي يربط هاتين الخدمتين بعناوين 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 الجذر "/" الذي تم تعيينه لـ 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 التي تعالج مصادقة المستخدم.

يستخدم التطبيق نظام 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);
}
ملف 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 tokenPersistance = new HashMap();

  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 للتطبيق في ملف خصائص. وبدلاً من ذلك، يمكنك ببساطة جعلها ثابتة في مكان ما في إحدى فئات جافا، على الرغم من وجود فئة 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 وسر العميل تطبيقك ويُسمَح لواجهة برمجة تطبيقات "مهام Google" بتطبيق الفلاتر وقواعد الحصص المحدَّدة لتطبيقك. يمكن العثور على معرِّف العميل والسر في وحدة تحكم Google APIs. بعد الوصول إلى وحدة التحكّم، عليك تنفيذ ما يلي:

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

عنوان URL لإعادة التوجيه في وحدة تحكم واجهات برمجة التطبيقات
عنوان URL لإعادة التوجيه في وحدة تحكم واجهات برمجة التطبيقات

الاستماع إلى رمز التفويض من نقطة نهاية تفويض 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 = "/"; /** تنفيذ رمز OAuth المميز بتنسيق DAO. يمكنك إدخاله بدلاً من استخدام * إعداد ثابت. ونستخدم أيضًا طريقة تنفيذ ذاكرة بسيطة * كنموذج. غيِّر عملية التنفيذ إلى استخدام نظام قاعدة البيانات. */ 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 = getCodeCallbackHandlerUrl(req); // Exchange الرمز المميّز لـ OAuth //UserServiceToken() //CurrentServiceSignService UserImp = 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 = getCodeCallbackHandlerUrl(req); // Exchange the icon //CurrentServiceToken(//reauthToken UserResponse = exchangeCodeForAccessAndPreviewTokens(//codeService9Userبِمَنْ ){currentUser لإتاحة الوصول إلى هذا الرمز.{قة.}
  /**
   * 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;
  }
* * @paramcode The icon get back from the التفويض service * @param currentUrl The URL of the callback * @param oauthProperties The object where the OAuth Configuration * @return الكائن الذي يحتوي على كلٍ من ملف الوصول والرمز المُعدَّل * @throws IOException */ public AccessToken IOException (String @param currentUrl ); String currentUrl Properties Hrows Currents currentUrl) نُ المتوفرة جديد لـ OAuth حاصل على تفويض OAuth .auth
ملف 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 Lists title 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 في واجهة برمجة التطبيقات الافتراضية للمستخدمين. * * @param accessTokenResponse كائن AccessTokenResponse 2.0 في OAuth * الذي يحتوي على رمز الدخول ورمز مميز لإعادة التحميل. * تُخرج @param كاتب بث الإخراج حيث يمكن مراجعة عناوين قوائم المهام. * @return قائمة بعناوين مهام المستخدمين في قائمة المهام التلقائية. * @throws IOException */ public void printTasksTitles(AccessTokenResponse accessTokenResponse, // مقطع تهيئة خدمة مهام Google HttpTransport Transport = new NetHttpTransport = new NetHttpTransport = new NetHttpTransport = new NetHttpTransport = new json ResourceOrigin = new JacksonResourceProtected); OAuthProperties oauth التسعير accessToken.
ملف PrintTasksTitlesServlet.java

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

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

نموذج تطبيق

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