OAuth 2.0 ve Java için Google OAuth İstemci Kitaplığı

Genel Bakış

Amaç: Bu belgede, Google Cloud Search tarafından sunulan genel OAuth 2.0 işlevleri Google OAuth İstemci Kitaplığı. Bu işlevleri şunlar için kullanabilirsiniz: İnternet hizmetleri için kimlik doğrulaması ve yetkilendirme.

GoogleCredential kullanarak OAuth 2.0 yetkilendirmesi gerçekleştirmek için talimatlar: Google hizmetleri (bkz. Java için Google API istemci kitaplığı ile OAuth 2.0'ı kullanma.

Özet: OAuth 2.0, Google tarafından Son kullanıcıların bir istemciyi güvenli bir şekilde yetkilendirmesine izin veren standart spesifikasyon korumasındaki sunucu tarafı kaynaklara erişmek için kullanır. Ayrıca, OAuth 2.0 hamiline ait jeton spesifikasyonu, bir erişim kodu kullanarak bu korunan kaynaklara nasıl erişileceğini son kullanıcı yetkilendirme işlemi sırasında verildi.

Ayrıntılı bilgi için aşağıdaki paketlere ilişkin Javadoc dokümanlarına bakın:

Müşteri kaydı

Java için Google OAuth İstemci Kitaplığı'nı kullanmadan önce büyük olasılıkla almak için uygulamanızı bir yetkilendirme sunucusuna kaydetme ve istemci sırrı: (Bu işlem hakkında genel bilgi için bkz. Müşteri Kayıt spesifikasyonu.)

Kimlik bilgisi ve kimlik bilgisi deposu

Kimlik bilgisi korumalı kaynaklara erişmek için kullanılan iş parçacığı açısından güvenli bir OAuth 2.0 yardımcı sınıfıdır. erişim jetonu. Yenileme jetonu kullanılırken Credential, erişimi de yeniler yenileme jetonunu kullanarak erişim jetonunun süresi dolmadan yeni bir jeton oluşturun. Örneğin, zaten bir erişim jetonunuz varsa aşağıdaki şekilde istekte bulunabilirsiniz:

  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, kimlik bilgilerinin erişim jetonunu sürdürmesi ve yetkilendirmeye gelecekte yeniden yönlendirme yapmamak için jetonu yenileme sayfasını ziyaret ederek kontrol edebilirsiniz. İlgili içeriği oluşturmak için kullanılan CredentialStore bu kitaplıktaki uygulama desteği sonlandırıldı ve bu uygulama ileride kaldırılacaktır yayınlar. Alternatif olarak, DataStoreFactory ve DataStore şununla arayüz: StoredCredential, tarafından sağlanan Java için Google HTTP istemci kitaplığı.

Kitaplık tarafından sağlanan aşağıdaki uygulamalardan birini kullanabilirsiniz:

Google App Engine kullanıcıları:

AppEngineCredentialStore kullanımdan kaldırıldı ve artık kaldırılıyor.

Optimum kampanya performansı için AppEngineDataStoreFactory StoredCredential ile kullanın. Eski yöntemle depolanmış kimlik bilgileriniz varsa ek yardımcı yöntemleri kullanabilirsiniz migrateTo(AppEngineDataStoreFactory) veya migrateTo(DataStore) tıklayın.

DataStoreCredentialRefreshListener'ı kullanın. ve bunu kullanarak kimlik bilgisi için GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener).

Yetkilendirme kodu akışı

Son kullanıcının uygulamanıza izin vermesi için yetkilendirme kodu akışını kullanın tarafından korunuyor. Bu akışa ilişkin protokol Yetkilendirme Kodu İzni spesifikasyonu.

Bu akış, AuthorizationCodeFlow. Adımlar aşağıdaki gibidr:

Alternatif olarak AuthorizationCodeFlow, daha alt düzey sınıfları kullanabilirsiniz:

Servlet yetkilendirme kodu akışı

Bu kitaplık, performansı önemli ölçüde basitleştirmek için servlet yardımcı sınıfları yetkilendirme kodu akışını izleyin. Yalnızca somut alt sınıflar sağlarsınız / AbstractAuthorizationCodeServlet ve AbstractAuthorizationCodeCallbackServlet (google-oauth-client-servlet'ten) ve web.xml dosyanıza ekleyin. Kullanıcıyla yine de ilgilenmeniz gerektiğini unutmayın. web uygulamanız için giriş yapın ve bir kullanıcı kimliği çıkarı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ışı servlet ile neredeyse aynıdır yetkilendirme kodu akışını, yalnızca Google App Engine'in Kullanıcılar Java API'si. Users Java API'nin etkinleştirilmesi için kullanıcının giriş yapmış olması gerekir; şunun için: halihazırda değilse, kullanıcıları bir giriş sayfasına yönlendirmeyle ilgili bilgiler giriş yaptınız, bkz. Güvenlik ve Kimlik Doğrulama (web.xml dosyasında).

servlet durumundan ilk fark, somut bir ortam sağlayarak alt sınıfları AbstractAppEngineAuthorizationCodeServlet ve AbstractAppEngineAuthorizationCodeCallbackServlet (google-oauth-client-appengine'den). Soyut servlet sınıflarını genişletir ve Users Java API'yi 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ışı

Şuradan alınan basitleştirilmiş örnek kod: 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);
  ...
}

Tarayıcı tabanlı istemci akışı

Bunlar, Dolaylı Hibe spesifikasyonu:

  • BrowserClientRequestUrl'ni kullanarak son kullanıcının tarayıcısını, kullanıcının uygulamanızın korunan verilerine erişmesine izin verebilirsiniz.
  • URL'de bulunan erişim jetonunu işlemek için bir JavaScript uygulaması kullanın parçası, yetkilendirme sunucusuna kayıtlı yönlendirme URI'sındaki

Bir 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ş bir erişim jetonunu algılama

OAuth 2.0 taşıyıcı spesifikasyonuna göre, sunucu, erişimi süresi dolmuş korumalı bir kaynağa erişmek için çağrıldığında belirtilmezse sunucu genellikle bir HTTP 401 Unauthorized durum koduyla Örneğin:

   HTTP/1.1 401 Unauthorized
   WWW-Authenticate: Bearer realm="example",
                     error="invalid_token",
                     error_description="The access token expired"

Ancak spesifikasyonda oldukça fazla esneklik olduğu görülüyor. Örneğin, OAuth 2.0 sağlayıcısının dokümanlarına bakın.

Alternatif bir yaklaşım, expires_in parametresini erişim jetonu yanıtı. Bu değer, verilen erişim jetonunun saniye cinsinden ömrünü belirtir. genellikle bir saat sürer. Ancak erişim jetonunun süresi sonunda dolmamış olabilir çok daha fazla zaman alabilir. Sunucu, erişim izni vermeye devam edebilir. İşte bu yüzden genellikle bunun yerine bir 401 Unauthorized durum kodu beklemenizi önerir. geçen süreye göre jetonun süresinin dolduğu varsayılmıştır. Alternatif olarak süresi dolmadan kısa süre önce bir erişim jetonunu yenilemeyi deneyin ve jeton sunucusu kullanılamıyor, 401 alana kadar erişim jetonunu kullanmaya devam edin. Bu proje yönetiminde varsayılan olarak Kimlik bilgisi.

Diğer bir seçenek de her istekten önce yeni bir erişim jetonu almaktır. her seferinde jeton sunucusuna fazladan bir HTTP isteği gerektirir. Bu nedenle hız ve ağ kullanımı açısından kötü bir seçim. İdeal olarak, erişim jetonunu uygulamaların yeni erişim isteklerini en aza indirmek için güvenli, kalıcı depolama alanında jeton. (Ancak yüklü uygulamalar için güvenli depolama, zor bir sorundur.)

Erişim jetonunun, geçerlilik süresinin dolmasından farklı nedenlerle geçersiz hale gelebileceğini Örneğin, kullanıcı jetonu açık bir şekilde iptal ettiyse nasıl güvenilir bir kod olduğuna dikkat çekmek istiyorum. Bir jetonun artık kullanılamadığını tespit ettiğinizde geçerlidir. Örneğin, süresi dolmuş veya iptal edilmişse erişim iznini kaldırmalısınız. jeton. Örneğin, Android'de AccountManager.invalidateAuthToken.