Menggunakan OAuth 2.0 dengan Library Klien Google API untuk Java

Ringkasan

Tujuan: Dokumen ini menjelaskan cara menggunakan kelas utilitas GoogleCredential untuk melakukan otorisasi OAuth 2.0 dengan layanan Google. Untuk informasi tentang fungsi OAuth 2.0 umum yang kami sediakan, lihat OAuth 2.0 dan Library Klien Google OAuth untuk Java.

Ringkasan: Untuk mengakses data terlindungi yang disimpan di layanan Google, gunakan OAuth 2.0 untuk otorisasi. Google API mendukung alur OAuth 2.0 untuk berbagai jenis aplikasi klien. Dalam semua alur ini, aplikasi klien meminta token akses yang hanya dikaitkan dengan aplikasi klien Anda dan pemilik data terlindungi yang sedang diakses. Token akses juga dikaitkan dengan cakupan terbatas yang menentukan jenis data yang dapat diakses aplikasi klien Anda (misalnya, "Mengelola tugas Anda"). Tujuan penting OAuth 2.0 adalah untuk memberikan akses yang aman dan nyaman ke data yang dilindungi, sekaligus meminimalkan potensi dampaknya jika token akses dicuri.

Paket OAuth 2.0 di Library Klien Google API untuk Java dibuat berdasarkan Library Klien Google OAuth 2.0 untuk Java tujuan umum.

Untuk detailnya, lihat dokumentasi Javadoc untuk paket berikut:

Konsol API Google

Sebelum dapat mengakses Google API, Anda perlu menyiapkan project di Konsol API Google untuk tujuan autentikasi dan penagihan, baik klien Anda adalah aplikasi yang diinstal, aplikasi seluler, server web, atau klien yang berjalan di browser.

Untuk petunjuk cara menyiapkan kredensial Anda dengan benar, lihat Bantuan Konsol API.

Kredensial

GoogleCredential

GoogleCredential adalah class helper yang aman untuk thread bagi OAuth 2.0 guna mengakses resource yang dilindungi menggunakan token akses. Misalnya, jika sudah memiliki token akses, Anda dapat membuat permintaan dengan cara berikut:

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

Identitas Google App Engine

Kredensial alternatif ini didasarkan pada Google App Engine App Identity Java API. Tidak seperti kredensial yang mana aplikasi klien meminta akses ke data pengguna akhir, App Identity API menyediakan akses ke data aplikasi klien itu sendiri.

Gunakan AppIdentityCredential (dari google-api-client-appengine). Kredensial ini jauh lebih sederhana karena Google App Engine menangani semua detail. Anda hanya menentukan cakupan OAuth 2.0 yang diperlukan.

Contoh kode yang diambil dari 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();
}

Penyimpanan data

Token akses biasanya memiliki tanggal habis masa berlaku 1 jam. Setelah itu, Anda akan menerima pesan error jika mencoba menggunakannya. GoogleCredential akan otomatis "memperbarui" token, yang berarti mendapatkan token akses baru. Hal ini dilakukan melalui token refresh yang berumur panjang, yang biasanya diterima bersama dengan token akses jika Anda menggunakan parameter access_type=offline selama alur kode otorisasi (lihat GoogleAuthorizationCodeFlow.Builder.setAccessType(String)).

Sebagian besar aplikasi harus mempertahankan token akses dan/atau token refresh kredensial. Untuk mempertahankan akses kredensial dan/atau token refresh, Anda dapat memberikan implementasi DataStoreFactory Anda sendiri dengan StoredCredential; atau menggunakan salah satu implementasi berikut yang disediakan oleh library:

Pengguna AppEngine: AppEngineCredentialStore tidak digunakan lagi dan akan segera dihapus. Sebaiknya gunakan AppEngineDataStoreFactory dengan StoredCredential. Jika memiliki kredensial yang disimpan dengan cara lama, Anda dapat menggunakan metode helper tambahan migrationTo(AppEngineDataStoreFactory) atau MigrateTo(DataStore) untuk melakukan migrasi.

Anda dapat menggunakan DataStoreCredentialRefreshListener dan menetapkannya untuk kredensial menggunakan GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener)).

Alur kode otorisasi

Gunakan alur kode otorisasi untuk memungkinkan pengguna akhir memberi aplikasi Anda akses ke data mereka yang dilindungi di Google API. Protokol untuk alur ini ditentukan dalam Pemberian Kode Otorisasi.

Alur ini diterapkan menggunakan GoogleAuthorizationCodeFlow. Langkah-langkahnya adalah:

Atau, jika tidak menggunakan GoogleAuthorizationCodeFlow, Anda dapat menggunakan class tingkat lebih rendah:

Saat menyiapkan project di Konsol API Google, Anda dapat memilih di antara kredensial yang berbeda, bergantung pada alur yang Anda gunakan. Untuk mengetahui detail selengkapnya, lihat Menyiapkan OAuth 2.0 dan Skenario OAuth 2.0. Berikut adalah cuplikan kode untuk setiap alur.

Aplikasi server web

Protokol untuk alur ini dijelaskan dalam Menggunakan OAuth 2.0 untuk Aplikasi Server Web.

Library ini menyediakan class helper servlet untuk menyederhanakan alur kode otorisasi secara signifikan untuk kasus penggunaan dasar. Anda cukup memberikan subclass konkret dari AbstractAuthorizationCodeServlet dan AbstractAuthorizationCodeCallbackServlet (dari google-oauth-client-servlet) dan menambahkannya ke file web.xml. Perlu diperhatikan bahwa Anda masih harus mengurus login pengguna untuk aplikasi web Anda dan mengekstrak ID pengguna.

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
  }
}

Aplikasi Google App Engine

Alur kode otorisasi di App Engine hampir identik dengan alur kode otorisasi servlet. Hanya saja, kita dapat memanfaatkan Users Java API dari Google App Engine. Pengguna harus login agar Pengguna Java API dapat diaktifkan. Untuk mengetahui informasi tentang cara mengalihkan pengguna ke halaman login jika mereka belum login, lihat Keamanan dan Autentikasi (dalam web.xml).

Perbedaan utama dengan kasus servlet adalah Anda menyediakan subclass konkret dari AbstractAppEngineAuthorizationCodeServlet dan AbstractAppEngineAuthorizationCodeCallbackServlet (dari google-oauth-client-appengine. Library ini memperluas class servlet abstrak dan menerapkan metode getUserId untuk Anda menggunakan Users Java API. AppEngineDataStoreFactory (dari google-http-client-appengine) adalah opsi yang baik untuk mempertahankan kredensial menggunakan Google App Engine Data Store API.

Contoh yang diambil (sedikit diubah) dari 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();
  }
}

Untuk contoh tambahan, lihat storage-serviceaccount-appengine-sample.

Akun layanan

GoogleCredential juga mendukung akun layanan. Tidak seperti kredensial yang digunakan aplikasi klien untuk meminta akses ke data pengguna akhir, Akun Layanan memberikan akses ke data milik aplikasi klien. Aplikasi klien Anda menandatangani permintaan token akses menggunakan kunci pribadi yang didownload dari Konsol API Google.

Contoh kode yang diambil dari 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();
...

Untuk sampel tambahan, lihat storage-serviceaccount-cmdline-sample.

Peniruan Identitas

Anda juga dapat menggunakan alur akun layanan untuk meniru identitas pengguna dalam domain milik Anda. Ini sangat mirip dengan alur akun layanan di atas, tetapi Anda juga memanggil GoogleCredential.Builder.setServiceAccountUser(String).

Aplikasi terpasang

Ini adalah alur kode otorisasi command line yang dijelaskan di Menggunakan OAuth 2.0 untuk Aplikasi yang Diinstal.

Contoh cuplikan dari 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");
}

Aplikasi sisi klien

Untuk menggunakan alur klien berbasis browser yang dijelaskan dalam Menggunakan OAuth 2.0 untuk Aplikasi Sisi Klien, Anda biasanya perlu mengikuti langkah-langkah berikut:

  1. Alihkan pengguna akhir di browser ke halaman otorisasi menggunakan GoogleBrowserClientRequestUrl untuk memberi aplikasi browser Anda akses ke data yang dilindungi oleh pengguna akhir.
  2. Gunakan Library Klien Google API untuk JavaScript untuk memproses token akses yang ada di fragmen URL di URI pengalihan yang terdaftar di Konsol API Google.

Contoh penggunaan untuk aplikasi web:

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

@Beta

Library yang akan digunakan dengan Android:

Jika Anda mengembangkan aplikasi untuk Android dan Google API yang ingin Anda gunakan disertakan dalam library Layanan Google Play, gunakan library tersebut untuk mendapatkan performa dan pengalaman terbaik. Jika Google API yang ingin Anda gunakan dengan Android bukan bagian dari library Layanan Google Play, Anda dapat menggunakan Library Klien Google API untuk Java, yang mendukung Android 4.0 (Ice Cream Sandwich) (atau yang lebih tinggi), dan yang dijelaskan di sini. Dukungan untuk Android dalam Library Klien Google API untuk Java adalah @Beta.

Latar belakang:

Mulai dari Eclair (SDK 2.1), akun pengguna dikelola di perangkat Android menggunakan Account Manager. Semua otorisasi aplikasi Android dikelola secara terpusat oleh SDK menggunakan AccountManager. Anda menentukan cakupan OAuth 2.0 yang dibutuhkan aplikasi Anda, dan menampilkan token akses yang akan digunakan.

Cakupan OAuth 2.0 ditentukan melalui parameter authTokenType sebagai oauth2: ditambah cakupan. Contoh:

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

Atribut ini menentukan akses baca/tulis ke Google Tasks API. Jika Anda memerlukan beberapa cakupan OAuth 2.0, gunakan daftar yang dipisahkan spasi.

Beberapa API memiliki parameter authTokenType khusus yang juga berfungsi. Misalnya, "Kelola tugas Anda" adalah alias untuk contoh authtokenType yang ditampilkan di atas.

Anda juga harus menentukan kunci API dari Konsol API Google. Jika tidak, token yang diberikan AccountManager kepada Anda hanya akan memberi Anda kuota anonim, yang biasanya sangat rendah. Sebaliknya, dengan menentukan kunci API, Anda akan menerima kuota gratis yang lebih tinggi dan secara opsional dapat menyiapkan penagihan untuk penggunaan di atasnya.

Contoh cuplikan kode yang diambil dari 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;
  }
}