استفاده از OAuth 2.0 با Google API Client Library برای جاوا

بررسی اجمالی

هدف: این سند نحوه استفاده از کلاس ابزار GoogleCredential برای انجام مجوز OAuth 2.0 با خدمات Google را توضیح می دهد. برای اطلاعات در مورد توابع عمومی OAuth 2.0 که ارائه می کنیم، OAuth 2.0 و Google OAuth Client Library برای جاوا را ببینید.

خلاصه: برای دسترسی به داده های محافظت شده ذخیره شده در سرویس های Google، از OAuth 2.0 برای مجوز استفاده کنید. API های Google از جریان های OAuth 2.0 برای انواع مختلف برنامه های مشتری پشتیبانی می کنند. در تمام این جریان‌ها، برنامه مشتری یک نشانه دسترسی را درخواست می‌کند که فقط با برنامه مشتری شما و مالک داده‌های محافظت‌شده در حال دسترسی مرتبط است. نشانه دسترسی همچنین با محدوده محدودی مرتبط است که نوع داده‌هایی را که برنامه مشتری شما به آن دسترسی دارد را مشخص می‌کند (به عنوان مثال "مدیریت وظایف خود"). یک هدف مهم برای OAuth 2.0 این است که دسترسی ایمن و راحت به داده های محافظت شده را فراهم کند، در حالی که تأثیر احتمالی در صورت سرقت توکن دسترسی را به حداقل می رساند.

بسته‌های OAuth 2.0 در Google API Client Library برای جاوا بر اساس Google OAuth 2.0 Client Library برای جاوا ساخته شده‌اند.

برای جزئیات، به مستندات Javadoc برای بسته های زیر مراجعه کنید:

کنسول API گوگل

قبل از اینکه بتوانید به Google API دسترسی داشته باشید، باید پروژه‌ای را در Google API Console برای اهداف احراز هویت و صورت‌حساب راه‌اندازی کنید، خواه کلاینت شما یک برنامه نصب‌شده باشد، یک برنامه تلفن همراه، یک وب سرور یا یک کلاینت که در مرورگر اجرا می‌شود.

برای دستورالعمل‌های مربوط به تنظیم صحیح اطلاعات کاربری خود، به راهنمای کنسول API مراجعه کنید.

اعتبارنامه

اعتبار Google

GoogleCredential یک کلاس کمکی ایمن رشته ای برای OAuth 2.0 برای دسترسی به منابع محافظت شده با استفاده از یک نشانه دسترسی است. به عنوان مثال، اگر قبلاً یک نشانه دسترسی دارید، می توانید به روش زیر درخواست دهید:

GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken);
Plus plus = new Plus.builder(new NetHttpTransport(),
                             GsonFactory.getDefaultInstance(),
                             credential)
    .setApplicationName("Google-PlusSample/1.0")
    .build();

هویت موتور برنامه Google

این اعتبار جایگزین بر اساس Google App Engine App Identity Java API است. برخلاف اعتباری که در آن یک برنامه مشتری درخواست دسترسی به داده‌های کاربر نهایی را می‌دهد، App Identity API دسترسی به داده‌های خود برنامه مشتری را فراهم می‌کند.

از AppIdentityCredential (از google-api-client-appengine ) استفاده کنید. این اعتبار بسیار ساده تر است زیرا Google App Engine از تمام جزئیات مراقبت می کند. شما فقط محدوده OAuth 2.0 مورد نیاز خود را مشخص می کنید.

کد مثال گرفته شده از urlshortener-robots-appengine-sample :

static Urlshortener newUrlshortener() {
  AppIdentityCredential credential =
      new AppIdentityCredential(
          Collections.singletonList(UrlshortenerScopes.URLSHORTENER));
  return new Urlshortener.Builder(new UrlFetchTransport(),
                                  GsonFactory.getDefaultInstance(),
                                  credential)
      .build();
}

ذخیره اطلاعات

تاریخ انقضای یک نشانه دسترسی معمولاً 1 ساعت است و پس از آن در صورت استفاده از آن با خطا مواجه می شوید. GoogleCredential از "بازسازی" خودکار توکن مراقبت می کند، که به سادگی به معنای دریافت رمز دسترسی جدید است. این کار با استفاده از یک نشانه تازه‌سازی طولانی مدت انجام می‌شود، که معمولاً در صورت استفاده از پارامتر access_type=offline در طول جریان کد مجوز، همراه با رمز دسترسی دریافت می‌شود (به GoogleAuthorizationCodeFlow.Builder.setAccessType(String) مراجعه کنید).

بیشتر برنامه‌ها باید رمز دسترسی اعتبارنامه و/یا نشانه تازه‌سازی را حفظ کنند. برای تداوم دسترسی به اعتبار و/یا بازخوانی توکن‌ها، می‌توانید پیاده‌سازی DataStoreFactory خود را با StoredCredential ارائه دهید. یا می توانید از یکی از پیاده سازی های زیر ارائه شده توسط کتابخانه استفاده کنید:

  • AppEngineDataStoreFactory : اعتبار را با استفاده از Google App Engine Data Store API حفظ می کند.
  • MemoryDataStoreFactory : اعتبارنامه را در حافظه "پاک می کند" که فقط به عنوان یک ذخیره سازی کوتاه مدت برای طول عمر فرآیند مفید است.
  • FileDataStoreFactory : اعتبارنامه را در یک فایل حفظ می کند.

کاربران AppEngine: AppEngineCredentialStore منسوخ شده است و به زودی حذف خواهد شد. توصیه می کنیم از AppEngineDataStoreFactory با StoredCredential استفاده کنید. اگر اعتبارنامه هایی را به روش قدیمی ذخیره کرده اید، می توانید از روش های کمکی اضافه شده migrateTo(AppEngineDataStoreFactory) یا migrateTo(DataStore) برای انجام انتقال استفاده کنید.

می‌توانید از DataStoreCredentialRefreshListener استفاده کنید و آن را برای اعتبار با استفاده از GoogleCredential.Builder.addRefreshListener (CredentialRefreshListener) تنظیم کنید.

جریان کد مجوز

از جریان کد مجوز استفاده کنید تا به کاربر نهایی اجازه دهید به برنامه شما اجازه دسترسی به داده های محافظت شده خود در APIهای Google را بدهد. پروتکل این جریان در مجوز کد Grant مشخص شده است.

این جریان با استفاده از GoogleAuthorizationCodeFlow پیاده سازی می شود. مراحل عبارتند از:

  • کاربر نهایی به برنامه شما وارد می شود. شما باید آن کاربر را با یک شناسه کاربری که برای برنامه شما منحصر به فرد است مرتبط کنید.
  • برای بررسی اینکه آیا اعتبار کاربر نهایی از قبل شناخته شده است یا خیر، AuthorizationCodeFlow.loadCredential (String) ) را بر اساس شناسه کاربر فراخوانی کنید. اگر چنین است، کار ما تمام شده است.
  • در غیر این صورت، با AuthorizationCodeFlow.newAuthorizationUrl() تماس بگیرید و مرورگر کاربر نهایی را به صفحه مجوز هدایت کنید تا به برنامه شما اجازه دسترسی به داده های محافظت شده خود را بدهد.
  • سپس سرور مجوز Google مرورگر را به آدرس URL تغییر مسیر مشخص شده توسط برنامه شما همراه با یک پارامتر جستجوی code هدایت می کند. از پارامتر code برای درخواست رمز دسترسی با استفاده از AuthorizationCodeFlow.newTokenRequest (String) استفاده کنید.
  • از AuthorizationCodeFlow.createAndStoreCredential(TokenResponse، String) ) برای ذخیره و دریافت اعتبار برای دسترسی به منابع محافظت شده استفاده کنید.

از طرف دیگر، اگر از GoogleAuthorizationCodeFlow استفاده نمی کنید، می توانید از کلاس های سطح پایین تر استفاده کنید:

وقتی پروژه خود را در Google API Console راه‌اندازی می‌کنید، بسته به جریانی که استفاده می‌کنید، اعتبارنامه‌های مختلف را انتخاب می‌کنید. برای جزئیات بیشتر، به تنظیم سناریوهای OAuth 2.0 و OAuth 2.0 مراجعه کنید. قطعه کد برای هر یک از جریان ها در زیر آمده است.

برنامه های کاربردی وب سرور

پروتکل این جریان در Using OAuth 2.0 for Web Server Applications توضیح داده شده است.

این کتابخانه کلاس های کمکی servlet را برای ساده کردن قابل توجه جریان کد مجوز برای موارد استفاده اساسی ارائه می دهد. شما فقط زیر کلاس های مشخص AbstractAuthorizationCodeServlet و AbstractAuthorizationCodeCallbackServlet (از google-oauth-client-servlet ) را ارائه کرده و آنها را به فایل web.xml خود اضافه کنید. توجه داشته باشید که هنوز باید مراقب ورود کاربر برای برنامه وب خود باشید و شناسه کاربری را استخراج کنید.

public class CalendarServletSample extends AbstractAuthorizationCodeServlet {

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws IOException {
    // do stuff
  }

  @Override
  protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
    GenericUrl url = new GenericUrl(req.getRequestURL().toString());
    url.setRawPath("/oauth2callback");
    return url.build();
  }

  @Override
  protected AuthorizationCodeFlow initializeFlow() throws IOException {
    return new GoogleAuthorizationCodeFlow.Builder(
        new NetHttpTransport(), GsonFactory.getDefaultInstance(),
        "[[ENTER YOUR CLIENT ID]]", "[[ENTER YOUR CLIENT SECRET]]",
        Collections.singleton(CalendarScopes.CALENDAR)).setDataStoreFactory(
        DATA_STORE_FACTORY).setAccessType("offline").build();
  }

  @Override
  protected String getUserId(HttpServletRequest req) throws ServletException, IOException {
    // return user ID
  }
}

public class CalendarServletCallbackSample extends AbstractAuthorizationCodeCallbackServlet {

  @Override
  protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential)
      throws ServletException, IOException {
    resp.sendRedirect("/");
  }

  @Override
  protected void onError(
      HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse)
      throws ServletException, IOException {
    // handle error
  }

  @Override
  protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
    GenericUrl url = new GenericUrl(req.getRequestURL().toString());
    url.setRawPath("/oauth2callback");
    return url.build();
  }

  @Override
  protected AuthorizationCodeFlow initializeFlow() throws IOException {
    return new GoogleAuthorizationCodeFlow.Builder(
        new NetHttpTransport(), GsonFactory.getDefaultInstance()
        "[[ENTER YOUR CLIENT ID]]", "[[ENTER YOUR CLIENT SECRET]]",
        Collections.singleton(CalendarScopes.CALENDAR)).setDataStoreFactory(
        DATA_STORE_FACTORY).setAccessType("offline").build();
  }

  @Override
  protected String getUserId(HttpServletRequest req) throws ServletException, IOException {
    // return user ID
  }
}

برنامه های کاربردی Google App Engine

جریان کد مجوز در App Engine تقریباً مشابه جریان کد مجوز سرورلت است، با این تفاوت که ما می‌توانیم از API جاوا کاربران Google App Engine استفاده کنیم. کاربر باید وارد سیستم شود تا Users Java API فعال شود. برای اطلاعات در مورد هدایت مجدد کاربران به صفحه ورود به سیستم در صورتی که قبلاً وارد سیستم نشده‌اند، به امنیت و احراز هویت (در web.xml) مراجعه کنید.

تفاوت اصلی با مورد servlet این است که شما زیر کلاس‌های مشخصی از AbstractAppEngineAuthorizationCodeServlet و AbstractAppEngineAuthorizationCodeCallbackServlet (از google-oauth-client-appengine . آنها کلاس‌های servlet انتزاعی را گسترش داده و با استفاده از متد getUserId google . -http-client-appengine ) گزینه خوبی برای حفظ اعتبار با استفاده از Google App Engine Data Store API است.

مثال گرفته شده (کمی تغییر یافته) از calendar-appengine-sample :

public class CalendarAppEngineSample extends AbstractAppEngineAuthorizationCodeServlet {

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws IOException {
    // do stuff
  }

  @Override
  protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
    return Utils.getRedirectUri(req);
  }

  @Override
  protected AuthorizationCodeFlow initializeFlow() throws IOException {
    return Utils.newFlow();
  }
}

class Utils {
  static String getRedirectUri(HttpServletRequest req) {
    GenericUrl url = new GenericUrl(req.getRequestURL().toString());
    url.setRawPath("/oauth2callback");
    return url.build();
  }

  static GoogleAuthorizationCodeFlow newFlow() throws IOException {
    return new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY,
        getClientCredential(), Collections.singleton(CalendarScopes.CALENDAR)).setDataStoreFactory(
        DATA_STORE_FACTORY).setAccessType("offline").build();
  }
}

public class OAuth2Callback extends AbstractAppEngineAuthorizationCodeCallbackServlet {

  private static final long serialVersionUID = 1L;

  @Override
  protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential)
      throws ServletException, IOException {
    resp.sendRedirect("/");
  }

  @Override
  protected void onError(
      HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse)
      throws ServletException, IOException {
    String nickname = UserServiceFactory.getUserService().getCurrentUser().getNickname();
    resp.getWriter().print("<h3>" + nickname + ", why don't you want to play with me?</h1>");
    resp.setStatus(200);
    resp.addHeader("Content-Type", "text/html");
  }

  @Override
  protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
    return Utils.getRedirectUri(req);
  }

  @Override
  protected AuthorizationCodeFlow initializeFlow() throws IOException {
    return Utils.newFlow();
  }
}

برای نمونه اضافی، storage-serviceaccount-appengine-sample را ببینید.

حساب های خدماتی

GoogleCredential از حساب‌های سرویس نیز پشتیبانی می‌کند. برخلاف اعتباری که در آن یک برنامه مشتری درخواست دسترسی به داده‌های کاربر نهایی را می‌دهد، حساب‌های خدمات دسترسی به داده‌های خود برنامه مشتری را فراهم می‌کنند. برنامه مشتری شما با استفاده از کلید خصوصی دانلود شده از Google API Console، درخواست یک نشانه دسترسی را امضا می کند.

کد مثال گرفته شده از plus-serviceaccount-cmdline-sample :

HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory jsonFactory = GsonFactory.getDefaultInstance();
...
// Build service account credential.

GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json"))
    .createScoped(Collections.singleton(PlusScopes.PLUS_ME));
// Set up global Plus instance.
plus = new Plus.Builder(httpTransport, jsonFactory, credential)
    .setApplicationName(APPLICATION_NAME).build();
...

برای نمونه اضافی، storage-serviceaccount-cmdline-sample را ببینید.

جعل هویت

همچنین می‌توانید از جریان حساب سرویس برای جعل هویت کاربر در دامنه‌ای که مالک آن هستید استفاده کنید. این بسیار شبیه به جریان حساب سرویس در بالا است، اما شما همچنین GoogleCredential.Builder.setServiceAccountUser(String) را فراخوانی می کنید.

برنامه های نصب شده

این جریان کد مجوز خط فرمان است که در استفاده از OAuth 2.0 برای برنامه های نصب شده توضیح داده شده است.

قطعه نمونه از plus-cmdline-sample :

public static void main(String[] args) {
  try {
    httpTransport = GoogleNetHttpTransport.newTrustedTransport();
    dataStoreFactory = new FileDataStoreFactory(DATA_STORE_DIR);
    // authorization
    Credential credential = authorize();
    // set up global Plus instance
    plus = new Plus.Builder(httpTransport, JSON_FACTORY, credential).setApplicationName(
        APPLICATION_NAME).build();
   // ...
}

private static Credential authorize() throws Exception {
  // load client secrets
  GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY,
      new InputStreamReader(PlusSample.class.getResourceAsStream("/client_secrets.json")));
  // set up authorization code flow
  GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
      httpTransport, JSON_FACTORY, clientSecrets,
      Collections.singleton(PlusScopes.PLUS_ME)).setDataStoreFactory(
      dataStoreFactory).build();
  // authorize
  return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
}

برنامه های کاربردی سمت مشتری

برای استفاده از جریان مشتری مبتنی بر مرورگر که در استفاده از OAuth 2.0 برای برنامه‌های سمت کلاینت توضیح داده شده است، معمولاً این مراحل را دنبال می‌کنید:

  1. کاربر نهایی در مرورگر را با استفاده از GoogleBrowserClientRequestUrl به صفحه مجوز هدایت کنید تا برنامه مرورگر شما به داده های محافظت شده کاربر نهایی دسترسی پیدا کند.
  2. از Google API Client Library برای جاوا اسکریپت برای پردازش نشانه دسترسی یافت شده در قطعه URL در URI تغییر مسیر ثبت شده در Google API Console استفاده کنید.

نمونه استفاده برای یک برنامه وب:

public void doGet(HttpServletRequest request, HttpServletResponse response)throws IOException {
  String url = new GoogleBrowserClientRequestUrl("812741506391.apps.googleusercontent.com",
      "https://oauth2.example.com/oauthcallback", Arrays.asList(
          "https://www.googleapis.com/auth/userinfo.email",
          "https://www.googleapis.com/auth/userinfo.profile")).setState("/profile").build();
  response.sendRedirect(url);
}

اندروید

@بتا

از کدام کتابخانه با اندروید استفاده شود:

اگر در حال توسعه برای Android هستید و Google API که می‌خواهید استفاده کنید در کتابخانه خدمات Google Play گنجانده شده است، از آن کتابخانه برای بهترین عملکرد و تجربه استفاده کنید. اگر Google API که می‌خواهید با Android استفاده کنید بخشی از کتابخانه خدمات Google Play نیست، می‌توانید از Google API Client Library برای جاوا استفاده کنید که از Android 4.0 (Ice Cream Sandwich) (یا بالاتر) پشتیبانی می‌کند و در اینجا توضیح داده شده است. . پشتیبانی از Android در Google API Client Library برای جاوا @Beta است.

زمینه:

با شروع Eclair (SDK 2.1)، حساب‌های کاربری در دستگاه Android با استفاده از مدیریت حساب مدیریت می‌شوند. تمام مجوزهای برنامه Android به طور مرکزی توسط SDK با استفاده از AccountManager مدیریت می شود. شما محدوده OAuth 2.0 مورد نیاز برنامه خود را مشخص می کنید، و یک رمز دسترسی برای استفاده برمی گرداند.

محدوده OAuth 2.0 از طریق پارامتر authTokenType به عنوان oauth2: به اضافه دامنه مشخص می شود. مثلا:

oauth2:https://www.googleapis.com/auth/tasks

این دسترسی خواندن/نوشتن به Google Tasks API را مشخص می کند. اگر به چندین محدوده OAuth 2.0 نیاز دارید، از یک لیست جدا شده با فاصله استفاده کنید.

برخی از API ها دارای پارامترهای authTokenType خاصی هستند که کار می کنند. به عنوان مثال، "Manage your tasks" نام مستعار برای مثال authtokenType است که در بالا نشان داده شده است.

همچنین باید کلید API را از Google API Console مشخص کنید. در غیر این صورت، توکنی که AccountManager به شما می دهد فقط سهمیه ناشناس را در اختیار شما قرار می دهد که معمولاً بسیار کم است. در مقابل، با تعیین یک کلید API، سهمیه رایگان بیشتری دریافت می‌کنید و می‌توانید به صورت اختیاری صورت‌حساب را برای استفاده بالاتر از آن تنظیم کنید.

نمونه کد کد گرفته شده از tasks-android-sample :

com.google.api.services.tasks.Tasks service;

@Override
public void onCreate(Bundle savedInstanceState) {
  credential =
      GoogleAccountCredential.usingOAuth2(this, Collections.singleton(TasksScopes.TASKS));
  SharedPreferences settings = getPreferences(Context.MODE_PRIVATE);
  credential.setSelectedAccountName(settings.getString(PREF_ACCOUNT_NAME, null));
  service =
      new com.google.api.services.tasks.Tasks.Builder(httpTransport, jsonFactory, credential)
          .setApplicationName("Google-TasksAndroidSample/1.0").build();
}

private void chooseAccount() {
  startActivityForResult(credential.newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
  switch (requestCode) {
    case REQUEST_GOOGLE_PLAY_SERVICES:
      if (resultCode == Activity.RESULT_OK) {
        haveGooglePlayServices();
      } else {
        checkGooglePlayServicesAvailable();
      }
      break;
    case REQUEST_AUTHORIZATION:
      if (resultCode == Activity.RESULT_OK) {
        AsyncLoadTasks.run(this);
      } else {
        chooseAccount();
      }
      break;
    case REQUEST_ACCOUNT_PICKER:
      if (resultCode == Activity.RESULT_OK && data != null && data.getExtras() != null) {
        String accountName = data.getExtras().getString(AccountManager.KEY_ACCOUNT_NAME);
        if (accountName != null) {
          credential.setSelectedAccountName(accountName);
          SharedPreferences settings = getPreferences(Context.MODE_PRIVATE);
          SharedPreferences.Editor editor = settings.edit();
          editor.putString(PREF_ACCOUNT_NAME, accountName);
          editor.commit();
          AsyncLoadTasks.run(this);
        }
      }
      break;
  }
}