概要
目的: このドキュメントでは、Google Cloud で提供される OAuth 2.0 の一般的な機能について説明します。 Java 用 Google OAuth クライアントライブラリを 使用しますこれらの関数を使用して 認証および認可。
GoogleCredential
を使用して OAuth 2.0 認証を行う手順について、
詳しくは、
Java 用 Google API クライアント ライブラリで OAuth 2.0 を使用する。
まとめ: OAuth 2.0 は、 エンド ユーザーが安全にクライアントを承認できるようにするための標準仕様。 保護されたサーバーサイド リソースにアクセスする。また、 OAuth 2.0 署名なしトークン アクセス メカニズムを使用して、これらの保護されたリソースにアクセスする方法を 付与される必要があります。
詳細については、次のパッケージの Javadoc のドキュメントをご覧ください。
- com.google.api.client.auth.oauth2 (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)
クライアントの登録
Java 用 Google OAuth クライアント ライブラリを使用する前に、 認証サーバーにアプリケーションを登録してクライアント ID を受け取り、 クライアント シークレット。(このプロセスに関する一般的な情報については、 クライアント 登録の仕様をご覧ください)。
認証情報と認証情報ストア
認証情報
は、スレッドセーフの OAuth 2.0 ヘルパー クラスです。
できます。更新トークンを使用する場合、Credential
はアクセスも更新します。
更新トークンを使用してアクセス トークンが期限切れになったときに、トークンを再発行します。たとえば
アクセス トークンがある場合は、次の方法でリクエストできます。
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(); }
ほとんどのアプリケーションでは、認証情報のアクセス トークンと 認証ページにリダイレクトされないように、 表示されます。「 CredentialStore このライブラリの実装は非推奨となったため、今後削除される予定です。 できます。別の方法として、 DataStoreFactory および DataStore インターフェースとインターフェース StoredCredential、 Kubernetes によって提供される Java 用の Google HTTP クライアント ライブラリ。
ライブラリで提供されている次のいずれかの実装を使用できます。
- JdoDataStoreFactory JDO を使用して認証情報を保持します。
- AppEngineDataStoreFactory Google App Engine Data Store API を使用して認証情報を保持します。
- MemoryDataStoreFactory 「永続化」メモリ内の認証情報です。これは短期的な使用、データの 存続します。
- FileDataStoreFactory 認証情報がファイルに保持されます。
Google App Engine ユーザー:
AppEngineCredentialStore は非推奨となったため、今後削除されます。
Google Cloud コンソールの AppEngineDataStoreFactory StoredCredential を使用します。 認証情報を以前の方法で保存している場合は、追加されたヘルパー メソッドを使用できます migrateTo(AppEngineDataStoreFactory) または migrateTo(DataStore) 移行する必要があります。
DataStoreCredentialRefreshListener を使用する 使用して認証情報に GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener).
認可コードフロー
認可コードフローを使用して、エンドユーザーがアプリケーションに認証を許可できるようにする アクセスできるようになります。このフローのプロトコルは、 認可コード付与の仕様。
このフローは、 AuthorizationCodeFlow: ステップは次のとおりです。
- エンドユーザーがアプリケーションにログインします。このユーザーを アプリケーションに固有のユーザー ID です。
- 発信 AuthorizationCodeFlow.loadCredential(String), し、ユーザーの認証情報が既知であるかどうかを確認します。 完了です。
- そうでない場合は、AuthorizationCodeFlow.newAuthorizationUrl() を呼び出します。 エンドユーザーのブラウザから認証ページにアクセスし、 保護対象データにアクセスできます。
- ウェブブラウザが「コード」を含むリダイレクト URL にリダイレクトします。検索語句 このパラメータを使用して、アクセス トークンを AuthorizationCodeFlow.newTokenRequest(String) を呼び出します。
- 使用 AuthorizationCodeFlow.createAndStoreCredential(TokenResponse, String) 認証情報を保存および取得して、保護されたリソースにアクセスすることができます。
または、Cloud Storage バケットを AuthorizationCodeFlow、 下位レベルのクラスを使用できます。
- DataStore.get(String) を使用します。 ユーザー ID に基づいて、ストアから認証情報を読み込みます。
- AuthorizationCodeRequestUrl を使用する ブラウザを認証ページに誘導します。
- AuthorizationCodeResponseUrl を使用する 認可レスポンスを処理し、認可コードを解析します。
- AuthorizationCodeTokenRequest を使用する 更新トークンをリクエストすることもできます。
- 新しい認証情報を作成し、DataStore.set(String, V) を使用して保存します。
- 認証情報を使用して、保護されたリソースにアクセスします。 期限切れのアクセス トークンは、次の場合に更新トークンを使用して自動的に更新されます。 適用されます。必ず DataStoreCredentialRefreshListener 使用して認証情報に Credential.Builder.addRefreshListener(CredentialRefreshListener).
サーブレット認可コードフロー
このライブラリが提供するサーブレット ヘルパークラスを使用すると、 認証コードフローについて説明します。具体的なサブクラスを指定するだけで / AbstractAuthorizationCodeServlet および AbstractAuthorizationCodeCallbackServlet(google-oauth-client-servlet から) web.xml ファイルに追加しますユーザー、グループ チャット、 ログインし、ユーザー ID を抽出します。
サンプルコード:
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 の認可コードフロー
App Engine の認可コードフローはサーブレットとほぼ同じ 使用する場合を除いて、Google App Engine の Users Java API。 Users Java API を有効にするには、ユーザーがログインしている必要があります。 ログインページへのユーザーのリダイレクトに関する情報(まだリダイレクトしていない場合) ログインしている場合は、 セキュリティと認証 (web.xml 内)。
サーブレットの場合との主な違いは、
サブクラス
AbstractAppEngineAuthorizationCodeServlet と AbstractAppEngineAuthorizationCodeCallbackServlet(google-oauth-client-appengine から)。抽象サーブレット クラスを拡張し、Users Java API を使用して getUserId
メソッドを実装します。Google App Engine Data Store API を使用して認証情報を保存するには、AppEngineDataStoreFactory(Java 用 Google HTTP クライアント ライブラリのもの)を使用することをおすすめします。
サンプルコード:
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(); } }
コマンドライン認証コードフロー
以下から抜粋した簡略化したサンプルコード 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); ... }
ブラウザベースのクライアント フロー
これらは、 暗黙的権限付与の仕様:
- BrowserClientRequestUrl を使用すると、 エンドユーザーのブラウザから認証ページにリダイレクトされ、そこでエンドユーザーは 保護対象データへのアクセス権をアプリケーションに付与します。
- JavaScript アプリケーションを使用して、URL 内で見つかったアクセス トークンを処理する 認証サーバーに登録されているリダイレクト URI のフラグメント。
ウェブ アプリケーションの使用例:
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); }
期限切れのアクセス トークンの検出
OAuth 2.0 署名なし仕様によると、
アクセス期限が切れた保護されたリソースにアクセスするためにサーバーが呼び出されたとき
サーバーは HTTP 401 Unauthorized
ステータス コードを返します。
次に例を示します。
HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer realm="example", error="invalid_token", error_description="The access token expired"
ただし、この仕様にはかなりの柔軟性があるようです。対象 OAuth 2.0 プロバイダのドキュメントをご覧ください。
もう 1 つの方法は、expires_in
アクセス トークン レスポンス。
これは、許可されたアクセス トークンの存続時間を秒単位で指定します。
通常は 1 時間ですただし、最後にアクセス トークンが期限切れにならない場合もあります。
その間は引き続きアクセスが許可されます。だからこそ
通常は、メインスレッドではなく 401 Unauthorized
ステータス コードを待つことをおすすめします。
経過時間に基づいてトークンの有効期限が切れていると仮定します。別の方法として、
期限切れの直前にアクセス トークンを更新しようとして、トークン サーバーが
が使用できない場合は、401
を受け取るまでアクセス トークンを引き続き使用してください。この
これは、Chronicle ではデフォルトで
認証情報。
もう 1 つの方法は、すべてのリクエストの前に新しいアクセス トークンを取得することですが、 トークン サーバーへの追加の HTTP リクエストを毎回必要とするため、 速度とネットワーク使用量の点で 適した選択肢ではありません理想的には、アクセス トークンを 安全な永続ストレージに格納し、アプリケーションからの新しいアクセス リクエストを最小限に抑えます。 使用します。(ただし、インストール済みのアプリケーションの場合、安全なストレージは難しい問題です)。
アクセス トークンが無効になる理由としては、有効期限、 たとえば、ユーザーがトークンを明示的に取り消した場合などは、 エラー処理コードは堅牢ですトークンがもう存在しないことを検出したら、 期限切れになった場合や取り消された場合などは、そのアクセス権を トークンを取得できます。たとえば Android では、 AccountManager.invalidateAuthToken。