Ringkasan
Tujuan: Dokumen ini menjelaskan fungsi OAuth 2.0 umum yang ditawarkan oleh Library Klien Google OAuth untuk Java. Anda dapat menggunakan {i>function<i} ini untuk otentikasi dan otorisasi untuk setiap layanan Internet.
Untuk petunjuk tentang penggunaan GoogleCredential
untuk melakukan otorisasi OAuth 2.0 dengan
Layanan Google, lihat
Menggunakan OAuth 2.0 dengan Library Klien Google API untuk Java.
Ringkasan: OAuth 2.0 adalah spesifikasi standar agar pengguna akhir dapat mengotorisasi klien secara aman aplikasi untuk mengakses sumber daya sisi server yang dilindungi. Selain itu, Token pemilik OAuth 2.0 spesifikasi ini menjelaskan cara mengakses sumber daya yang dilindungi tersebut dengan menggunakan token yang diberikan selama proses otorisasi pengguna akhir.
Untuk detailnya, lihat dokumentasi Javadoc untuk paket-paket berikut:
- com.google.api.client.auth.oauth2 (dari google-oauth-client)
- com.google.api.client.extensions.servlet.auth.oauth2 (from google-oauth-client-servlet)
- com.google.api.client.extensions.appengine.auth.oauth2 (from google-oauth-client-appengine)
Pendaftaran klien
Sebelum menggunakan Library Klien Google OAuth untuk Java, Anda mungkin perlu mendaftarkan aplikasi ke server otorisasi untuk menerima ID klien dan rahasia klien. (Untuk informasi umum tentang proses ini, lihat Klien Spesifikasi pendaftaran.)
Penyimpanan kredensial dan kredensial
Kredensial
adalah class helper OAuth 2.0 yang aman untuk thread untuk mengakses resource yang dilindungi menggunakan
token masing-masing. Saat menggunakan token refresh, Credential
juga memperbarui akses
saat token akses kedaluwarsa menggunakan token refresh. Misalnya, jika Anda
sudah memiliki token akses, Anda bisa membuat permintaan dengan cara berikut:
public static HttpResponse executeGet( HttpTransport transport, JsonFactory jsonFactory, String accessToken, GenericUrl url) throws IOException { Credential credential = new Credential(BearerToken.authorizationHeaderAccessMethod()).setAccessToken(accessToken); HttpRequestFactory requestFactory = transport.createRequestFactory(credential); return requestFactory.buildGetRequest(url).execute(); }
Sebagian besar aplikasi perlu mempertahankan token akses kredensial dan perbarui token untuk menghindari pengalihan di masa mendatang ke otorisasi di browser. Tujuan CredentialStore implementasi di library ini tidak digunakan lagi dan akan dihapus pada masa mendatang rilis. Alternatifnya adalah dengan menggunakan DataStoreFactory dan DataStore antarmuka dengan StoredCredential, yang disediakan oleh Library Klien HTTP Google untuk Java.
Anda dapat menggunakan salah satu implementasi berikut yang disediakan oleh library:
- JdoDataStoreFactory mempertahankan kredensial menggunakan JDO.
- AppEngineDataStoreFactory mempertahankan kredensial menggunakan Google App Engine Data Store API.
- MemoryDataStoreFactory "bertahan" kredensial dalam memori, yang hanya berguna sebagai kredensial jangka pendek penyimpanan selama masa pakai proses.
- FileDataStoreFactory mempertahankan kredensial dalam file.
Pengguna Google App Engine:
AppEngineCredentialStore tidak digunakan lagi dan akan dihapus.
Sebaiknya gunakan AppEngineDataStoreFactory dengan StoredCredential. Jika Anda memiliki kredensial yang disimpan dengan cara lama, Anda dapat menggunakan metode bantuan yang ditambahkan migrateTo(AppEngineDataStoreFactory) atau migrateTo(DataStore) untuk dimigrasikan.
Menggunakan DataStoreCredentialRefreshListener dan menyetelnya 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. Protokol untuk alur ini ditentukan dalam Spesifikasi Pemberian Kode Otorisasi.
Alur ini diimplementasikan menggunakan AuthorizationCodeFlow. Langkah-langkahnya adalah:
- Pengguna akhir login ke aplikasi Anda. Anda harus mengaitkan pengguna itu dengan ID pengguna yang unik untuk aplikasi Anda.
- Telepon AuthorizationCodeFlow.loadCredential(String), berdasarkan ID pengguna, untuk memeriksa apakah kredensial pengguna sudah diketahui. Jika ya, Anda sudah selesai.
- Jika tidak, panggil AuthorizationCodeFlow.newAuthorizationUrl() dan mengarahkan browser pengguna akhir ke halaman otorisasi tempat mereka dapat memberikan aplikasi Anda ke data yang dilindungi.
- Kemudian, browser web akan mengalihkan ke URL pengalihan dengan "kode" kueri yang kemudian dapat digunakan untuk meminta token akses menggunakan AuthorizationCodeFlow.newTokenRequest(String).
- Gunakan AuthorizationCodeFlow.createAndStoreCredential(TokenResponse, String) untuk menyimpan dan mendapatkan kredensial untuk mengakses sumber daya yang dilindungi.
Alternatifnya, jika Anda tidak menggunakan AuthorizationCodeFlow, Anda dapat menggunakan class di tingkat yang lebih rendah:
- Menggunakan DataStore.get(String) untuk memuat kredensial dari penyimpanan, berdasarkan ID pengguna.
- Menggunakan AuthorizationCodeRequestUrl untuk mengarahkan browser ke halaman otorisasi.
- Menggunakan AuthorizationCodeResponseUrl untuk memproses respons otorisasi dan menguraikan kode otorisasi.
- Menggunakan AuthorizationCodeTokenRequest untuk meminta token akses dan mungkin token pembaruan.
- Buat Credential baru dan simpan menggunakan DataStore.set(String, V).
- Akses resource yang dilindungi menggunakan Credential. Token akses yang habis masa berlakunya akan diperbarui secara otomatis menggunakan token refresh, jika berlaku. Pastikan untuk menggunakan DataStoreCredentialRefreshListener dan menyetelnya untuk kredensial menggunakan Credential.Builder.addRefreshListener(CredentialRefreshListener).
Alur kode otorisasi Servlet
Library ini menyediakan class helper servlet untuk menyederhanakan alur kode otorisasi untuk kasus penggunaan dasar. Anda hanya menyediakan subclass konkret pengguna 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.
Contoh kode:
public class ServletSample 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 AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new NetHttpTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialDataStore( StoredCredential.getDefaultDataStore( new FileDataStoreFactory(new File("datastoredir")))) .build(); } @Override protected String getUserId(HttpServletRequest req) throws ServletException, IOException { // return user ID } } public class ServletCallbackSample 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 AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new NetHttpTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialDataStore( StoredCredential.getDefaultDataStore( new FileDataStoreFactory(new File("datastoredir")))) .build(); } @Override protected String getUserId(HttpServletRequest req) throws ServletException, IOException { // return user ID } }
Alur kode otorisasi 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 diaktifkan; untuk informasi tentang cara mengalihkan pengguna ke halaman login jika mereka belum sudah masuk, 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). Library ini memperluas class servlet abstrak dan mengimplementasikan metode getUserId
untuk Anda menggunakan Users Java API. AppEngineDataStoreFactory (dari Google HTTP Client Library untuk Java adalah opsi yang tepat untuk mempertahankan kredensial menggunakan Google App Engine Data Store API.
Contoh kode:
public class AppEngineSample extends AbstractAppEngineAuthorizationCodeServlet { @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 AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new UrlFetchTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialStore( StoredCredential.getDefaultDataStore(AppEngineDataStoreFactory.getDefaultInstance())) .build(); } } public class AppEngineCallbackSample extends AbstractAppEngineAuthorizationCodeCallbackServlet { @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 AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new UrlFetchTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialStore( StoredCredential.getDefaultDataStore(AppEngineDataStoreFactory.getDefaultInstance())) .build(); } }
Alur kode otorisasi command line
Kode contoh sederhana yang diambil dari dailymotion-cmdline-sample:
/** Authorizes the installed application to access user's protected data. */ private static Credential authorize() throws Exception { OAuth2ClientCredentials.errorIfNotSpecified(); // set up authorization code flow AuthorizationCodeFlow flow = new AuthorizationCodeFlow.Builder(BearerToken .authorizationHeaderAccessMethod(), HTTP_TRANSPORT, JSON_FACTORY, new GenericUrl(TOKEN_SERVER_URL), new ClientParametersAuthentication( OAuth2ClientCredentials.API_KEY, OAuth2ClientCredentials.API_SECRET), OAuth2ClientCredentials.API_KEY, AUTHORIZATION_SERVER_URL).setScopes(Arrays.asList(SCOPE)) .setDataStoreFactory(DATA_STORE_FACTORY).build(); // authorize LocalServerReceiver receiver = new LocalServerReceiver.Builder().setHost( OAuth2ClientCredentials.DOMAIN).setPort(OAuth2ClientCredentials.PORT).build(); return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user"); } private static void run(HttpRequestFactory requestFactory) throws IOException { DailyMotionUrl url = new DailyMotionUrl("https://api.dailymotion.com/videos/favorites"); url.setFields("id,tags,title,url"); HttpRequest request = requestFactory.buildGetRequest(url); VideoFeed videoFeed = request.execute().parseAs(VideoFeed.class); ... } public static void main(String[] args) { ... DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR); final Credential credential = authorize(); HttpRequestFactory requestFactory = HTTP_TRANSPORT.createRequestFactory(new HttpRequestInitializer() { @Override public void initialize(HttpRequest request) throws IOException { credential.initialize(request); request.setParser(new JsonObjectParser(JSON_FACTORY)); } }); run(requestFactory); ... }
Alur klien berbasis browser
Ini adalah langkah umum dari alur klien berbasis browser yang ditetapkan dalam Spesifikasi Pemberian Implisit:
- Dengan menggunakan BrowserClientRequestUrl, alihkan browser pengguna akhir ke laman otorisasi di mana pengguna akhir dapat memberi aplikasi Anda akses ke data yang dilindungi.
- Menggunakan aplikasi JavaScript untuk memproses token akses yang ditemukan di URL di URI pengalihan yang terdaftar di server otorisasi.
Contoh penggunaan untuk aplikasi web:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { String url = new BrowserClientRequestUrl( "https://server.example.com/authorize", "s6BhdRkqt3").setState("xyz") .setRedirectUri("https://client.example.com/cb").build(); response.sendRedirect(url); }
Mendeteksi token akses yang sudah tidak berlaku
Menurut spesifikasi pembawa OAuth 2.0,
saat server dipanggil untuk mengakses sumber daya yang dilindungi dengan akses yang sudah tidak berlaku
server biasanya merespons dengan kode status HTTP 401 Unauthorized
seperti berikut:
HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer realm="example", error="invalid_token", error_description="The access token expired"
Namun, tampaknya ada banyak fleksibilitas dalam spesifikasi ini. Sebagai detailnya, periksa dokumentasi penyedia OAuth 2.0.
Pendekatan alternatifnya adalah memeriksa parameter expires_in
dalam
respons token akses.
Token ini menentukan masa pakai token akses yang diberikan dalam hitungan detik,
biasanya satu jam. Namun, token akses mungkin tidak
benar-benar kedaluwarsa pada akhirnya
selama periode tersebut, dan server mungkin terus
mengizinkan akses. Itu sebabnya kita
biasanya merekomendasikan menunggu kode status 401 Unauthorized
, bukan
dengan asumsi token telah habis masa berlakunya berdasarkan waktu yang telah berlalu. Atau, Anda dapat
mencoba menyegarkan token akses sesaat
sebelum masa berlakunya habis, dan jika server token
tidak tersedia, terus gunakan token akses hingga Anda menerima 401
. Ini
adalah strategi yang digunakan secara default
Kredensial.
Opsi lain adalah mengambil token akses baru sebelum setiap permintaan, tetapi itu setiap saat memerlukan permintaan HTTP tambahan ke server token, jadi kemungkinan pilihan yang buruk dalam hal kecepatan dan penggunaan jaringan. Idealnya, simpan token akses dalam penyimpanan yang aman dan persisten untuk meminimalkan permintaan akses baru dari aplikasi token kata. (Tetapi untuk aplikasi yang terpasang, penyimpanan yang aman adalah masalah yang sulit.)
Perhatikan bahwa token akses bisa menjadi tidak valid karena alasan selain masa berlaku, misalnya, jika pengguna telah mencabut token secara eksplisit, jadi pastikan kode penanganan {i>error<i} yang andal. Setelah Anda mendeteksi bahwa token tidak lagi valid, misalnya jika telah kedaluwarsa atau telah dicabut, Anda harus menghapus akses tersebut token akses dari penyimpanan Anda. Di Android, misalnya, Anda harus memanggil AccountManager.invalidateAuthToken.