Java용 Google API 클라이언트 라이브러리에서 OAuth 2.0 사용

개요

목적: 이 문서에서는 GoogleCredential 유틸리티 클래스를 사용하여 Google 서비스에서 OAuth 2.0 인증을 수행할 수 있습니다. 대상 Google에서 제공하는 일반 OAuth 2.0 기능에 대한 자세한 내용은 OAuth 2.0 및 Java용 Google OAuth 클라이언트 라이브러리.

요약: Google 서비스에 저장된 보호된 데이터에 액세스하려면 다음을 사용하세요. 승인을 위한 OAuth 2.0 Google API는 다양한 유형의 클라이언트 애플리케이션에 대해 OAuth 2.0 흐름을 지원합니다. 이러한 모든 흐름에서 클라이언트 애플리케이션은 클라이언트 애플리케이션 및 보호되는 데이터의 소유자와만 연결되어 있음 있습니다 또한 액세스 토큰은 은 클라이언트 애플리케이션이 액세스할 수 있는 데이터의 종류를 정의합니다 (예: '할 일 관리'). OAuth 2.0의 중요한 목표는 모든 웹 애플리케이션에 대한 안전하고 잠재적 영향을 최소화하면서 보호된 데이터에 편리하게 액세스 안전하게 보호할 수 있습니다

Java용 Google API 클라이언트 라이브러리의 OAuth 2.0 패키지는 범용 Java용 Google OAuth 2.0 클라이언트 라이브러리.

자세한 내용은 다음 패키지에 대한 Javadoc 문서를 참고하세요.

Google API 콘솔

Google API에 액세스하려면 먼저 Google API 콘솔을 통한 인증 및 결제 클라이언트가 설치된 애플리케이션, 모바일 애플리케이션, 웹 서버 또는 브라우저에서 실행되는 클라이언트입니다.

사용자 인증 정보를 올바르게 설정하는 방법에 대한 안내는 다음을 참조하세요. API 콘솔 도움말

사용자 인증 정보

GoogleCredential

GoogleCredential 보호되는 리소스에 액세스하기 위한 OAuth 2.0용 스레드 안전 도우미 클래스입니다. 액세스할 수 있습니다 예를 들어 이미 액세스 토큰이 있는 경우 는 다음과 같은 방식으로 요청할 수 있습니다.

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

Google App Engine ID

이 대체 사용자 인증 정보는 Google App Engine App Identity Java API 클라이언트 애플리케이션이 인스턴스에 대한 액세스를 요청하는 사용자 인증 정보와는 달리 App Identity API가 클라이언트에 대한 액세스 권한을 제공 애플리케이션의 자체 데이터를 저장하는 데 사용됩니다

AppIdentityCredential 사용 (google-api-client-appengine에서) 이 사용자 인증 정보는 Google App Engine이 확인할 수 있습니다. 필요한 OAuth 2.0 범위만 지정하면 됩니다.

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

데이터 스토어

액세스 토큰의 만료일은 일반적으로 1시간이며 사용하려고 하면 오류가 발생합니다. GoogleCredential '새로고침'을 자동으로 처리하여 다시 말해, 간단히 말하자면 새 액세스 토큰이 생성됩니다 이는 수명이 긴 갱신 토큰을 통해 이루어집니다. 일반적으로 access_type=offline 매개변수 GoogleAuthorizationCodeFlow.Builder.setAccessType(String))

대부분의 애플리케이션은 사용자 인증 정보의 액세스 토큰을 유지하고 갱신 토큰을 사용할 수 있습니다. 사용자 인증 정보의 액세스 및/또는 갱신 토큰을 유지하려면 다음을 수행합니다. 자체 DataStoreFactory 구현 제공 StoredCredential 사용 또는 라이브러리에서 제공되는 다음 구현 중 하나를 사용할 수 있습니다.

  • AppEngineDataStoreFactory: Google App Engine Data Store API를 사용하여 사용자 인증 정보를 유지합니다.
  • MemoryDataStoreFactory: '유지' 메모리의 사용자 인증 정보로, 프로세스의 전체 기간 동안 단기 저장소로만 유용합니다.
  • FileDataStoreFactory: 사용자 인증 정보를 파일에 유지합니다

App Engine 사용자: AppEngineCredentialStore 지원 중단되었으며 곧 삭제됩니다. 이때 AppEngineDataStoreFactory StoredCredential을 사용합니다. 이전 방식으로 사용자 인증 정보를 저장한 경우 추가된 도우미 메서드 migrateTo(AppEngineDataStoreFactory) 또는 migrateTo(DataStore) 몇 가지 옵션을 제공합니다

다음을 사용할 수 있습니다. DataStoreCredentialRefreshListener GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener)를 사용하여 사용자 인증 정보에 대해 설정합니다.

승인 코드 흐름

승인 코드 흐름을 사용하여 최종 사용자가 애플리케이션에 권한을 부여할 수 있도록 허용 Google API에서 보호되는 데이터에 액세스할 수 없습니다. 이 흐름의 프로토콜은 지정된 승인 코드 부여.

이 흐름은 GoogleAuthorizationCodeFlow를 사용하여 구현됩니다. 단계는 다음과 같습니다.

  • 최종 사용자가 애플리케이션에 로그인합니다. 해당 사용자를 를 애플리케이션에 대해 고유한 사용자 ID로 바꿉니다.
  • AuthorizationCodeFlow.loadCredential(String)을 호출합니다. 사용자 ID를 기반으로 최종 사용자의 인증 정보를 이미 알고 있는지 확인합니다. 했다면 작업이 완료된 것입니다.
  • 그렇지 않은 경우 AuthorizationCodeFlow.newAuthorizationUrl()을 호출합니다. 최종 사용자의 브라우저가 승인 페이지로 이동하도록 하여 액세스할 수 있습니다.
  • 그러면 Google 인증 서버가 브라우저를 다시 리디렉션합니다. code 쿼리와 함께 애플리케이션에서 지정한 리디렉션 URL 매개변수 값으로 사용됩니다. code 매개변수를 사용하여 다음을 사용하여 액세스 토큰을 요청합니다. AuthorizationCodeFlow.newTokenRequest(String))
  • AuthorizationCodeFlow.createAndStoreCredential(TokenResponse, String) 사용) 사용자 인증 정보를 저장하고 가져올 수 있습니다.

GoogleAuthorizationCodeFlow를 사용하지 않는 경우 하위 수준의 클래스를 사용할 수도 있습니다.

Google API 콘솔에서 프로젝트를 설정할 때 사용 중인 흐름에 따라 다양한 사용자 인증 정보 중에서 선택할 수 있습니다. 자세한 내용은 OAuth 2.0 설정을 참조하세요. 및 OAuth 2.0 시나리오를 참조하세요. 다음은 각 흐름의 코드 스니펫입니다.

웹 서버 애플리케이션

이 흐름의 프로토콜은 웹 서버 애플리케이션용 OAuth 2.0 사용

이 라이브러리는 서블릿 도우미 클래스를 제공하여 인증 코드 플로우를 자세히 설명합니다. 구체적인 서브클래스를 제공하기만 하면 (AbstractAuthorizationCodeServlet) 및 AbstractAuthorizationCodeCallbackServlet (google-oauth-client-servlet에서 가져옴) web.xml 파일에 추가합니다. 여전히 사용자를 직접 처리해야 합니다. 로그인하고 사용자 ID를 추출합니다.

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

Google App Engine 애플리케이션

App Engine의 승인 코드 흐름은 서블릿과 거의 동일합니다. Google App Engine의 Users Java API. 사용자 Users Java API를 사용하려면 로그인해야 합니다. 자세한 내용은 로그인하지 않은 경우 사용자를 로그인 페이지로 리디렉션하는 경우, 자세한 내용은 보안 및 인증 (web.xml에 있음).

서블릿 사례와의 가장 큰 차이점은 서브클래스로 AbstractAppEngineAuthorizationCodeServletAbstractAppEngineAuthorizationCodeCallbackServlet (google-oauth-client-appengine에서) 추상 서블릿 클래스를 확장하고 getUserId 메서드를 구현합니다. 사용자 Java API를 사용해 생성할 수 있습니다 AppEngineDataStoreFactory (google-http-client-appengine에서) 이 방법은 Google App Engine 데이터 애플리케이션을 사용하여 사용자 인증 정보를 Store API를 사용할 수 있습니다.

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

추가 샘플은 다음을 참고하세요. storage-serviceaccount-appengine-sample.

서비스 계정

GoogleCredential 또한 서비스 계정도 지원합니다. 클라이언트 애플리케이션이 인스턴스에 대한 액세스를 요청하는 사용자 인증 정보와는 달리 서비스 계정은 최종 사용자의 데이터에 대한 액세스를 제공하여 데이터를 수집하는 데 사용됩니다 클라이언트 애플리케이션은 Google API 콘솔에서 다운로드한 비공개 키

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();
...

추가 샘플은 다음을 참고하세요. storage-serviceaccount-cmdline-sample.

명의 도용

또한 서비스 계정 흐름을 사용하여 있습니다. 위의 서비스 계정 흐름과 매우 유사하지만 GoogleCredential.Builder.setServiceAccountUser(String)를 추가로 호출합니다.

설치된 애플리케이션

다음은 설치된 애플리케이션에 OAuth 2.0 사용에 설명된 명령줄 승인 코드 플로우입니다.

스니펫 예시: 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");
}

클라이언트 측 애플리케이션

다음 페이지에 설명된 브라우저 기반 클라이언트 플로우 사용하기 클라이언트 측 애플리케이션용 OAuth 2.0 사용, 일반적으로 다음 단계를 따릅니다.

  1. 다음을 사용하여 브라우저의 최종 사용자를 인증 페이지로 리디렉션합니다. GoogleBrowserClientRequestUrl 최종 사용자의 보호된 데이터에 브라우저 애플리케이션이 액세스할 수 있도록 허용합니다.
  2. JavaScript용 Google API 클라이언트 라이브러리 사용 리디렉션 URI의 URL 프래그먼트에서 발견된 액세스 토큰을 처리합니다. Google API 콘솔에 등록되어야 합니다.

웹 애플리케이션의 샘플 사용법:

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

@베타

Android에서 사용할 라이브러리:

Android용으로 개발 중이고 사용하려는 Google API가 를 Google Play 서비스 라이브러리 최적의 성능과 경험을 위해 해당 라이브러리를 사용해야 합니다. Google API에서 사용하고 싶은 앱이 Google Play 서비스 라이브러리에 포함되어 있지 않다면 Android 4.0 (Ice Cream Sandwich)을 지원하는 Java용 Google API 클라이언트 라이브러리 사용 가능 (또는 그 이상)에서 확인할 수 있으며, 여기에 설명되어 있습니다. Google Java용 API 클라이언트 라이브러리는 @베타입니다.

배경 정보

Eclair (SDK 2.1)부터 Android 기기에서 사용자 계정을 관리합니다. 계정을 생성합니다. 모든 Android 애플리케이션 승인이 중앙에서 이루어짐 SDK가 직접 관리하며 AccountManager로 이동합니다. 애플리케이션에 필요한 OAuth 2.0 범위를 지정하면 토큰입니다.

OAuth 2.0 범위는 authTokenType 매개변수를 통해 oauth2:로 지정됩니다. 범위를 추가합니다 예를 들면 다음과 같습니다.

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

Google Tasks API에 대한 읽기/쓰기 액세스 권한을 지정합니다. 여러 개의 OAuth 2.0 범위의 경우 공백으로 구분된 목록을 사용합니다.

일부 API에는 작동하는 특수 authTokenType 매개변수도 있습니다. 예를 들어 "할 일 관리하기" 는 위에 표시된 authtokenType 예의 별칭입니다.

또한 다음 위치에서 API 키를 지정해야 합니다. Google API 콘솔. 그렇지 않은 경우 AccountManager가 제공하는 토큰은 일반적으로 매우 낮은 익명 할당량입니다. 반면에 API를 지정하면 키를 제공하면 더 많은 무료 할당량이 제공되며, 원하는 경우 사용량에 대한 결제를 설정할 수 있습니다. 있습니다.

다음에서 가져온 코드 스니펫 예시 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;
  }
}