Menggunakan OAuth 2.0 dengan Library Klien Google API untuk Java

Ringkasan

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

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

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

Untuk detailnya, lihat dokumentasi Javadoc untuk paket-paket berikut:

Konsol API Google

Sebelum dapat mengakses Google API, Anda perlu menyiapkan project di Konsol API Google untuk autentikasi dan penagihan apakah klien Anda adalah aplikasi terinstal, aplikasi seluler, server web, atau klien yang berjalan di {i>browser<i}.

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

Kredensial

GoogleCredential

GoogleCredential adalah class helper keamanan thread untuk OAuth 2.0 guna mengakses resource yang dilindungi menggunakan token akses. Sebagai contoh, jika sudah memiliki sebuah 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 API Java App Identity Google App Engine. Berbeda dengan kredensial yang digunakan aplikasi klien untuk meminta akses ke data pengguna akhir, App Identity API memberikan akses ke data aplikasi itu sendiri.

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

Kode contoh 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 kedaluwarsa 1 jam, setelah itu Anda akan mendapatkan pesan {i>error<i} jika Anda mencoba menggunakannya. GoogleCredential menangani proses "penyegaran" 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 perlu mempertahankan token akses kredensial dan/atau token refresh. Untuk mempertahankan akses kredensial dan/atau token refresh, Anda dapat memberikan implementasi DataStoreFactory Anda sendiri dengan StoredCredential; atau Anda dapat 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 Anda memiliki kredensial yang disimpan dengan cara lama, Anda dapat menggunakan metode bantuan migrateTo(AppEngineDataStoreFactory) atau migrateTo(DataStore) untuk melakukan migrasi.

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

Alur kode otorisasi

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

Alur ini diimplementasikan menggunakan GoogleAuthorizationCodeFlow. Langkah-langkahnya adalah:

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

Saat Anda menyiapkan project di Konsol API Google, Anda pilih di antara kredensial yang berbeda, tergantung pada alur yang Anda gunakan. Untuk mengetahui detail selengkapnya, lihat Menyiapkan OAuth 2.0 dan Skenario OAuth 2.0. Cuplikan kode untuk setiap alur tercantum di bawah ini.

Aplikasi server web

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

Library ini menyediakan class helper servlet untuk menyederhanakan alur kode otorisasi untuk kasus penggunaan dasar. Anda hanya menyediakan subclass konkret AbstractAuthorizationCodeServlet dan AbstractAuthorizationCodeCallbackServlet (dari google-oauth-client-servlet) dan menambahkannya ke file web.xml Anda. Perhatikan bahwa Anda masih perlu memperhatikan {i>login<i} 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 servlet alur kode otorisasi, tapi kita dapat memanfaatkan metode API Java Pengguna. Pengguna harus login agar Users Java API dapat diaktifkan; informasi tentang mengalihkan pengguna ke halaman {i>login<i} jika mereka belum {i>log in<i}, lihat Keamanan dan Autentikasi (dalam web.xml).

Perbedaan utama dari {i>casing<i} servlet adalah bahwa Anda memberikan subclass dari AbstractAppEngineAuthorizationCodeServlet dan AbstractAppEngineAuthorizationCodeCallbackServlet (dari google-oauth-client-appengine. Class ini memperluas class servlet abstrak dan mengimplementasikan metode getUserId untuk Anda menggunakan Users Java API. AppEngineDataStoreFactory (dari google-http-client-appengine) adalah opsi yang tepat untuk mempertahankan kredensial menggunakan Data Google App Engine 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. Berbeda dengan kredensial yang digunakan aplikasi klien untuk meminta akses ke data pengguna akhir, Akun Layanan memberikan akses ke data Anda sendiri. Aplikasi klien menandatangani permintaan untuk 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 contoh tambahan, lihat storage-serviceaccount-cmdline-sample.

Peniruan Identitas

Anda juga dapat menggunakan alur akun layanan untuk meniru identitas pengguna di domain yang milik Anda. Ini sangat mirip dengan alur akun layanan di atas, tetapi Anda selanjutnya panggil 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 di Menggunakan OAuth 2.0 untuk Aplikasi Sisi Klien, Anda biasanya akan mengikuti langkah-langkah berikut:

  1. Alihkan pengguna akhir di browser ke halaman otorisasi menggunakan GoogleBrowserClientRequestUrl untuk memberi aplikasi browser akses ke data yang dilindungi pengguna akhir.
  2. Menggunakan Library Klien Google API untuk JavaScript untuk memproses token akses yang ditemukan dalam 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 di Library Layanan Google Play, menggunakan library tersebut untuk mendapatkan performa dan pengalaman terbaik. Jika Google API yang Anda ingin digunakan dengan Android bukan bagian dari pustaka Layanan Google Play, Anda dapat menggunakan Library Klien Google API untuk Java, yang mendukung Android 4.0 (Ice Cream Sandwich) (atau lebih tinggi), dan dijelaskan di sini. Dukungan untuk Android di Google Library Klien API untuk Java adalah @Beta.

Latar belakang:

Mulai dari Eclair (SDK 2.1), akun pengguna dikelola di perangkat Android menggunakan {i>Account Manager<i}. Semua otorisasi aplikasi Android terpusat dikelola SDK menggunakan AccountManager. Anda menentukan cakupan OAuth 2.0 yang dibutuhkan aplikasi, dan layanan ini menampilkan akses yang akan digunakan.

Cakupan OAuth 2.0 ditentukan melalui parameter authTokenType sebagai oauth2: ditambah dengan ruang lingkup. Contoh:

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

Izin 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, "Mengelola tugas Anda" adalah alias untuk contoh authtokenType yang ditampilkan di atas.

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

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