Genel Bakış
Amaç: Bu dokümanda, Java için Google OAuth İstemci Kitaplığı tarafından sunulan genel OAuth 2.0 işlevleri açıklanmaktadır. Bu işlevleri, herhangi bir internet hizmeti için kimlik doğrulama ve yetkilendirme amacıyla kullanabilirsiniz.
Google hizmetlerinde OAuth 2.0 yetkilendirmesi yapmak için GoogleCredential
kullanmayla ilgili talimatlar için Java için Google API İstemci Kitaplığı ile OAuth 2.0'ı kullanma başlıklı makaleyi inceleyin.
Özet: OAuth 2.0, son kullanıcıların istemci uygulamasının korunan sunucu tarafı kaynaklarına güvenli bir şekilde yetkilendirilmesine olanak tanıyan standart bir spesifikasyondur. Ayrıca, OAuth 2.0 taşıyıcı jetonu spesifikasyonunda, son kullanıcı yetkilendirme işlemi sırasında verilen bir erişim jetonu kullanılarak bu korumalı kaynaklara nasıl erişileceği açıklanmaktadır.
Ayrıntılar için aşağıdaki paketlerin Javadoc dokümanlarına bakın:
- com.google.api.client.auth.oauth2 (google-oauth-clientden)
- com.google.api.client.extensions.servlet.auth.oauth2 (google-oauth-client-servlet'ten)
- com.google.api.client.extensions.appengine.auth.oauth2 (from google-oauth-client-appengine)
Müşteri kaydı
Java için Google OAuth İstemci Kitaplığı'nı kullanmadan önce muhtemelen, istemci kimliği ve istemci gizli anahtarı almak için uygulamanızı bir yetkilendirme sunucusuna kaydetmeniz gerekir. (Bu işlemle ilgili genel bilgiler için İstemci Kaydı spesifikasyonuna bakın.)
Kimlik bilgisi ve kimlik bilgisi deposu
Kimlik bilgisi, erişim jetonu kullanarak korumalı kaynaklara erişmek için iş parçacığı için güvenli bir OAuth 2.0 yardımcı sınıfıdır. Yenileme jetonu kullanıldığında Credential
, erişim jetonunun süresi dolduğunda yenileme jetonunu kullanarak erişim jetonunu da yeniler. Örneğin, halihazırda bir erişim jetonunuz varsa aşağıdaki şekilde istek gönderebilirsiniz:
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(); }
Çoğu uygulamanın, tarayıcıda yetkilendirme sayfasına yönlendirilmeyi önlemek için kimlik bilgisinin erişim jetonunu ve yenileme jetonunu koruması gerekir. Bu kitaplıktaki CredentialStore uygulamasının desteği sonlandırılmıştır ve gelecekteki sürümlerde kaldırılacaktır. Alternatif olarak, Java için Google HTTP İstemci Kitaplığı tarafından sağlanan DataStoreFactory ve DataStore arayüzlerini StoredCredential ile kullanabilirsiniz.
Kitaplık tarafından sağlanan aşağıdaki uygulamalardan birini kullanabilirsiniz:
- JdoDataStoreFactory, kimlik bilgisini JDO kullanarak tutar.
- AppEngineDataStoreFactory, Google App Engine Data Store API'yi kullanarak kimlik bilgisini tutar.
- MemoryDataStoreFactory, kimlik bilgisini bellekte "korur". Bu, yalnızca işlemin yaşam boyu kısa süreli depolama alanı olarak kullanışlıdır.
- FileDataStoreFactory, kimlik bilgisini bir dosyada tutar.
Google App Engine kullanıcıları:
AppEngineCredentialStore desteği sonlandırıldı ve kaldırılıyor.
StoredCredential ile AppEngineDataStoreFactory kullanmanızı öneririz. Eski yöntemlerle depolanan kimlik bilgileriniz varsa taşıma işlemi için migrationTo(AppEngineDataStoreFactory) veya MigrateTo(DataStore) gibi yardımcı yöntemleri kullanabilirsiniz.
DataStoreCredentialRefreshListener'ı kullanın ve GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener) yöntemini kullanarak kimlik bilgisi için ayarlayın.
Yetkilendirme kodu akışı
Son kullanıcının, uygulamanıza korumalı verilerine erişim izni vermesine izin vermek için yetkilendirme kodu akışını kullanın. Bu akışa ilişkin protokol, Yetkilendirme Kodu Verme spesifikasyonunda belirtilmiştir.
Bu akış, AuthorizationCodeFlow kullanılarak uygulanır. Adımlar aşağıdaki gibidr:
- Son kullanıcı uygulamanıza giriş yapar. Bu kullanıcıyı, uygulamanız için benzersiz bir kullanıcı kimliğiyle ilişkilendirmeniz gerekir.
- Kullanıcı kimliğine göre AuthorizationCodeFlow.loadCredential(String)'ı çağırarak kullanıcının kimlik bilgilerinin önceden bilinip bilinmediğini kontrol edin. Bu durumda, işlem tamamlanmıştır.
- Aksi takdirde AuthorizationCodeFlow.newAuthorizationUrl() işlevini çağırın ve son kullanıcının tarayıcısını, uygulamanıza korumalı verilerine erişim izni verebilecekleri bir yetkilendirme sayfasına yönlendirin.
- Ardından web tarayıcısı, "code" sorgu parametresini içeren yönlendirme URL'sine yönlendirir. Bu parametre, AuthorizationCodeFlow.newTokenRequest(String) kullanılarak erişim jetonu istemek için kullanılabilir.
- Korunan kaynaklara erişmek için kimlik bilgisi depolamak ve almak üzere AuthorizationCodeFlow.createAndStoreCredential(TokenResponse, String) işlevini kullanın.
Alternatif olarak, AuthorizationCodeFlow kullanmıyorsanız daha alt düzey sınıfları da kullanabilirsiniz:
- Kullanıcı kimliğine göre kimlik bilgisini mağazadan yüklemek için DataStore.get(String) işlevini kullanın.
- Tarayıcıyı yetkilendirme sayfasına yönlendirmek için AuthorizationCodeRequestUrl parametresini kullanın.
- Yetkilendirme yanıtını işlemek ve yetkilendirme kodunu ayrıştırmak için AuthorizationCodeResponseUrl parametresini kullanın.
- Erişim jetonu ve muhtemelen yenileme jetonu istemek için AuthorizationCodeTokenRequest'i kullanın.
- Yeni bir Kimlik Bilgisi oluşturun ve DataStore.set(String, V) yöntemini kullanarak saklayın.
- Kimlik Bilgisi'ni kullanarak korunan kaynaklara erişin. Süresi dolmuş erişim jetonları, geçerliyse yenileme jetonu kullanılarak otomatik olarak yenilenir. DataStoreCredentialRefreshListener sınıfını kullandığınızdan ve Credential.Builder.addRefreshListener(CredentialRefreshListener) yöntemini kullanarak kimlik bilgisi için ayarladığınızdan emin olun.
Servlet yetkilendirme kodu akışı
Bu kitaplık, temel kullanım alanları için yetkilendirme kodu akışını önemli ölçüde basitleştirmek amacıyla servlet yardımcı sınıfları sağlar. AbstractAuthorizationCodeServlet ve AbstractAuthorizationCodeCallbackServlet (google-oauth-client-servlet'ten) sınıflarının somut alt sınıflarını sağlayıp web.xml dosyanıza eklemeniz yeterlidir. Web uygulamanız için kullanıcı girişini halletmeniz ve bir kullanıcı kimliği çıkarmanız gerektiğini unutmayın.
Örnek kod:
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 } }
Google App Engine yetkilendirme kodu akışı
App Engine'deki yetkilendirme kodu akışı, Google App Engine'in Users Java API'sinden yararlanabilmemiz dışında servlet yetkilendirme kodu akışıyla neredeyse aynıdır. Users Java API'nin etkinleştirilmesi için kullanıcının giriş yapmış olması gerekir. Henüz giriş yapmamış kullanıcıları bir giriş sayfasına yönlendirme hakkında daha fazla bilgi edinmek için Güvenlik ve Kimlik Doğrulama sayfasına (web.xml'de) göz atın.
Servlet örneğiyle arasındaki temel fark, AbstractAppEngineAuthorizationCodeServlet ve AbstractAppEngineAuthorizationCodeCallbackServlet (google-oauth-client-appengine'dan) sınıflarının somut alt sınıflarını sağlamanızdır. Soyut servlet sınıflarını genişletir ve Users Java API'sini kullanarak getUserId
yöntemini sizin için uygular. AppEngineDataStoreFactory (Java için Google HTTP İstemci Kitaplığı'ndan), Google App Engine Data Store API'yi kullanarak kimlik bilgisini kalıcı hale getirmek için iyi bir seçenektir.
Örnek kod:
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(); } }
Komut satırı yetkilendirme kod akışı
dailymotion-cmdline-sample'dan alınmış basitleştirilmiş örnek kod:
/** 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); ... }
Tarayıcı tabanlı istemci akışı
Dolaysız Atama spesifikasyonunda belirtilen tarayıcı tabanlı istemci akışının tipik adımları şunlardır:
- BrowserClientRequestUrl'yi kullanarak son kullanıcının tarayıcısını, son kullanıcının uygulamanızın korunan verilerine erişmesine izin verebileceği yetkilendirme sayfasına yönlendirin.
- Yetkilendirme sunucusuna kayıtlı yönlendirme URI'sindeki URL parçasında bulunan erişim jetonunu işlemek için bir JavaScript uygulaması kullanın.
Web uygulaması için örnek kullanım:
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); }
Süresi dolmuş erişim jetonlarını algılama
OAuth 2.0 taşıyıcı spesifikasyonuna göre, süresi dolmuş bir erişim jetonuyla korunan bir kaynağa erişmek için sunucu çağrıldığında sunucu genellikle aşağıdaki gibi bir HTTP 401 Unauthorized
durum koduyla yanıt verir:
HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer realm="example", error="invalid_token", error_description="The access token expired"
Ancak spesifikasyonda büyük bir esneklik olduğu görülüyor. Ayrıntılar için OAuth 2.0 sağlayıcısının dokümanlarını inceleyin.
Alternatif bir yaklaşım da erişim jetonu yanıtındaki expires_in
parametresini kontrol etmektir.
Bu, verilen erişim jetonunun saniye cinsinden geçerlilik süresini belirtir. Bu süre genellikle bir saattir. Ancak erişim jetonunun süresi bu sürenin sonunda sona ermeyebilir ve sunucu, erişime izin vermeye devam edebilir. Bu nedenle, genellikle geçen süreye göre jetonun süresinin dolduğunu varsaymak yerine 401 Unauthorized
durum kodunu beklemenizi öneririz. Alternatif olarak, erişim jetonunu süresi dolmadan kısa bir süre önce yenilemeyi deneyebilir ve jeton sunucusu kullanılamıyorsa 401
alana kadar erişim jetonunu kullanmaya devam edebilirsiniz. Bu, Kimlik Bilgileri'nde varsayılan olarak kullanılan stratejidir.
Diğer bir seçenek de her istekten önce yeni bir erişim jetonu almaktır. Ancak bu, her seferinde jeton sunucusuna ek bir HTTP isteği gönderilmesini gerektirir. Bu nedenle, hız ve ağ kullanımı açısından muhtemelen kötü bir seçimdir. İdeal olarak, uygulamaların yeni erişim jetonu isteklerini en aza indirmek için erişim jetonunu güvenli ve kalıcı depolama alanında depolayın. (Ancak yüklü uygulamalar için güvenli depolama, zor bir sorundur.)
Erişim jetonunun geçerlilik bitişi dışındaki nedenlerle de geçersiz olabileceğini unutmayın (örneğin, kullanıcı jetonu açıkça iptal ettiyse). Bu nedenle, hata işleme kodunuzun sağlam olduğundan emin olun. Bir jetonun artık geçerli olmadığını tespit ettiğinizde (ör. süresi dolmuş veya iptal edilmişse) erişim jetonunu depolama alanınızdan kaldırmanız gerekir. Örneğin, Android'de AccountManager.invalidateAuthToken'ı çağırmanız gerekir.